Mkdir rn42

This commit is contained in:
tmk
2014-07-19 02:33:23 +09:00
parent b4e4f599df
commit 59ea5e02f7
11 changed files with 39 additions and 6 deletions

View File

@@ -0,0 +1,102 @@
#include <avr/io.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include "lufa.h"
#include "print.h"
#include "sendchar.h"
#include "rn42.h"
#include "rn42_task.h"
#include "serial.h"
#include "keyboard.h"
#include "keycode.h"
#include "action.h"
#include "action_util.h"
#include "wait.h"
#include "suart.h"
#include "suspend.h"
static int8_t sendchar_func(uint8_t c)
{
sendchar(c); // LUFA
xmit(c); // SUART
return 0;
}
static void SetupHardware(void)
{
/* Disable watchdog if enabled by bootloader/fuses */
MCUSR &= ~(1 << WDRF);
wdt_disable();
/* Disable clock division */
clock_prescale_set(clock_div_1);
// Leonardo needs. Without this USB device is not recognized.
USB_Disable();
USB_Init();
// for Console_Task
USB_Device_EnableSOFEvents();
print_set_sendchar(sendchar_func);
// SUART PD0:output, PD1:input
DDRD |= (1<<0);
PORTD |= (1<<0);
DDRD &= ~(1<<1);
PORTD |= (1<<1);
}
int main(void) __attribute__ ((weak));
int main(void)
{
SetupHardware();
sei();
/* wait for USB startup to get ready for debug output */
uint8_t timeout = 255; // timeout when USB is not available(Bluetooth)
while (timeout-- && USB_DeviceState != DEVICE_STATE_Configured) {
wait_ms(4);
#if defined(INTERRUPT_CONTROL_ENDPOINT)
;
#else
USB_USBTask();
#endif
}
print("USB configured.\n");
rn42_init();
rn42_task_init();
print("RN-42 init\n");
/* init modules */
keyboard_init();
if (!rn42_rts()) {
host_set_driver(&rn42_driver);
} else {
host_set_driver(&lufa_driver);
}
#ifdef SLEEP_LED_ENABLE
sleep_led_init();
#endif
print("Keyboard start.\n");
while (1) {
while (USB_DeviceState == DEVICE_STATE_Suspended) {
suspend_power_down();
if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
USB_Device_SendRemoteWakeup();
}
}
keyboard_task();
#if !defined(INTERRUPT_CONTROL_ENDPOINT)
USB_USBTask();
#endif
rn42_task();
}
}

View File

