Add RN-42 Bluetooth module support
This commit is contained in:
parent
be19b2bf32
commit
31a298f9d9
|
@ -0,0 +1,294 @@
|
||||||
|
Roving RN-42
|
||||||
|
============
|
||||||
|
|
||||||
|
|
||||||
|
TODO
|
||||||
|
----
|
||||||
|
- USB connection check
|
||||||
|
- BT on/off check: whether RX line is pulled up?
|
||||||
|
- CTS/RTS lines are needed? just connect in loop back if no flow control is needed.
|
||||||
|
- CTS is needed for waking up from deep sleep?
|
||||||
|
- USB/BT switching BT is on -> BT, BT is off -> USB
|
||||||
|
|
||||||
|
- Low voltage lock out LVLO of DC converter for protection of Lipo
|
||||||
|
- Lipo charger configuration: fast charge time
|
||||||
|
- Low voltage alarm: LED indcates voltage of Lipo
|
||||||
|
|
||||||
|
- Power saving: HHKB scan, BT radio control
|
||||||
|
|
||||||
|
- Serial API callback when data are available/received(and when send buffer is available)
|
||||||
|
- Serial API instance several serial ports can be used
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
Ver 6.15 04/26/2013
|
||||||
|
(c) Roving Networks
|
||||||
|
***Settings***
|
||||||
|
BTA=0006664B3AE3
|
||||||
|
BTName=tmkBT-3AE3
|
||||||
|
Baudrt(SW4)=115K
|
||||||
|
Mode =Pair
|
||||||
|
Authen=2
|
||||||
|
Bonded=1
|
||||||
|
Rem=001BDC06415B
|
||||||
|
***ADVANCED Settings***
|
||||||
|
SrvName= keyboard/mouse
|
||||||
|
SrvClass=0000
|
||||||
|
DevClass=05C0
|
||||||
|
InqWindw=0100
|
||||||
|
PagWindw=0100
|
||||||
|
CfgTimer=255
|
||||||
|
StatuStr=NULL
|
||||||
|
HidFlags=3f
|
||||||
|
DTRtimer=8
|
||||||
|
KeySwapr=0
|
||||||
|
***OTHER Settings***
|
||||||
|
Profile= HID
|
||||||
|
CfgChar= $
|
||||||
|
SniffEna=0
|
||||||
|
LowPower=0
|
||||||
|
TX Power=4
|
||||||
|
IOPorts= 0
|
||||||
|
IOValues=0
|
||||||
|
Sleeptmr=0
|
||||||
|
DebugMod=0
|
||||||
|
RoleSwch=0
|
||||||
|
|
||||||
|
Ver 6.15 04/26/2013
|
||||||
|
(c) Roving Networks
|
||||||
|
***Settings***
|
||||||
|
BTA=0006664B3AE3
|
||||||
|
BTName=tmkBT-3AE3
|
||||||
|
Baudrt(SW4)=115K
|
||||||
|
Mode =DTR
|
||||||
|
Authen=2
|
||||||
|
Bonded=0
|
||||||
|
Rem=BCF5AC9BCB7E
|
||||||
|
***ADVANCED Settings***
|
||||||
|
SrvName= keyboard/mouse
|
||||||
|
SrvClass=0000
|
||||||
|
DevClass=0000
|
||||||
|
InqWindw=0100
|
||||||
|
PagWindw=0100
|
||||||
|
CfgTimer=255
|
||||||
|
StatuStr=NULL
|
||||||
|
HidFlags=3f
|
||||||
|
DTRtimer=8
|
||||||
|
KeySwapr=0
|
||||||
|
***OTHER Settings***
|
||||||
|
Profile= HID
|
||||||
|
CfgChar= $
|
||||||
|
SniffEna=0
|
||||||
|
LowPower=0
|
||||||
|
TX Power=ffe8
|
||||||
|
IOPorts= 0
|
||||||
|
IOValues=0
|
||||||
|
Sleeptmr=0
|
||||||
|
DebugMod=0
|
||||||
|
RoleSwch=0
|
||||||
|
|
||||||
|
|
||||||
|
command mode
|
||||||
|
------------
|
||||||
|
To enter command mode disconnect the module from host and type '$$$'.(you will see 'CMD')
|
||||||
|
To exit type '---'.(you will see 'END')
|
||||||
|
|
||||||
|
Serial line: 115200bps, 8bit, 1-stopbit, non-parity, no flow control
|
||||||
|
SSP: 115200bps, 8bit, 1-stopbit, non-parity, no flow control(via Bluetooth)
|
||||||
|
|
||||||
|
|
||||||
|
S-,tmkBT // Device name
|
||||||
|
SH,0038 // HID register
|
||||||
|
SC,0000 // COD: 000005C0 (see HID spec/Bluegiga doc)
|
||||||
|
SD,05C0 // bit 12-8 7 6 5-0
|
||||||
|
// 00101 1 1 0
|
||||||
|
// peripheral pointing keybaord joystick, gamepad, ...
|
||||||
|
S~,6 // HID profile
|
||||||
|
SS,keyboard/mouse // service name
|
||||||
|
SM,6 // Pairing mode: auto connect
|
||||||
|
SM,4 // Master mode: Connection can be controled with GPIO6
|
||||||
|
|
||||||
|
SY,FEE8 // lower power -20dbM
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HID profile
|
||||||
|
-----------
|
||||||
|
S~,6 HID profile
|
||||||
|
S~,0 SPP profile
|
||||||
|
R,1 reboot
|
||||||
|
|
||||||
|
|
||||||
|
Apple iOS
|
||||||
|
---------
|
||||||
|
Cannot supported without apple iAP authentication chip.
|
||||||
|
|
||||||
|
|
||||||
|
HID flag register
|
||||||
|
-----------------
|
||||||
|
SH,0200
|
||||||
|
GH
|
||||||
|
|
||||||
|
10 0000 0000(0200) default
|
||||||
|
00 0011 1000(0038) Combo
|
||||||
|
|| | | |\_\____ number of paired devices to which the module can reconnect
|
||||||
|
|| | | \_______ send out reports over UART (0xFF <len> <data>)
|
||||||
|
|| \__\_________ descriptor type
|
||||||
|
|\______________ toggle virtual keyboard on iOS when first connected
|
||||||
|
\_______________ Force HID mode if GPIO11 is high on power-up
|
||||||
|
|
||||||
|
Descriptor type:
|
||||||
|
0000: keybaord
|
||||||
|
0001: Game Pad
|
||||||
|
0010: Mouse
|
||||||
|
0011: Combo
|
||||||
|
0100: Joystick
|
||||||
|
1xxx: reserved
|
||||||
|
|
||||||
|
Out report - Indicator
|
||||||
|
----------------------
|
||||||
|
0xFE 0x02 0x01 <LED_state>
|
||||||
|
|
||||||
|
|
||||||
|
LED Status
|
||||||
|
----------
|
||||||
|
Configuring 10 times per sec
|
||||||
|
Startup/configuration timer 2 times per sec
|
||||||
|
Discoverable/Inquiring/Idle once per sec
|
||||||
|
Connected solid on
|
||||||
|
|
||||||
|
|
||||||
|
Pairing
|
||||||
|
-------
|
||||||
|
First, host initiates pairing process and once it is done, auto connect will work thereafter.
|
||||||
|
SM,3 Master mode
|
||||||
|
SM,4 Auto Connect DTR Mode uses GPIO6 to make and break connection(Mode =DTR)
|
||||||
|
confirm: auto connect works and control connection with GPIO6
|
||||||
|
SM,5 Auto Connect ANY Mode (Mode =ANY)
|
||||||
|
each time GPIO is set, make inquiry and connect to the first found device
|
||||||
|
SM,6 automatically reconnect(Mode =Pair)
|
||||||
|
confirm: auto connect works well but difficult to enter command mode.
|
||||||
|
|
||||||
|
|
||||||
|
Fast data mode
|
||||||
|
--------------
|
||||||
|
The module enters fast data mode after 'remote configuration timer' window is passed from power on.
|
||||||
|
In this mode the module does not accept '$$$' to enter command mode.
|
||||||
|
|
||||||
|
Power Management
|
||||||
|
----------------
|
||||||
|
Inquiry and Page window Idle or Active (3.1.1)
|
||||||
|
Downside: delay in discovery or connection time
|
||||||
|
SI, // set inquiry scan window(discovery) on/off duty?
|
||||||
|
SJ, // set page scan window(connection)
|
||||||
|
This reduces averaege power >20mA to 5mA(3mA in Sniff mode)
|
||||||
|
|
||||||
|
Sniff mode Transmit
|
||||||
|
Sniff mode is disabled by default and radio is active continuously when connected.(25-30mA)
|
||||||
|
In Sniff mode the radio wakes up intermittently and sleeps in very low power mode.(2mA)
|
||||||
|
SW,<val> // set interval timer(*0.625ms) 0000-7FFF
|
||||||
|
|
||||||
|
Deep sleep Idle (3.1.2)
|
||||||
|
In this mode the module shuts down completly and only draws about 300uA. To enable this set the most signifant bit(0x8000) of Sniff interaval timer.
|
||||||
|
SW,8320 // deep sleep enable(interval=0x320*0.625ms)
|
||||||
|
In normal sleep the firmware is still running in idle mode, and wakes up about 20 times per second to check ports, update LEDs, etc. During deep sleep, the firmware actually stops runnig some tasks and the LEDs only update about once per second.
|
||||||
|
To wake from deep sleep there are three ways: (in worst case wake up takes 5ms)
|
||||||
|
*send a charactor to the UART(first charactor will be lost)
|
||||||
|
*toggle CTS low to high and wait 5ms
|
||||||
|
*wake automatically every slot time(<val>*0.625ms)
|
||||||
|
Once the radio is awake it stay active for exactly 1 second of inactivity and then sleeps again.
|
||||||
|
Downside: latency and data loss
|
||||||
|
|
||||||
|
Disable Output driver Idle or Active (3.1.3)
|
||||||
|
S%,1000 // set all GPIO pins(0-11) to inputs.
|
||||||
|
|
||||||
|
Lower Transmit Power Idle or Active (3.1.4)
|
||||||
|
SY,<hex> // transmit power setting(takes effect after a power cycle and reboot)
|
||||||
|
Downside: reducing effective range
|
||||||
|
|
||||||
|
|
||||||
|
Optimizig for Latency
|
||||||
|
---------------------
|
||||||
|
By default the firmware is optimized for throughput.
|
||||||
|
SQ,16 // set latency bit
|
||||||
|
SQ,0 // unset latency bit
|
||||||
|
|
||||||
|
|
||||||
|
Configuration timer settings
|
||||||
|
----------------------------
|
||||||
|
Remote configuration is used for the module to be configured with various commands over Bluetooth(SPP profile only?).
|
||||||
|
|
||||||
|
The module has remote configuration timer to allow remote configuration over Bluetooth after power up in Slave mode. In Master modes the remote configuration timer is set to 0(no remote configuration). (In Trigger Master mode the timer is used as an idle timer to break the connection after time expires with no charactors receive.)
|
||||||
|
ST,0 // no remote, no local when connected
|
||||||
|
ST,<1-252> // local and remote with timeout in seconds from power up
|
||||||
|
ST,253 // local only without timeout
|
||||||
|
ST,254 // remote only without timeout
|
||||||
|
ST,255 // local and remote without timeout
|
||||||
|
|
||||||
|
|
||||||
|
Android
|
||||||
|
-------
|
||||||
|
3.7.1.5 Note: To connect with Android phone the modules must wake up 11ms every 2.5seconds.
|
||||||
|
|
||||||
|
|
||||||
|
Commands
|
||||||
|
--------
|
||||||
|
SC,
|
||||||
|
SM,<val>
|
||||||
|
SD,
|
||||||
|
SP,<string> Pin code(alpahnumeric)
|
||||||
|
SQ,<mask> Special configuration(GPIO, discovery mode, low latency, reboot, UART)
|
||||||
|
SR,<hex> Store remote address
|
||||||
|
SR,Z Erase all address
|
||||||
|
SS,<string> Set service name(1-20)**
|
||||||
|
ST,<val> Remote configuration timer(Master:0, Slave:0-255, Triger:as idle timer)
|
||||||
|
SU,<val> UART baud rate
|
||||||
|
SW,<val> low-power sniff mode** deep sleep and wake up every 625us * <val>
|
||||||
|
SX,<0|1> bonding enable only acceps device that matches the stored address
|
||||||
|
SY,<hex> power setting**
|
||||||
|
SZ,<val> non-standard raw baud rate <val>=baud*0.004096
|
||||||
|
S~,<val> Profile 0:SPP, 5:APL, 6:HID
|
||||||
|
S-,<string> Device name -15 alphanumeric charactors
|
||||||
|
S?,<0|1> role switch enable
|
||||||
|
S$,<char> command mode char
|
||||||
|
$|,<hex> low-power connect mode deep sleep/active(discoverable and connectable) cycle
|
||||||
|
D display basic setting
|
||||||
|
E display extended setting
|
||||||
|
GB display the device's Bluetooth address
|
||||||
|
GF display Bluetooth address of connected device
|
||||||
|
GK show connection status
|
||||||
|
GR show remote address for reconnecting
|
||||||
|
G& show GPIO pin
|
||||||
|
G<char> show stored setting
|
||||||
|
+ toggle local echo on/off
|
||||||
|
& show GPIO 3,4,6,7(DIP switch)
|
||||||
|
C connect to stored remote address
|
||||||
|
C,<address> connect last address
|
||||||
|
CFI connect and go into fast data mode
|
||||||
|
CFR connect and go into fast data mode
|
||||||
|
CT,<address>,<val> connect to the address and disconnect after val?
|
||||||
|
F,1 fast data mod:
|
||||||
|
H display help
|
||||||
|
I,<time>,<cod> inquiry scan with <cod>
|
||||||
|
IN
|
||||||
|
IQ scan
|
||||||
|
IS inquiry scan with 001F00
|
||||||
|
J hide pin code
|
||||||
|
K, kill disconnects current connection
|
||||||
|
L link quality
|
||||||
|
M show modem signlal status
|
||||||
|
O display other settings
|
||||||
|
P,<car> pass through?
|
||||||
|
Q quiet mode make the module not discoverable
|
||||||
|
Q,0 discoverable and connectable
|
||||||
|
Q,1 not discoverable and not connectable
|
||||||
|
Q,2 not discoverable and connectable
|
||||||
|
Q,? display current quiet mode
|
||||||
|
R,1 reboot
|
||||||
|
T,<0|1> pass received data while in command mode
|
||||||
|
U,<baud>,<parity> change UART setting tentatively
|
||||||
|
V display firmware version
|
||||||
|
W wake from quiet mode enable discovery and connection
|
||||||
|
Z deep sleep mode(<2mA)
|
|
@ -52,12 +52,14 @@ TARGET_DIR = .
|
||||||
SRC += keymap_common.c \
|
SRC += keymap_common.c \
|
||||||
matrix.c \
|
matrix.c \
|
||||||
led.c \
|
led.c \
|
||||||
|
serial_uart.c \
|
||||||
|
rn42.c \
|
||||||
main.c
|
main.c
|
||||||
|
|
||||||
ifdef KEYMAP
|
ifdef KEYMAP
|
||||||
SRC := keymap_$(KEYMAP).c $(SRC)
|
SRC := keymap_$(KEYMAP).c $(SRC)
|
||||||
else
|
else
|
||||||
SRC := keymap_hhkb.c $(SRC)
|
SRC := keymap_hasu.c $(SRC)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CONFIG_H = config.h
|
CONFIG_H = config.h
|
||||||
|
@ -123,14 +125,15 @@ MOUSEKEY_ENABLE = yes # Mouse keys
|
||||||
EXTRAKEY_ENABLE = yes # Audio control and System control
|
EXTRAKEY_ENABLE = yes # Audio control and System control
|
||||||
CONSOLE_ENABLE = yes # Console for debug
|
CONSOLE_ENABLE = yes # Console for debug
|
||||||
COMMAND_ENABLE = yes # Commands for debug and configuration
|
COMMAND_ENABLE = yes # Commands for debug and configuration
|
||||||
NKRO_ENABLE = yes # USB Nkey Rollover
|
#NKRO_ENABLE = yes # USB Nkey Rollover
|
||||||
KEYMAP_SECTION_ENABLE = yes # fixed address keymap for keymap editor
|
#KEYMAP_SECTION_ENABLE = yes # fixed address keymap for keymap editor
|
||||||
|
|
||||||
|
|
||||||
# Search Path
|
# Search Path
|
||||||
VPATH += $(TARGET_DIR)
|
VPATH += $(TARGET_DIR)
|
||||||
VPATH += $(TOP_DIR)
|
VPATH += $(TOP_DIR)
|
||||||
|
|
||||||
|
include $(TOP_DIR)/protocol.mk
|
||||||
include $(TOP_DIR)/protocol/lufa.mk
|
include $(TOP_DIR)/protocol/lufa.mk
|
||||||
include $(TOP_DIR)/common.mk
|
include $(TOP_DIR)/common.mk
|
||||||
include $(TOP_DIR)/rules.mk
|
include $(TOP_DIR)/rules.mk
|
||||||
|
|
|
@ -47,6 +47,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#define BOOTMAGIC_KEY_SALT KC_FN6
|
#define BOOTMAGIC_KEY_SALT KC_FN6
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hardware Serial(UART)
|
||||||
|
* Baud rate are calculated with round off(+0.5).
|
||||||
|
*/
|
||||||
|
#ifdef __AVR_ATmega32U4__
|
||||||
|
/* iom32u4.h has no definition of UCSR1D. copy from iom32u2.h */
|
||||||
|
#define UCSR1D _SFR_MEM8(0xCB)
|
||||||
|
#define RTSEN 0
|
||||||
|
#define CTSEN 1
|
||||||
|
|
||||||
|
#define SERIAL_UART_BAUD 115200
|
||||||
|
#define SERIAL_UART_DATA UDR1
|
||||||
|
#define SERIAL_UART_UBRR ((F_CPU/(16.0*SERIAL_UART_BAUD)-1+0.5))
|
||||||
|
#define SERIAL_UART_RXD_VECT USART1_RX_vect
|
||||||
|
#define SERIAL_UART_TXD_READY (UCSR1A&(1<<UDRE1))
|
||||||
|
#define SERIAL_UART_INIT() do { \
|
||||||
|
UBRR1L = (uint8_t) SERIAL_UART_UBRR; /* baud rate */ \
|
||||||
|
UBRR1H = ((uint16_t)SERIAL_UART_UBRR>>8); /* baud rate */ \
|
||||||
|
UCSR1B |= (1<<RXCIE1) | (1<<RXEN1); /* RX interrupt, RX: enable */ \
|
||||||
|
UCSR1B |= (0<<TXCIE1) | (1<<TXEN1); /* TX interrupt, TX: enable */ \
|
||||||
|
UCSR1C |= (0<<UPM11) | (0<<UPM10); /* parity: none(00), even(01), odd(11) */ \
|
||||||
|
UCSR1D |= (0<<RTSEN) | (0<<CTSEN); /* RTS, CTS */ \
|
||||||
|
sei(); \
|
||||||
|
} while(0)
|
||||||
|
#else
|
||||||
|
#error "USART configuration is needed."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Feature disable options
|
* Feature disable options
|
||||||
* These options are also useful to firmware size reduction.
|
* These options are also useful to firmware size reduction.
|
||||||
|
|
|
@ -4,8 +4,18 @@
|
||||||
#include "lufa.h"
|
#include "lufa.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
#include "sendchar.h"
|
#include "sendchar.h"
|
||||||
|
#include "rn42.h"
|
||||||
|
#include "serial.h"
|
||||||
|
#include "keyboard.h"
|
||||||
|
#include "command.h"
|
||||||
|
#include "keycode.h"
|
||||||
|
#include "action.h"
|
||||||
|
#include "action_util.h"
|
||||||
|
#include "wait.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool config_mode = false;
|
||||||
|
|
||||||
static void SetupHardware(void)
|
static void SetupHardware(void)
|
||||||
{
|
{
|
||||||
/* Disable watchdog if enabled by bootloader/fuses */
|
/* Disable watchdog if enabled by bootloader/fuses */
|
||||||
|
@ -25,14 +35,17 @@ static void SetupHardware(void)
|
||||||
print_set_sendchar(sendchar);
|
print_set_sendchar(sendchar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool force_usb = false;
|
||||||
int main(void) __attribute__ ((weak));
|
int main(void) __attribute__ ((weak));
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
SetupHardware();
|
SetupHardware();
|
||||||
sei();
|
sei();
|
||||||
|
|
||||||
/* wait for USB startup & debug output */
|
/* wait for USB startup to get ready for debug output */
|
||||||
while (USB_DeviceState != DEVICE_STATE_Configured) {
|
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)
|
#if defined(INTERRUPT_CONTROL_ENDPOINT)
|
||||||
;
|
;
|
||||||
#else
|
#else
|
||||||
|
@ -41,27 +54,240 @@ int main(void)
|
||||||
}
|
}
|
||||||
print("USB configured.\n");
|
print("USB configured.\n");
|
||||||
|
|
||||||
|
rn42_init();
|
||||||
|
print("RN-42 init\n");
|
||||||
|
|
||||||
/* init modules */
|
/* init modules */
|
||||||
keyboard_init();
|
keyboard_init();
|
||||||
host_set_driver(&lufa_driver);
|
|
||||||
|
if (rn42_ready()) {
|
||||||
|
host_set_driver(&rn42_driver);
|
||||||
|
} else {
|
||||||
|
host_set_driver(&lufa_driver);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SLEEP_LED_ENABLE
|
#ifdef SLEEP_LED_ENABLE
|
||||||
sleep_led_init();
|
sleep_led_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
print("Keyboard start.\n");
|
print("Keyboard start.\n");
|
||||||
while (1) {
|
while (1) {
|
||||||
|
/*
|
||||||
while (USB_DeviceState == DEVICE_STATE_Suspended) {
|
while (USB_DeviceState == DEVICE_STATE_Suspended) {
|
||||||
suspend_power_down();
|
suspend_power_down();
|
||||||
if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
|
if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
|
||||||
USB_Device_SendRemoteWakeup();
|
USB_Device_SendRemoteWakeup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
keyboard_task();
|
keyboard_task();
|
||||||
|
|
||||||
#if !defined(INTERRUPT_CONTROL_ENDPOINT)
|
#if !defined(INTERRUPT_CONTROL_ENDPOINT)
|
||||||
USB_USBTask();
|
USB_USBTask();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int16_t c;
|
||||||
|
if (config_mode) {
|
||||||
|
while ((c = serial_recv2()) != -1) xprintf("%c", c);
|
||||||
|
} else {
|
||||||
|
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("%X\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: %X\n", c);
|
||||||
|
state = LED_INIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = LED_INIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bluetooth mode when ready */
|
||||||
|
if (!force_usb) {
|
||||||
|
if (rn42_ready() && host_get_driver() != &rn42_driver) {
|
||||||
|
clear_keyboard();
|
||||||
|
host_set_driver(&rn42_driver);
|
||||||
|
} else if (!rn42_ready() && host_get_driver() != &lufa_driver) {
|
||||||
|
clear_keyboard();
|
||||||
|
host_set_driver(&lufa_driver);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool local_echo = false;
|
||||||
|
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("w: toggle RN-42 config mode(enter/exit)\n");
|
||||||
|
print("l: toggle print module output(local echo)\n");
|
||||||
|
print("a: Bluetooth auto connect\n");
|
||||||
|
print("del: Bluetooth disconnect\n");
|
||||||
|
print("i: info\n");
|
||||||
|
|
||||||
|
if (config_mode) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
print("u: force USB mode\n");
|
||||||
|
return false; // to display default command help
|
||||||
|
}
|
||||||
|
case KC_W:
|
||||||
|
if (!config_mode) {
|
||||||
|
print("\nEnter RN-42 config mode\n");
|
||||||
|
print("type $$$ to enter RN-42 command mode\n");
|
||||||
|
print("type Delete to disconnect Bluetooth connection\n");
|
||||||
|
command_state = CONSOLE;
|
||||||
|
config_mode = true;
|
||||||
|
prev_driver = host_get_driver();
|
||||||
|
clear_keyboard();
|
||||||
|
host_set_driver(&rn42_config_driver);
|
||||||
|
} else {
|
||||||
|
print("\nExit RN-42 config mode\n");
|
||||||
|
command_state = ONESHOT;
|
||||||
|
config_mode = false;
|
||||||
|
clear_keyboard();
|
||||||
|
host_set_driver(prev_driver);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case KC_L:
|
||||||
|
if (local_echo) {
|
||||||
|
print("local echo off\n");
|
||||||
|
local_echo = false;
|
||||||
|
} else {
|
||||||
|
print("local echo on\n");
|
||||||
|
local_echo = true;
|
||||||
|
}
|
||||||
|
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_A:
|
||||||
|
print("auto connect\n");
|
||||||
|
rn42_autoconnect();
|
||||||
|
return true;
|
||||||
|
case KC_DELETE:
|
||||||
|
print("disconnect\n");
|
||||||
|
rn42_disconnect();
|
||||||
|
//rn42_putc('\0'); // see 5.3.4.4 DISCONNECT KEY of User's Guide
|
||||||
|
return true;
|
||||||
|
case KC_I:
|
||||||
|
print("\nRN-42 info\n");
|
||||||
|
xprintf("protocol: %s\n", (host_get_driver() == &rn42_driver) ? "RN-42" : "LUFA");
|
||||||
|
xprintf("force_usb: %X\n", force_usb);
|
||||||
|
xprintf("rn42_ready(): %X\n", rn42_ready());
|
||||||
|
xprintf("config_mode: %X\n", config_mode);
|
||||||
|
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));
|
||||||
|
if (local_echo) xprintf("%c", 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 ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#include "host.h"
|
#include "host.h"
|
||||||
#include "host_driver.h"
|
#include "host_driver.h"
|
||||||
|
#include "serial.h"
|
||||||
#include "rn42.h"
|
#include "rn42.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "wait.h"
|
||||||
|
|
||||||
|
|
||||||
/* Host driver */
|
/* Host driver */
|
||||||
|
@ -18,20 +21,146 @@ host_driver_t rn42_driver = {
|
||||||
send_consumer
|
send_consumer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void rn42_init(void)
|
||||||
|
{
|
||||||
|
// PF1: check RTS(active low)
|
||||||
|
DDRF &= ~(1<<1);
|
||||||
|
PORTF &= ~(1<<1);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// PD5: CTS (low: allow to send, high:not allowed)
|
||||||
|
DDRD |= (1<<5);
|
||||||
|
PORTD &= ~(1<<5);
|
||||||
|
|
||||||
|
serial_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rn42_putc(uint8_t c)
|
||||||
|
{
|
||||||
|
serial_send(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rn42_autoconnect(void)
|
||||||
|
{
|
||||||
|
DDRF &= ~(1<<7);
|
||||||
|
PORTF &= ~(1<<7);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rn42_disconnect(void)
|
||||||
|
{
|
||||||
|
DDRF |= (1<<7);
|
||||||
|
PORTF &= ~(1<<7);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rn42_ready(void)
|
||||||
|
{
|
||||||
|
// RTS low
|
||||||
|
return PINF&(1<<1) ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static uint8_t keyboard_leds(void) { return 0; }
|
static uint8_t keyboard_leds(void) { return 0; }
|
||||||
|
|
||||||
static void send_keyboard(report_keyboard_t *report)
|
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)
|
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)
|
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)
|
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>>8)&0xFF);
|
||||||
|
serial_send(bits&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) {}
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
#ifndef RN42_H
|
#ifndef RN42_H
|
||||||
#define RN42_H
|
#define RN42_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
host_driver_t rn42_driver;
|
host_driver_t rn42_driver;
|
||||||
|
host_driver_t rn42_config_driver;
|
||||||
|
|
||||||
|
void rn42_init(void);
|
||||||
|
void rn42_putc(uint8_t c);
|
||||||
|
void rn42_autoconnect(void);
|
||||||
|
void rn42_disconnect(void);
|
||||||
|
bool rn42_ready(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue