2013-08-13 13:05:56 +02:00
# include "keyboard.h"
# include <avr/io.h>
# include <avr/wdt.h>
# include <util/delay.h>
# include <avr/pgmspace.h>
# include <avr/interrupt.h>
# include <avr/eeprom.h>
# include "usbdrv.h"
# include "lib/prgKeyboard.h"
static uchar reportBuffer [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ; /* buffer for HID reports */
static uchar idleRate ; /* in 4 ms units */
static uchar newReport = 1 ; /* current report */
uint16_t memAddr = 0 ;
uint8_t cMode = 0 ; // 0->Keyup 1->Keydown
uint8_t oldKey = 0 ;
2013-08-15 17:08:51 +02:00
uint8_t hasReleased = 1 ;
2013-08-13 13:05:56 +02:00
uint8_t flag_keyPress = 0 ;
2013-08-15 17:08:51 +02:00
uint8_t keyAction = 0 ;
2013-08-13 13:05:56 +02:00
2013-08-13 14:19:18 +02:00
uint8_t keyType [ 16 ] ; // Will contain whether the key has to be handled as a ...
# define KEYTYPE_TEXT 0 // Text-Button (-> only button presses) or whether we need to pay attention to
# define KEYTYPE_KEY 1 // push and release events
2013-08-13 13:05:56 +02:00
// MEMORY ARCHITECTURE (INTERNAL)
# define MEM_SERIAL_OFFSET 0
# define MEM_SERIAL_LENGTH 16
// MEM. ARCH. (EXTERNAL)
# define MEM_KEY_LENGTH 512 // 512B * 16 -> 8KByte (64Kbit)
// COMMANDS:
# define CMD_PING 0
# define CMD_SWVERSION 1
# define CMD_EEWRITE 2
# define CMD_EEREAD 3
# define CMD_EEOPEN 4 // Can be used if errors occour while writing to the device.
# define CMD_RESET 5 // WDT RESET
# define CMD_SERIAL 6 // DEV-Serial PRGKBD0010911001 --> This data sits in the INETRNAL eeprom
// 6B PRGKBD
// 3B HW_REV
// 4B HW_Date (MMYY)
// 3B ID
# define CMD_IEEWRITE 7 //internal eeprom
# define CMD_IEEREAD 8 // int. eep.
# define CMD_EXEC 9 // execute from addr.
2013-08-15 17:08:51 +02:00
# define CMD_INITKEYS 10 // Auto-detect keytypes
2013-08-13 13:05:56 +02:00
/*Keys[] 0-133 codes.pdf Page 53 (10. Table 12)
ModKeys 224 - 231 codes . pdf Page 59 ( 10. Table 12 )
- - - - - - - - - - - - - - - - - - - - - - - - - */
# define INSTR_KEYDOWN 254
# define INSTR_KEYUP 253
# define INSTR_KEYPRESS 252
# define INSTR_BREAK 255
# define KEYMODE_UP 1
# define KEYMODE_DOWN 0
# define KEYMODE_PRESS 2 //push and release
2013-08-15 17:08:51 +02:00
# define KEYACTION_DOWN 0
# define KEYACTION_UP 1
2013-08-13 13:05:56 +02:00
// Very slow programming?
// -> Disable verifying the entered data in CMD_EEWRITE
/* Reportbuffer format:
0 Modifier byte
1 reserved
2 keycode array ( 0 )
3 keycode array ( 1 )
4 keycode array ( 2 )
5 keycode array ( 3 )
6 keycode array ( 4 )
7 keycode array ( 5 )
< < This is the standard usb - keyboard reportbuffer . It allows for 6 simultaneous keypresses to be detected ( excl . modifier keys ) . In this application we only use 1 , so the last 5 bytes in this buffer will always remain 0. > >
< < I decided not to optimize this in order to make it easy to add extra keys that can be pressed simultaneously > >
Modifier byte : 8 bits , each individual bit represents one of the modifier keys .
bit0 LEFT CTRL ( 1 < < 0 )
bit1 LEFT SHIFT ( 1 < < 1 )
bit2 LEFT ALT ( 1 < < 2 )
bit3 LEFT GUI ( 1 < < 3 )
bit4 RIGHT CTRL ( 1 < < 4 )
bit5 RIGHT SHIFT ( 1 < < 5 )
bit6 RIGHT ALT ( 1 < < 6 )
bit7 RIGHT GUI ( 1 < < 7 )
an example of a reportBuffer for a CTRL + ALT + Delete keypress :
{ ( ( 1 < < 0 ) + ( 1 < < 2 ) ) , 0 , 76 , 0 , 0 , 0 , 0 , 0 }
the first byte holds both the LEFT CTRL and LEFT modifier keys the 3 rd byte holds the delete key ( = = decimal 76 )
*/
/* ------------------------------------------------------------------------- */
// MOD-Key defines
# define LEFT_CTRL (1<<0)
# define LEFT_SHIFT (1<<1)
# define LEFT_ALT (1<<2)
# define LEFT_GUI (1<<3)
# define RIGHT_CTRL (1<<4)
# define RIGHT_SHIFT (1<<5)
# define RIGHT_ALT (1<<6)
# define RIGHT_GUI (1<<7)
const PROGMEM char usbHidReportDescriptor [ USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH ] = { //
0x05 , 0x01 , // USAGE_PAGE (Generic Desktop)
0x09 , 0x06 , // USAGE (Keyboard) 6
0xa1 , 0x01 , // COLLECTION (Application)
0x05 , 0x07 , // USAGE_PAGE (Keyboard)
0x19 , 0xe0 , // USAGE_MINIMUM (Keyboard LeftControl)
0x29 , 0xe7 , // USAGE_MAXIMUM (Keyboard Right GUI)
0x15 , 0x00 , // LOGICAL_MINIMUM (0)
0x25 , 0x01 , // LOGICAL_MAXIMUM (1)
0x75 , 0x01 , // REPORT_SIZE (1)
0x95 , 0x08 , // REPORT_COUNT (8)
0x81 , 0x02 , // INPUT (Data,Var,Abs) ** Modifier Byte **
0x95 , 0x01 , // REPORT_COUNT (1)
0x75 , 0x08 , // REPORT_SIZE (8)
0x81 , 0x03 , // INPUT (Cnst,Var,Abs) ** Reserved Byte **
0x95 , 0x05 , // REPORT_COUNT (5)
0x75 , 0x01 , // REPORT_SIZE (1)
0x05 , 0x08 , // USAGE_PAGE (LEDs)
0x19 , 0x01 , // USAGE_MINIMUM (Num Lock)
0x29 , 0x05 , // USAGE_MAXIMUM (Kana)
0x91 , 0x02 , // OUTPUT (Data,Var,Abs) ** LED Report **
0x95 , 0x01 , // REPORT_COUNT (1)
0x75 , 0x03 , // REPORT_SIZE (3)
0x91 , 0x03 , // OUTPUT (Cnst,Var,Abs) ** LED Report Padding **
0x95 , 0x06 , // REPORT_COUNT (6) ** here we define the maximum number of simultaneous keystrokes we can detect **
0x75 , 0x08 , // REPORT_SIZE (8)
0x15 , 0x00 , // LOGICAL_MINIMUM (0)
0x25 , 0x84 , // LOGICAL_MAXIMUM (101)
0x05 , 0x07 , // USAGE_PAGE (Keyboard)
0x19 , 0x00 , // USAGE_MINIMUM (Reserved (no event indicated))
0x29 , 0x84 , // USAGE_MAXIMUM (Keyboard Application)
0x81 , 0x00 , // INPUT (Data,Ary,Abs) ** Key arrays (6 bytes) **
0xc0 // END_COLLECTION
} ;
/* -------------------------------------------------------------------------------- */
/* ------------------------ interface to USB driver ------------------------ */
/* -------------------------------------------------------------------------------- */
uchar usbFunctionSetup ( uchar data [ 8 ] )
{
usbRequest_t * rq = ( void * ) data ;
static uchar reportBuffer [ 24 ] ;
usbMsgPtr = reportBuffer ;
if ( ( rq - > bmRequestType & USBRQ_TYPE_MASK ) = = USBRQ_TYPE_CLASS ) { /* class request type */
if ( rq - > bRequest = = USBRQ_HID_GET_REPORT ) { /* wValue: ReportType (highbyte), ReportID (lowbyte) */
/* we only have one report type, so don't look at wValue */
buildReport ( ) ;
return sizeof ( reportBuffer ) ;
} else if ( rq - > bRequest = = USBRQ_HID_GET_IDLE ) {
usbMsgPtr = & idleRate ;
return 1 ;
} else if ( rq - > bRequest = = USBRQ_HID_SET_IDLE ) {
idleRate = rq - > wValue . bytes [ 1 ] ;
}
} else {
// Here we're gonna add the functions to program the keyboard
if ( rq - > bRequest = = CMD_PING ) {
reportBuffer [ 0 ] = rq - > wValue . bytes [ 0 ] ;
return 1 ;
}
else if ( rq - > bRequest = = CMD_SWVERSION ) {
reportBuffer [ 0 ] = ' 0 ' ;
2013-08-19 17:35:25 +02:00
reportBuffer [ 1 ] = ' 3 ' ;
2013-08-13 13:05:56 +02:00
reportBuffer [ 2 ] = ' B ' ;
return 3 ;
}
else if ( rq - > bRequest = = CMD_EEWRITE ) {
EEWriteByte ( rq - > wValue . bytes [ 0 ] + ( rq - > wValue . bytes [ 1 ] < < 8 ) , rq - > wIndex . bytes [ 0 ] ) ;
2013-08-19 17:35:25 +02:00
//reportBuffer[0] = EEReadByte(rq->wValue.bytes[0] + (rq->wValue.bytes[1] << 8)); // WE MIGHT HAVE 2 REMOVE THIS BECAUSE OF PERFORMANCE ISSUES -- Reading back too quickly causes an error (-1)
reportBuffer [ 0 ] = rq - > wIndex . bytes [ 0 ] ;
2013-08-13 13:05:56 +02:00
return 1 ;
}
else if ( rq - > bRequest = = CMD_EEREAD ) {
reportBuffer [ 0 ] = EEReadByte ( rq - > wValue . bytes [ 0 ] + ( rq - > wValue . bytes [ 1 ] < < 8 ) ) ;
reportBuffer [ 1 ] = EEReadByte ( rq - > wValue . bytes [ 0 ] + ( rq - > wValue . bytes [ 1 ] < < 8 ) ) ; // We read twice to ensure the right value was loaded and transmitted.
return 2 ;
}
else if ( rq - > bRequest = = CMD_EEOPEN ) {
EEOpen ( ) ;
reportBuffer [ 0 ] = 1 ;
return 1 ;
}
else if ( rq - > bRequest = = CMD_RESET ) {
wdt_enable ( WDTO_15MS ) ; // faster reboot
while ( 1 ) ; // Wait 'til the watchdog resets our system
}
else if ( rq - > bRequest = = CMD_SERIAL ) { // CURRENTLY NOT WORKING!
uint8_t x ;
for ( x = 0 ; x < MEM_SERIAL_LENGTH ; x + + ) {
reportBuffer [ x ] = eeprom_read_byte ( x + MEM_SERIAL_OFFSET ) ;
}
return MEM_SERIAL_LENGTH ;
}
else if ( rq - > bRequest = = CMD_IEEWRITE ) {
LED_PORT ^ = ( 1 < < LED_GREEN ) ;
eeprom_write_byte ( rq - > wValue . bytes [ 0 ] + ( rq - > wValue . bytes [ 1 ] < < 8 ) , rq - > wIndex . bytes [ 0 ] ) ;
reportBuffer [ 0 ] = eeprom_read_byte ( rq - > wValue . bytes [ 0 ] + ( rq - > wValue . bytes [ 1 ] < < 8 ) ) ; // (WE MIGHT HAVE 2 REMOVE THIS BECAUSE OF PERFORMANCE ISSUES)
return 1 ;
}
else if ( rq - > bRequest = = CMD_IEEREAD ) {
reportBuffer [ 0 ] = eeprom_read_byte ( rq - > wValue . bytes [ 0 ] + ( rq - > wValue . bytes [ 1 ] < < 8 ) ) ;
reportBuffer [ 1 ] = eeprom_read_byte ( rq - > wValue . bytes [ 0 ] + ( rq - > wValue . bytes [ 1 ] < < 8 ) ) ; // Check
return 2 ;
}
else if ( rq - > bRequest = = CMD_EXEC ) {
memAddr = rq - > wValue . bytes [ 0 ] + ( rq - > wValue . bytes [ 1 ] < < 8 ) ; // jump to addr.
cMode = KEYMODE_PRESS ; //default to keymode_press
oldKey = 0 ;
newReport = 0 ; //activate continued exec
continueExecution ( ) ; // exec NOW
}
2013-08-15 17:08:51 +02:00
else if ( rq - > bRequest = = CMD_INITKEYS ) {
detectKeyType ( ) ;
reportBuffer [ 0 ] = 1 ;
return 1 ;
}
2013-08-13 13:05:56 +02:00
}
return 0 ;
}
static void buildReport ( void ) {
2013-08-19 17:35:25 +02:00
if ( newReport = = 0 ) continueExecution ( ) ;
2013-08-13 13:05:56 +02:00
}
void continueExecution ( ) {
uint8_t newData = 0 ;
while ( ! newData ) {
uint8_t instr = EEReadByte ( memAddr ) ;
if ( instr = = INSTR_BREAK ) {
// Release all keys (?)
2013-08-15 17:08:51 +02:00
if ( keyType [ oldKey - 1 ] = = KEYTYPE_TEXT ) {
uint8_t x ;
for ( x = 0 ; x < 8 ; x + + ) {
reportBuffer [ x ] = 0 ;
}
2013-08-13 13:05:56 +02:00
}
newReport = 1 ; // done
newData = 1 ; //exit loop
}
else if ( instr = = INSTR_KEYUP ) {
2013-08-15 17:08:51 +02:00
if ( ( keyType [ oldKey - 1 ] = = KEYTYPE_TEXT ) | | keyAction = = KEYACTION_DOWN ) cMode = KEYMODE_UP ;
else cMode = KEYMODE_DOWN ; // When the key on the keyboard was released, we play everything back invertedly.
2013-08-13 13:05:56 +02:00
}
else if ( instr = = INSTR_KEYDOWN ) {
2013-08-15 17:08:51 +02:00
if ( ( keyType [ oldKey - 1 ] = = KEYTYPE_TEXT ) | | keyAction = = KEYACTION_DOWN ) cMode = KEYMODE_DOWN ;
else cMode = KEYMODE_UP ; // See four lines above.
2013-08-13 13:05:56 +02:00
}
else if ( instr = = INSTR_KEYPRESS ) {
cMode = KEYMODE_PRESS ;
}
else if ( instr > 0 & & instr < = 133 ) { //normal keypress
uint8_t x = 0 ;
for ( x = 2 ; x < 8 ; x + + ) { //Scan through all possible keys
if ( cMode = = KEYMODE_DOWN ) {
if ( reportBuffer [ x ] = = 0 ) { //we need 2 find an unused key.
reportBuffer [ x ] = instr ; // press key
break ; //leave loop
}
}
else if ( cMode = = KEYMODE_UP ) {
if ( reportBuffer [ x ] = = instr ) { //we found our key
reportBuffer [ x ] = 0 ; //release it
break ; //leave loop
}
}
else { //press
if ( ! flag_keyPress ) {
if ( reportBuffer [ x ] = = 0 ) { //we found an empty key
reportBuffer [ x ] = instr ; //press it
flag_keyPress = 1 ;
break ; //leave loop
}
}
else {
if ( reportBuffer [ x ] = = instr ) { //we found our key
reportBuffer [ x ] = 0 ; //release it
flag_keyPress = 0 ; //next cmd
break ; //leave loop
}
}
}
}
newData = 1 ; //exit loop
}
2013-08-16 20:35:03 +02:00
else if ( instr > = 224 & & instr < = 231 ) {
//reportBuffer[0] |= (1 << (instr - 224)); // MOD-Keys (en)
if ( ( keyType [ oldKey - 1 ] = = KEYTYPE_TEXT ) | | keyAction = = KEYACTION_DOWN ) reportBuffer [ 0 ] | = ( 1 < < ( instr - 224 ) ) ; // MOD-Keys (en)
else reportBuffer [ 0 ] & = ~ ( 1 < < ( instr - 224 ) ) ; // MOD-Keys (dis)
}
else if ( instr > = 232 & & instr < = 239 ) {
reportBuffer [ 0 ] & = ~ ( 1 < < ( instr - 232 ) ) ; // MOD-Keys (dis)
if ( ( keyType [ oldKey - 1 ] = = KEYTYPE_TEXT ) | | keyAction = = KEYACTION_DOWN ) reportBuffer [ 0 ] & = ~ ( 1 < < ( instr - 232 ) ) ; // MOD-Keys (dis)
else reportBuffer [ 0 ] | = ( 1 < < ( instr - 232 ) ) ; // MOD-Keys (en)
}
2013-08-13 13:05:56 +02:00
if ( ! flag_keyPress ) memAddr + + ; // increase memory address
// Currently there's NO protection.The system could jump onto the next key
2013-08-19 17:35:25 +02:00
// Can be simply fixed by using a modulo-operation on the current address and the MEM_KEY_LENGTH
2013-08-13 13:05:56 +02:00
}
}
2013-08-15 17:08:51 +02:00
void detectKeyType ( ) {
2013-08-19 17:35:25 +02:00
uint8_t keyId ;
uint16_t x ;
2013-08-15 17:08:51 +02:00
for ( keyId = 0 ; keyId < 16 ; + + keyId ) {
2013-08-19 17:35:25 +02:00
LED_PORT ^ = ( 1 < < LED_RED ) ;
2013-08-15 17:08:51 +02:00
for ( x = 0 ; x < MEM_KEY_LENGTH ; + + x ) {
2013-08-19 17:35:25 +02:00
wdt_reset ( ) ;
2013-08-15 17:08:51 +02:00
uint8_t instr = EEReadByte ( keyId * MEM_KEY_LENGTH + x ) ;
if ( instr = = INSTR_KEYDOWN | | instr = = INSTR_KEYUP ) { // If KEYPRESS is used at any time, it is a text-command
keyType [ keyId ] = KEYTYPE_KEY ;
break ;
}
else if ( instr = = INSTR_BREAK ) { // If only KEYDOWN and KEYUP are used KEYDOWN it is a standard key
keyType [ keyId ] = KEYTYPE_TEXT ;
break ;
}
}
}
}
2013-08-13 13:05:56 +02:00
void keyboard ( void ) {
2013-08-19 17:35:25 +02:00
LED_PORT | = ( 1 < < LED_RED ) ;
_delay_ms ( 250 ) ; // Make sure the cable is securely plugged in.
LED_PORT & = ~ ( 1 < < LED_RED ) ;
_delay_ms ( 250 ) ;
2013-08-13 13:05:56 +02:00
wdt_enable ( WDTO_2S ) ;
EEOpen ( ) ; // We open our eeprom
uchar i ;
2013-08-13 14:19:18 +02:00
// First, we scan through the codes so we can determine the key types.
2013-08-15 17:08:51 +02:00
detectKeyType ( ) ;
2013-08-13 14:19:18 +02:00
2013-08-13 13:05:56 +02:00
cli ( ) ;
usbInit ( ) ;
usbDeviceDisconnect ( ) ; /* enforce re-enumeration, do this while interrupts are disabled! */
i = 0 ;
while ( - - i ) { /* fake USB disconnect for > 250 ms */
wdt_reset ( ) ;
_delay_ms ( 1 ) ;
}
usbDeviceConnect ( ) ;
sei ( ) ; // Interrupt enable
2013-08-19 17:35:25 +02:00
LED_PORT & = ~ ( 1 < < LED_RED ) ;
LED_PORT | = ( 1 < < LED_GREEN ) ;
2013-08-13 13:05:56 +02:00
for ( ; ; ) { /* main event loop */
wdt_reset ( ) ;
usbPoll ( ) ;
checkBtn ( ) ;
if ( usbInterruptIsReady ( ) ) {
LED_PORT | = ( 1 < < LED_RED ) ; // Switch red LED on
if ( newReport = = 0 ) { /* we can send another report */
buildReport ( ) ;
usbSetInterrupt ( reportBuffer , sizeof ( reportBuffer ) ) ;
}
}
else LED_PORT & = ~ ( 1 < < LED_RED ) ; // Only for nice visual effects again :)
}
}
void checkBtn ( ) {
if ( newReport = = 0 ) return ; // ignore all keypresses if there's a current key in progress
2013-08-15 17:08:51 +02:00
if ( getKey ( ) ! = oldKey & & getKey ( ) = = 0 ) {
keyAction = KEYACTION_UP ;
if ( keyType [ oldKey - 1 ] = = KEYTYPE_KEY & & hasReleased = = 0 ) {
newReport = 0 ;
hasReleased = 1 ;
memAddr = MEM_KEY_LENGTH * ( oldKey - 1 ) ;
cMode = KEYMODE_PRESS ; //default to keymode_press
}
else {
oldKey = 0 ;
hasReleased = 1 ;
}
_delay_ms ( 10 ) ; // Debounce switches
}
else if ( getKey ( ) ! = oldKey & & hasReleased = = 1 ) {
keyAction = KEYACTION_DOWN ;
2013-08-13 13:05:56 +02:00
hasReleased = 0 ;
oldKey = getKey ( ) ;
newReport = 0 ;
2013-08-15 17:08:51 +02:00
memAddr = MEM_KEY_LENGTH * ( oldKey - 1 ) ;
cMode = KEYMODE_PRESS ; //default to keymode_press
2013-08-13 13:05:56 +02:00
}
}