@@ -0,0 +1,192 @@
#include <avr/io.h>
#include "host.h"
#include "host_driver.h"
#include "serial.h"
#include "rn42.h"
#include "print.h"
#include "wait.h"
/* Host driver */
static uint8_t keyboard_leds(void);
static void send_keyboard(report_keyboard_t *report);
static void send_mouse(report_mouse_t *report);
static void send_system(uint16_t data);
static void send_consumer(uint16_t data);
host_driver_t rn42_driver = {
keyboard_leds,
send_keyboard,
send_mouse,
send_system,
send_consumer
};
void rn42_init(void)
{
// PF7: BT connection control(HiZ: connect, low: disconnect)
// JTAG disable for PORT F. write JTD bit twice within four cycles.
MCUCR |= (1<<JTD);
MCUCR |= (1<<JTD);
rn42_autoconnect();
// PF1: RTS(low: allowed to send, high: not allowed)
DDRF &= ~(1<<1);
PORTF &= ~(1<<1);
// PD5: CTS(low: allow to send, high:not allow)
DDRD |= (1<<5);
PORTD &= ~(1<<5);
serial_init();
}
void rn42_putc(uint8_t c)
{
serial_send(c);
}
bool rn42_autoconnecting(void)
{
// GPIO6 for control connection(high: auto connect, low: disconnect)
// Note that this needs config: SM,4(Auto-Connect DTR Mode)
return (PORTF & (1<<7) ? true : false);
}
void rn42_autoconnect(void)
{
// hi to auto connect
DDRF |= (1<<7);
PORTF |= (1<<7);
}
void rn42_disconnect(void)
{
// low to disconnect
DDRF |= (1<<7);
PORTF &= ~(1<<7);
}
bool rn42_rts(void)
{
// low when RN-42 is powered and ready to receive
return PINF&(1<<1);
}
void rn42_cts_hi(void)
{
// not allow to send
PORTD |= (1<<5);
}
void rn42_cts_lo(void)
{
// allow to send
PORTD &= ~(1<<5);
}
static uint8_t keyboard_leds(void) { return 0; }
static void send_keyboard(report_keyboard_t *report)
{
// wake from deep sleep
/*
PORTD |= (1<<5); // high
wait_ms(5);
PORTD &= ~(1<<5); // low
*/
serial_send(0xFD); // Raw report mode
serial_send(9); // length
serial_send(1); // descriptor type
serial_send(report->mods);
serial_send(0x00);
serial_send(report->keys[0]);
serial_send(report->keys[1]);
serial_send(report->keys[2]);
serial_send(report->keys[3]);
serial_send(report->keys[4]);
serial_send(report->keys[5]);
}
static void send_mouse(report_mouse_t *report)
{
// wake from deep sleep
/*
PORTD |= (1<<5); // high
wait_ms(5);
PORTD &= ~(1<<5); // low
*/
serial_send(0xFD); // Raw report mode
serial_send(5); // length
serial_send(2); // descriptor type
serial_send(report->buttons);
serial_send(report->x);
serial_send(report->y);
serial_send(report->v);
}
static void send_system(uint16_t data)
{
// Table 5-6 of RN-BT-DATA-UB
// 81,82,83 scan codes can be used?
}
static uint16_t usage2bits(uint16_t usage)
{
switch (usage) {
case AC_HOME: return 0x01;
case AL_EMAIL: return 0x02;
case AC_SEARCH: return 0x04;
//case AL_KBD_LAYOUT: return 0x08; // Apple virtual keybaord toggle
case AUDIO_VOL_UP: return 0x10;
case AUDIO_VOL_DOWN: return 0x20;
case AUDIO_MUTE: return 0x40;
case TRANSPORT_PLAY_PAUSE: return 0x80;
case TRANSPORT_NEXT_TRACK: return 0x100;
case TRANSPORT_PREV_TRACK: return 0x200;
case TRANSPORT_STOP: return 0x400;
case TRANSPORT_STOP_EJECT: return 0x800;
//case return 0x1000; // Fast forward
//case return 0x2000; // Rewind
//case return 0x4000; // Stop/eject
//case return 0x8000; // Internet browser
};
return 0;
}
static void send_consumer(uint16_t data)
{
uint16_t bits = usage2bits(data);
serial_send(0xFD); // Raw report mode
serial_send(3); // length
serial_send(3); // descriptor type
serial_send(bits&0xFF);
serial_send((bits>>8)&0xFF);
}
/* Null driver for config_mode */
static uint8_t config_keyboard_leds(void);
static void config_send_keyboard(report_keyboard_t *report);
static void config_send_mouse(report_mouse_t *report);
static void config_send_system(uint16_t data);
static void config_send_consumer(uint16_t data);
host_driver_t rn42_config_driver = {
config_keyboard_leds,
config_send_keyboard,
config_send_mouse,
config_send_system,
config_send_consumer
};
static uint8_t config_keyboard_leds(void) { return 0; }
static void config_send_keyboard(report_keyboard_t *report) {}
static void config_send_mouse(report_mouse_t *report) {}
static void config_send_system(uint16_t data) {}
static void config_send_consumer(uint16_t data) {}

View File

@@ -0,0 +1,18 @@
#ifndef RN42_H
#define RN42_H
#include <stdbool.h>
host_driver_t rn42_driver;
host_driver_t rn42_config_driver;
void rn42_init(void);
void rn42_putc(uint8_t c);
bool rn42_autoconnecting(void);
void rn42_autoconnect(void);
void rn42_disconnect(void);
bool rn42_rts(void);
void rn42_cts_hi(void);
void rn42_cts_lo(void);
#endif

View File

@@ -0,0 +1,255 @@
#include <stdint.h>
#include "keycode.h"
#include "serial.h"
#include "host.h"
#include "action.h"
#include "action_util.h"
#include "lufa.h"
#include "rn42_task.h"
#include "print.h"
#include "timer.h"
#include "command.h"
static bool config_mode = false;
static bool force_usb = false;
static void battery_adc_init(void)
{
ADMUX = (1<<REFS1) | (1<<REFS0); // Ref:2.56V band-gap, Input:ADC0(PF0)
ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // Prescale:128 16MHz/128=125KHz
ADCSRA |= (1<<ADEN); // enable ADC
}
static uint16_t battery_adc(void)
{
volatile uint16_t bat;
ADCSRA |= (1<<ADEN);
// discard first result
ADCSRA |= (1<<ADSC);
while (ADCSRA & (1<<ADSC)) ;
bat = ADC;
// discard second result
ADCSRA |= (1<<ADSC);
while (ADCSRA & (1<<ADSC)) ;
bat = ADC;
ADCSRA |= (1<<ADSC);
while (ADCSRA & (1<<ADSC)) ;
bat = ADC;
ADCSRA &= ~(1<<ADEN);
return bat;
}
void rn42_task_init(void)
{
battery_adc_init();
}
void rn42_task(void)
{
int16_t c;
if (config_mode) {
// Config mode: print output from RN-42
while ((c = serial_recv2()) != -1) {
// without flow control it'll fail to receive data when flooded
rn42_cts_hi();
xprintf("%c", c);
rn42_cts_lo();
}
} else {
// Raw mode: interpret output report of LED state
while ((c = serial_recv2()) != -1) {
// LED Out report: 0xFE, 0x02, 0x01, <leds>
// To get the report over UART set bit3 with SH, command.
static enum {LED_INIT, LED_FE, LED_02, LED_01} state = LED_INIT;
xprintf("%02X\n", c);
switch (state) {
case LED_INIT:
if (c == 0xFE) state = LED_FE;
else state = LED_INIT;
break;
case LED_FE:
if (c == 0x02) state = LED_02;
else state = LED_INIT;
break;
case LED_02:
if (c == 0x01) state = LED_01;
else state = LED_INIT;
break;
case LED_01:
// TODO: move to rn42.c and make accessible with keyboard_leds()
xprintf("LED status: %02X\n", c);
state = LED_INIT;
break;
default:
state = LED_INIT;
}
}
}
/* Bluetooth mode when ready */
if (!config_mode && !force_usb) {
if (!rn42_rts() && host_get_driver() != &rn42_driver) {
clear_keyboard();
host_set_driver(&rn42_driver);
} else if (rn42_rts() && host_get_driver() != &lufa_driver) {
clear_keyboard();
host_set_driver(&lufa_driver);
}
}
}
/******************************************************************************
* Command
******************************************************************************/
bool command_extra(uint8_t code)
{
static host_driver_t *prev_driver = &rn42_driver;
switch (code) {
case KC_H:
case KC_SLASH: /* ? */
print("\n\n----- Bluetooth RN-42 Help -----\n");
print("Del: enter/exit config mode(auto_connect/disconnect)\n");
print("i: RN-42 info\n");
print("b: battery voltage\n");
if (config_mode) {
return true;
} else {
print("u: Force USB mode\n");
return false; // to display default command help
}
case KC_DELETE:
if (rn42_autoconnecting()) {
prev_driver = host_get_driver();
clear_keyboard();
_delay_ms(500);
host_set_driver(&rn42_config_driver); // null driver; not to send a key to host
rn42_disconnect();
print("\nRN-42: disconnect\n");
print("Enter config mode\n");
print("type $$$ to start and + for local echo\n");
command_state = CONSOLE;
config_mode = true;
} else {
rn42_autoconnect();
print("\nRN-42: auto_connect\n");
print("Exit config mode\n");
command_state = ONESHOT;
config_mode = false;
//clear_keyboard();
host_set_driver(prev_driver);
}
return true;
case KC_U:
if (config_mode) return false;
if (force_usb) {
print("Auto mode\n");
force_usb = false;
} else {
print("USB mode\n");
force_usb = true;
clear_keyboard();
host_set_driver(&lufa_driver);
}
return true;
case KC_I:
print("\n----- RN-42 info -----\n");
xprintf("protocol: %s\n", (host_get_driver() == &rn42_driver) ? "RN-42" : "LUFA");
xprintf("force_usb: %X\n", force_usb);
xprintf("rn42_autoconnecting(): %X\n", rn42_autoconnecting());
xprintf("rn42_rts(): %X\n", rn42_rts());
xprintf("config_mode: %X\n", config_mode);
return true;
case KC_B:
// battery monitor
xprintf("BAT: %04X(%08lX)\n", battery_adc(), timer_read32());
return true;
default:
if (config_mode)
return true;
else
return false; // exec default command
}
return true;
}
static uint8_t code2asc(uint8_t code);
bool command_console_extra(uint8_t code)
{
switch (code) {
default:
rn42_putc(code2asc(code));
return true;
}
return false;
}
// convert keycode into ascii charactor
static uint8_t code2asc(uint8_t code)
{
bool shifted = (get_mods() & (MOD_BIT(KC_LSHIFT)|MOD_BIT(KC_RSHIFT))) ? true : false;
switch (code) {
case KC_A: return (shifted ? 'A' : 'a');
case KC_B: return (shifted ? 'B' : 'b');
case KC_C: return (shifted ? 'C' : 'c');
case KC_D: return (shifted ? 'D' : 'd');
case KC_E: return (shifted ? 'E' : 'e');
case KC_F: return (shifted ? 'F' : 'f');
case KC_G: return (shifted ? 'G' : 'g');
case KC_H: return (shifted ? 'H' : 'h');
case KC_I: return (shifted ? 'I' : 'i');
case KC_J: return (shifted ? 'J' : 'j');
case KC_K: return (shifted ? 'K' : 'k');
case KC_L: return (shifted ? 'L' : 'l');
case KC_M: return (shifted ? 'M' : 'm');
case KC_N: return (shifted ? 'N' : 'n');
case KC_O: return (shifted ? 'O' : 'o');
case KC_P: return (shifted ? 'P' : 'p');
case KC_Q: return (shifted ? 'Q' : 'q');
case KC_R: return (shifted ? 'R' : 'r');
case KC_S: return (shifted ? 'S' : 's');
case KC_T: return (shifted ? 'T' : 't');
case KC_U: return (shifted ? 'U' : 'u');
case KC_V: return (shifted ? 'V' : 'v');
case KC_W: return (shifted ? 'W' : 'w');
case KC_X: return (shifted ? 'X' : 'x');
case KC_Y: return (shifted ? 'Y' : 'y');
case KC_Z: return (shifted ? 'Z' : 'z');
case KC_1: return (shifted ? '!' : '1');
case KC_2: return (shifted ? '@' : '2');
case KC_3: return (shifted ? '#' : '3');
case KC_4: return (shifted ? '$' : '4');
case KC_5: return (shifted ? '%' : '5');
case KC_6: return (shifted ? '^' : '6');
case KC_7: return (shifted ? '&' : '7');
case KC_8: return (shifted ? '*' : '8');
case KC_9: return (shifted ? '(' : '9');
case KC_0: return (shifted ? ')' : '0');
case KC_ENTER: return '\n';
case KC_ESCAPE: return 0x1B;
case KC_BSPACE: return '\b';
case KC_TAB: return '\t';
case KC_SPACE: return ' ';
case KC_MINUS: return (shifted ? '_' : '-');
case KC_EQUAL: return (shifted ? '+' : '=');
case KC_LBRACKET: return (shifted ? '{' : '[');
case KC_RBRACKET: return (shifted ? '}' : ']');
case KC_BSLASH: return (shifted ? '|' : '\\');
case KC_NONUS_HASH: return (shifted ? '|' : '\\');
case KC_SCOLON: return (shifted ? ':' : ';');
case KC_QUOTE: return (shifted ? '"' : '\'');
case KC_GRAVE: return (shifted ? '~' : '`');
case KC_COMMA: return (shifted ? '<' : ',');
case KC_DOT: return (shifted ? '>' : '.');
case KC_SLASH: return (shifted ? '?' : '/');
case KC_DELETE: return '\0'; // Delete to disconnect
default: return ' ';
}
}

View File

@@ -0,0 +1,10 @@
#ifndef RN42_TASK_H
#define RN42_TASK_H
#include <stdbool.h>
#include "rn42.h"
void rn42_task_init(void);
void rn42_task(void);
#endif

View File

@@ -0,0 +1,156 @@
;---------------------------------------------------------------------------;
; Software implemented UART module ;
; (C)ChaN, 2005 (http://elm-chan.org/) ;
;---------------------------------------------------------------------------;
; Bit rate settings:
;
; 1MHz 2MHz 4MHz 6MHz 8MHz 10MHz 12MHz 16MHz 20MHz
; 2.4kbps 138 - - - - - - - -
; 4.8kbps 68 138 - - - - - - -
; 9.6kbps 33 68 138 208 - - - - -
; 19.2kbps - 33 68 102 138 173 208 - -
; 38.4kbps - - 33 50 68 85 102 138 172
; 57.6kbps - - 21 33 44 56 68 91 114
; 115.2kbps - - - - 21 27 33 44 56
.nolist
#include <avr/io.h>
.list
#define BPS 44 /* Bit delay. (see above table) */
#define BIDIR 0 /* 0:Separated Tx/Rx, 1:Shared Tx/Rx */
#define OUT_1 sbi _SFR_IO_ADDR(SUART_OUT_PORT), SUART_OUT_BIT /* Output 1 */
#define OUT_0 cbi _SFR_IO_ADDR(SUART_OUT_PORT), SUART_OUT_BIT /* Output 0 */
#define SKIP_IN_1 sbis _SFR_IO_ADDR(SUART_IN_PIN), SUART_IN_BIT /* Skip if 1 */
#define SKIP_IN_0 sbic _SFR_IO_ADDR(SUART_IN_PIN), SUART_IN_BIT /* Skip if 0 */
#ifdef SPM_PAGESIZE
.macro _LPMI reg
lpm \reg, Z+
.endm
.macro _MOVW dh,dl, sh,sl
movw \dl, \sl
.endm
#else
.macro _LPMI reg
lpm
mov \reg, r0
adiw ZL, 1
.endm
.macro _MOVW dh,dl, sh,sl
mov \dl, \sl
mov \dh, \sh
.endm
#endif
;---------------------------------------------------------------------------;
; Transmit a byte in serial format of N81
;
;Prototype: void xmit (uint8_t data);
;Size: 16 words
.global xmit
.func xmit
xmit:
#if BIDIR
ldi r23, BPS-1 ;Pre-idle time for bidirectional data line
5: dec r23 ;
brne 5b ;/
#endif
in r0, _SFR_IO_ADDR(SREG) ;Save flags
com r24 ;C = start bit
ldi r25, 10 ;Bit counter
cli ;Start critical section
1: ldi r23, BPS-1 ;----- Bit transferring loop
2: dec r23 ;Wait for a bit time
brne 2b ;/
brcs 3f ;MISO = bit to be sent
OUT_1 ;
3: brcc 4f ;
OUT_0 ;/
4: lsr r24 ;Get next bit into C
dec r25 ;All bits sent?
brne 1b ; no, coutinue
out _SFR_IO_ADDR(SREG), r0 ;End of critical section
ret
.endfunc
;---------------------------------------------------------------------------;
; Receive a byte
;
;Prototype: uint8_t rcvr (void);
;Size: 19 words
.global rcvr
.func rcvr
rcvr:
in r0, _SFR_IO_ADDR(SREG) ;Save flags
ldi r24, 0x80 ;Receiving shift reg
cli ;Start critical section
1: SKIP_IN_1 ;Wait for idle
rjmp 1b
2: SKIP_IN_0 ;Wait for start bit
rjmp 2b
ldi r25, BPS/2 ;Wait for half bit time
3: dec r25
brne 3b
4: ldi r25, BPS ;----- Bit receiving loop
5: dec r25 ;Wait for a bit time
brne 5b ;/
lsr r24 ;Next bit
SKIP_IN_0 ;Get a data bit into r24.7
ori r24, 0x80
brcc 4b ;All bits received? no, continue
out _SFR_IO_ADDR(SREG), r0 ;End of critical section
ret
.endfunc
; Not wait for start bit. This should be called after detecting start bit.
.global recv
.func recv
recv:
in r0, _SFR_IO_ADDR(SREG) ;Save flags
ldi r24, 0x80 ;Receiving shift reg
cli ;Start critical section
;1: SKIP_IN_1 ;Wait for idle
; rjmp 1b
;2: SKIP_IN_0 ;Wait for start bit
; rjmp 2b
ldi r25, BPS/2 ;Wait for half bit time
3: dec r25
brne 3b
4: ldi r25, BPS ;----- Bit receiving loop
5: dec r25 ;Wait for a bit time
brne 5b ;/
lsr r24 ;Next bit
SKIP_IN_0 ;Get a data bit into r24.7
ori r24, 0x80
brcc 4b ;All bits received? no, continue
ldi r25, BPS/2 ;Wait for half bit time
6: dec r25
brne 6b
7: SKIP_IN_1 ;Wait for stop bit
rjmp 7b
out _SFR_IO_ADDR(SREG), r0 ;End of critical section
ret
.endfunc

View File

@@ -0,0 +1,8 @@
#ifndef SUART
#define SUART
void xmit(uint8_t);
uint8_t rcvr(void);
uint8_t recv(void);
#endif /* SUART */