diff --git a/firmware/Makefile b/firmware/Makefile index 85049b4..d98f001 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -9,9 +9,15 @@ AVRDUDE = avrdude -p atmega32 -c usbasp -COMPILE = avr-gcc -Wall -Os -Iusbdrv -I. -mmcu=atmega32 #-DDEBUG_LEVEL=1 +# Options: +DEFINES = -DMODELIBMMODELM +HWOBJECTS = modelibmmodelm.o +#DEFINES = -DMODELSUNTYPE5 +#HWOBJECTS = modelsuntype5.o -OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o +COMPILE = avr-gcc -Wall -Os -Iusbdrv -I. -mmcu=atmega32 -DF_CPU=12000000L $(DEFINES) + +OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o $(HWOBJECTS) # symbolic targets: diff --git a/firmware/main.c b/firmware/main.c index 2e86167..fedf9c7 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -210,8 +210,6 @@ * (c) 2008 by Ronald Schaten - http://www.schatenseite.de */ -#define F_CPU 12000000L ///< we use a 12MHz crystal - #include #include #include @@ -222,67 +220,8 @@ #include "usbdrv.h" #include "keycodes.h" - -/* ----------------------- hardware I/O abstraction ------------------------ */ - - -#define PORTCOLUMNS PORTB ///< port on which we read the state of the columns -#define PINCOLUMNS PINB ///< port on which we read the state of the columns -#define DDRCOLUMNS DDRB ///< port on which we read the state of the columns -#define PORTROWS1 PORTA ///< first port connected to the matrix rows -#define PINROWS1 PINA ///< first port connected to the matrix rows -#define DDRROWS1 DDRA ///< first port connected to the matrix rows -#define PORTROWS2 PORTC ///< second port connected to the matrix rows -#define PINROWS2 PINC ///< second port connected to the matrix rows -#define DDRROWS2 DDRC ///< second port connected to the matrix rows - -#define PORTLEDS PORTD ///< port on which the LEDs are connected -#define PINLEDS PIND ///< port on which the LEDs are connected -#define DDRLEDS DDRD ///< port on which the LEDs are connected -#define LEDSCROLL PIND4 ///< address of the scroll-lock LED -#define LEDCAPS PIND5 ///< address of the caps-lock LED -#define LEDNUM PIND6 ///< address of the num-lock LED - -#define PORTJUMPERS PORTD ///< port for additional jumpers -#define PINJUMPERS PIND ///< port for additional jumpers -#define DDRJUMPERS DDRD ///< port for additional jumpers -#define JUMPER0 PD1 ///< address for jumper 0 -#define JUMPER1 PD3 ///< address for jumper 1 -#define JUMPER2 PD7 ///< address for jumper 2 - -/** - * Initialize hardware. Configure ports as inputs and outputs, set USB reset - * condition, start timer and blink LEDs. - */ -static void hardwareInit(void) { - // column-port is input - PORTCOLUMNS = 0xff; - DDRCOLUMNS = 0x00; - - // row-ports are output - PORTROWS1 = 0xff; - DDRROWS1 = 0x00; - PORTROWS2 = 0xff; - DDRROWS2 = 0x00; - - // port D contains USB (D0, D2), - // LEDs (D4, D5, D6) - // and Jumpers (D1, D3, D7), - // so we call it PORTD instead of PORTJUMPERS or PORTLEDS - PORTD = 0xfa; // 1000 1010: activate pull-ups except on USB- and LED-lines - DDRD = 0x75; // 0111 0101: all pins input except USB (-> USB reset) and LED-pins - // USB Reset by device only required on Watchdog Reset - _delay_us(11); // delay >10ms for USB reset - DDRD = 0x70; // 0111 0000 bin: remove USB reset condition - - // configure timer 0 for a rate of 12M/(1024 * 256) = 45.78Hz (~22ms) - TCCR0 = 5; // timer 0 prescaler: 1024 - - // blink, to indicate power-on - PORTLEDS &= ~((1 << LEDNUM) | (1 << LEDCAPS) | (1 << LEDSCROLL)); - _delay_ms(50); - PORTLEDS |= ((1 << LEDNUM) | (1 << LEDCAPS) | (1 << LEDSCROLL)); -} +#include "tools.h" +#include "modelinterface.h" /* ------------------------------------------------------------------------- */ /* ----------------------------- USB interface ----------------------------- */ @@ -294,13 +233,6 @@ static uint8_t idleRate; ///< in 4ms units static uint8_t protocolVer = 1; ///< 0 = boot protocol, 1 = report protocol uint8_t expectReport = 0; ///< flag to indicate if we expect an USB-report -#define LED_NUM 0x01 ///< num LED on a boot-protocol keyboard -#define LED_CAPS 0x02 ///< caps LED on a boot-protocol keyboard -#define LED_SCROLL 0x04 ///< scroll LED on a boot-protocol keyboard -#define LED_COMPOSE 0x08 ///< compose LED on a boot-protocol keyboard -#define LED_KANA 0x10 ///< kana LED on a boot-protocol keyboard -uint8_t LEDstate = 0; ///< current state of the LEDs - /** USB report descriptor (length is defined in usbconfig.h). The report * descriptor has been created with usb.org's "HID Descriptor Tool" which can * be downloaded from http://www.usb.org/developers/hidpage/ (it's an .exe, but @@ -390,22 +322,7 @@ uint8_t usbFunctionSetup(uint8_t data[8]) { */ uint8_t usbFunctionWrite(uchar *data, uchar len) { if (expectReport && (len == 1)) { - LEDstate = data[0]; // Get the state of all 5 LEDs - if (LEDstate & LED_NUM) { // light up caps lock - PORTLEDS &= ~(1 << LEDNUM); - } else { - PORTLEDS |= (1 << LEDNUM); - } - if (LEDstate & LED_CAPS) { // light up caps lock - PORTLEDS &= ~(1 << LEDCAPS); - } else { - PORTLEDS |= (1 << LEDCAPS); - } - if (LEDstate & LED_SCROLL) { // light up caps lock - PORTLEDS &= ~(1 << LEDSCROLL); - } else { - PORTLEDS |= (1 << LEDSCROLL); - } + setLeds(data[0]); } expectReport = 0; return 0x01; @@ -428,61 +345,6 @@ void usbSendReport(uint8_t mode, uint8_t key) { /* ------------------------------------------------------------------------- */ -uint8_t curmatrix[16]; ///< contains current state of the keyboard -uint8_t ghostmatrix[16]; ///< contains pressed keys that belong to a ghost-key situation - -/** - * The keymatrix-array contains positions of keys in the matrix. Here you can - * see which row is connected to which column when a key is pressed. This array - * probably has to be modified if this firmware is ported to a different - * keyboard. - * \sa modmatrix - */ -const uint8_t PROGMEM keymatrix[16][8] = { - // 0 1 2 3 4 5 6 7 - {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved }, // 0 - {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved }, // 1 - {KEY_ESCAPE, KEY_Tab, KEY_grave, KEY_1, KEY_Q, KEY_A, KEY_Z, KEY_Reserved }, // 2 - {KEY_Euro, KEY_capslock, KEY_F1, KEY_2, KEY_W, KEY_S, KEY_X, KEY_Reserved }, // 3 - {KEY_F4, KEY_F3, KEY_F2, KEY_3, KEY_E, KEY_D, KEY_C, KEY_Reserved }, // 4 - {KEY_G, KEY_T, KEY_5, KEY_4, KEY_R, KEY_F, KEY_V, KEY_B }, // 5 - {KEY_F5, KEY_DELETE, KEY_F9, KEY_F10, KEY_Reserved, KEY_Reserved, KEY_Return, KEY_Spacebar }, // 6 - {KEY_H, KEY_Y, KEY_6, KEY_7, KEY_U, KEY_J, KEY_M, KEY_N }, // 7 - {KEY_F6, KEY_rbracket, KEY_equals, KEY_8, KEY_I, KEY_K, KEY_comma, KEY_Reserved }, // 8 - {KEY_Reserved, KEY_F7, KEY_F8, KEY_9, KEY_O, KEY_L, KEY_dot, KEY_Reserved }, // 9 - {KEY_apostroph, KEY_lbracket, KEY_minus, KEY_0, KEY_P, KEY_semicolon, KEY_hash, KEY_slash }, // 10 - {KEY_Reserved, KEY_KP4, KEY_DeleteForward, KEY_F11, KEY_KP7, KEY_KP1, KEY_NumLock, KEY_DownArrow }, // 11 - {KEY_KP0, KEY_KP5, KEY_Insert, KEY_F12, KEY_KP8, KEY_KP2, KEY_KPslash, KEY_RightArrow }, // 12 - {KEY_KPcomma, KEY_KP6, KEY_PageUp, KEY_PageDown, KEY_KP9, KEY_KP3, KEY_KPasterisk, KEY_KPminus }, // 13 - {KEY_UpArrow, KEY_Reserved, KEY_Home, KEY_End, KEY_KPplus, KEY_KPenter, KEY_Pause, KEY_LeftArrow }, // 14 - {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_PrintScreen, KEY_ScrollLock, KEY_Reserved, KEY_Reserved, KEY_Reserved }, // 15 -}; - -/** - * The modmatrix-array contains positions of the modifier-keys in the matrix. - * It is built in the same way as the keymatrix-array. - * \sa keymatrix - */ -const uint8_t PROGMEM modmatrix[16][8] = { // contains positions of modifiers in the matrix - // 0 1 2 3 4 5 6 7 - {MOD_NONE, MOD_NONE, MOD_CONTROL_LEFT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_CONTROL_RIGHT, MOD_NONE }, // 0 - {MOD_NONE, MOD_SHIFT_LEFT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_SHIFT_RIGHT, MOD_NONE }, // 1 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 2 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 3 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 4 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 5 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 6 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 7 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 8 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 9 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 10 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 11 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 12 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 13 - {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 14 - {MOD_ALT_LEFT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_ALT_RIGHT}, // 15 -}; - /** * This structure can be used as a container for a single 'key'. It consists of * the key-code and the modifier-code. @@ -643,162 +505,6 @@ void sendString(char* string) { } } -/** - * Print the current state of the keyboard in a readable form. This function - * is used for debug-purposes only. - */ -void printMatrix(void) { - for (uint8_t i = 0; i <= 15; i++) { - char buffer[10]; - /* - sprintf(buffer, "%d%d%d%d%d%d%d%d.", - (curmatrix[i] & (1 << 0) ? 1 : 0), - (curmatrix[i] & (1 << 1) ? 1 : 0), - (curmatrix[i] & (1 << 2) ? 1 : 0), - (curmatrix[i] & (1 << 3) ? 1 : 0), - (curmatrix[i] & (1 << 4) ? 1 : 0), - (curmatrix[i] & (1 << 5) ? 1 : 0), - (curmatrix[i] & (1 << 6) ? 1 : 0), - (curmatrix[i] & (1 << 7) ? 1 : 0)); - */ - sprintf(buffer, "%2x", curmatrix[i]); - sendString(buffer); - if (i == 7) { - sendString(":"); - } else { - sendString("."); - } - } - sendString("---"); -} - -/** - * Checks if more than one bit in data is set. - * \param data value to check - * \return true if more than one bit is set - */ -uint8_t bitcount2(uint16_t data) { - data &= (data - 1); - return data != 0; -} - -/** - * check if reportBuffer contains the key - * \param buffer buffer to check - * \param key key to search - * \return 1 if buffer contains key, 0 otherwise - */ -uint8_t bufferContains(uint8_t* buffer, uint8_t key) { - for (uint8_t i = 2; i < sizeof(buffer); i++) { - if (buffer[i] == key) { - return 1; - } - } - return 0; -} - -/** - * Scan and debounce keypresses. This is the main worker function for normal - * keyboard operation, the code contains lot of comments. Basically, it first - * scans the keyboard state. If a change is detected, it initializes a counter - * that is decreased each time this function is called. If the counter reaches - * 1, that means that the same scan result has been scanned ten times in a row, - * so we can be pretty sure that the keys are in a certain state (as in: not - * bouncing). Then, the codes for keys and modifiers are searched from the two - * arrays, the USB-message to send the state is prepared. The return value of - * this function indicates if the message has to be sent. - * \return flag to indicate whether something has changed - */ -uint8_t scankeys(void) { - static uint8_t debounce = 5; - uint8_t retval = 0; - for (uint8_t row = 0; row <= 15; row++) { - if (row <= 7) { - DDRROWS1 = (1 << row); - PORTROWS1 = ~(1 << row); - DDRROWS2 = 0x00; - PORTROWS2 = 0xff; - } else { - DDRROWS1 = 0x00; - PORTROWS1 = 0xff; - // (15 - row) looks a bit weird, you would expect (row - 8) here. - // This is because pins on PORTC are ordered in the other direction - // than on PORTA. With (15 - row), we have the bytes in the - // resulting matrix matching the pins of the keyboard connector. - DDRROWS2 = (1 << (15 - row)); - PORTROWS2 = ~(1 << (15 - row)); - } - _delay_us(30); - uint8_t data = ~PINCOLUMNS; - // check if we have to prevent ghost-keys - uint16_t rows= (PINROWS1 << 8) | PINROWS2; - if (bitcount2(~rows) && bitcount2(data)) { - // ghost-key situation detected - ghostmatrix[row] = data; - } else { - ghostmatrix[row] = 0x00; - } - if (data != curmatrix[row]) { - // if a change was detected - debounce = 10; // activate debounce counter - curmatrix[row] = data; // and store the result - } - } - if (debounce) { - // Count down, but avoid underflow - debounce--; - } - if (debounce == 1) { - // debounce counter expired, create report - uint8_t reportIndex = 2; // reportBuffer[0] contains modifiers - memset(reportBuffer, 0, sizeof(reportBuffer)); // clear report buffer - for (uint8_t row = 0; row <= 15; row++) { // process all rows for key-codes - uint8_t data = curmatrix[row]; // restore buffer - if (data != 0xff) { // anything on this row? - optimization - for (uint8_t col = 0; col <= 7; col++) { // check every bit on this row - uint8_t key, modifier, isghostkey; - if (data & (1 << col)) { - key = pgm_read_byte(&keymatrix[row][col]); - modifier = pgm_read_byte(&modmatrix[row][col]); - isghostkey = ghostmatrix[row] & (1 << col); - } else { - key = KEY_Reserved; - modifier = MOD_NONE; - isghostkey = 0x00; - } - if (key != KEY_Reserved) { // keycode should be added to report - if (reportIndex >= sizeof(reportBuffer)) { // too many keycodes - if (!retval & 0x02) { // Only fill buffer once - memset(reportBuffer+2, KEY_ErrorRollOver, sizeof(reportBuffer)-2); - retval |= 0x02; // continue decoding to get modifiers - } - } else { - if (isghostkey) { - // we're in a ghost-key situation - if (bufferContains(oldReportBuffer, key)) { - // this key has been pressed before, so we still send it - reportBuffer[reportIndex] = key; // set next available entry - reportIndex++; - } - } else { - reportBuffer[reportIndex] = key; // set next available entry - reportIndex++; - } - } - } - if (modifier != MOD_NONE) { // modifier should be added to report - reportBuffer[0] |= modifier; - } - } - } - } - retval |= 0x01; // must have been a change at some point, since debounce is done - } - return retval; -} - -/* ------------------------------------------------------------------------- */ - /** * Main function, containing the main loop that manages timer- and * USB-functionality. @@ -814,13 +520,13 @@ int main(void) { memset(oldReportBuffer, 0, sizeof(oldReportBuffer)); // clear old report buffer - scankeys(); + scankeys(reportBuffer, oldReportBuffer, sizeof(reportBuffer)); while (1) { // main event loop wdt_reset(); usbPoll(); - updateNeeded = scankeys(); // changes? + updateNeeded = scankeys(reportBuffer, oldReportBuffer, sizeof(reportBuffer)); // changes? // check timer if we need periodic reports if (TIFR & (1 << TOV0)) { diff --git a/firmware/modelibmmodelm.c b/firmware/modelibmmodelm.c new file mode 100644 index 0000000..7254e5a --- /dev/null +++ b/firmware/modelibmmodelm.c @@ -0,0 +1,311 @@ +/** + * \file firmware/modelibmmodelm.c + * \brief Hardware specific part for IBM Model M keyboard + * \author Ronald Schaten + * \version $Id: main.c,v 1.6 2008/07/15 05:16:41 rschaten Exp $ + * + * License: GNU GPL v2 (see License.txt) + */ + +#include +#include +#include +#include +#include + +#include "keycodes.h" +#include "tools.h" +#include "modelinterface.h" + +/* ----------------------- hardware I/O abstraction ------------------------ */ + +#define PORTCOLUMNS PORTB ///< port on which we read the state of the columns +#define PINCOLUMNS PINB ///< port on which we read the state of the columns +#define DDRCOLUMNS DDRB ///< port on which we read the state of the columns +#define PORTROWS1 PORTA ///< first port connected to the matrix rows +#define PINROWS1 PINA ///< first port connected to the matrix rows +#define DDRROWS1 DDRA ///< first port connected to the matrix rows +#define PORTROWS2 PORTC ///< second port connected to the matrix rows +#define PINROWS2 PINC ///< second port connected to the matrix rows +#define DDRROWS2 DDRC ///< second port connected to the matrix rows + +#define PORTLEDS PORTD ///< port on which the LEDs are connected +#define PINLEDS PIND ///< port on which the LEDs are connected +#define DDRLEDS DDRD ///< port on which the LEDs are connected +#define LEDSCROLL PIND4 ///< address of the scroll-lock LED +#define LEDCAPS PIND5 ///< address of the caps-lock LED +#define LEDNUM PIND6 ///< address of the num-lock LED + +#define PORTJUMPERS PORTD ///< port for additional jumpers +#define PINJUMPERS PIND ///< port for additional jumpers +#define DDRJUMPERS DDRD ///< port for additional jumpers +#define JUMPER0 PD1 ///< address for jumper 0 +#define JUMPER1 PD3 ///< address for jumper 1 +#define JUMPER2 PD7 ///< address for jumper 2 + +uint8_t curmatrix[16]; ///< contains current state of the keyboard +uint8_t ghostmatrix[16]; ///< contains pressed keys that belong to a ghost-key situation + +void hardwareInit(void) { + // column-port is input + PORTCOLUMNS = 0xff; + DDRCOLUMNS = 0x00; + + // row-ports are output + PORTROWS1 = 0xff; + DDRROWS1 = 0x00; + PORTROWS2 = 0xff; + DDRROWS2 = 0x00; + + // port D contains USB (D0, D2), + // LEDs (D4, D5, D6) + // and Jumpers (D1, D3, D7), + // so we call it PORTD instead of PORTJUMPERS or PORTLEDS + PORTD = 0xfa; // 1000 1010: activate pull-ups except on USB- and LED-lines + DDRD = 0x75; // 0111 0101: all pins input except USB (-> USB reset) and LED-pins + // USB Reset by device only required on Watchdog Reset + _delay_us(11); // delay >10ms for USB reset + DDRD = 0x70; // 0111 0000 bin: remove USB reset condition + + // configure timer 0 for a rate of 12M/(1024 * 256) = 45.78Hz (~22ms) + TCCR0 = 5; // timer 0 prescaler: 1024 + + // blink, to indicate power-on + PORTLEDS &= ~((1 << LEDNUM) | (1 << LEDCAPS) | (1 << LEDSCROLL)); + _delay_ms(50); + PORTLEDS |= ((1 << LEDNUM) | (1 << LEDCAPS) | (1 << LEDSCROLL)); +} + +/** + * Print the current state of the keyboard in a readable form. This function + * is used for debug-purposes only. + */ +void printMatrix(void) { + for (uint8_t i = 0; i <= 15; i++) { + char buffer[10]; + /* + sprintf(buffer, "%d%d%d%d%d%d%d%d.", + (curmatrix[i] & (1 << 0) ? 1 : 0), + (curmatrix[i] & (1 << 1) ? 1 : 0), + (curmatrix[i] & (1 << 2) ? 1 : 0), + (curmatrix[i] & (1 << 3) ? 1 : 0), + (curmatrix[i] & (1 << 4) ? 1 : 0), + (curmatrix[i] & (1 << 5) ? 1 : 0), + (curmatrix[i] & (1 << 6) ? 1 : 0), + (curmatrix[i] & (1 << 7) ? 1 : 0)); + */ + sprintf(buffer, "%2x", curmatrix[i]); + sendString(buffer); + if (i == 7) { + sendString(":"); + } else { + sendString("."); + } + } + sendString("---"); +} + +#define LED_NUM 0x01 ///< num LED on a boot-protocol keyboard +#define LED_CAPS 0x02 ///< caps LED on a boot-protocol keyboard +#define LED_SCROLL 0x04 ///< scroll LED on a boot-protocol keyboard +#define LED_COMPOSE 0x08 ///< compose LED on a boot-protocol keyboard +#define LED_KANA 0x10 ///< kana LED on a boot-protocol keyboard + +void setLeds(uint8_t LEDstate) { + if (LEDstate & LED_NUM) { // light up caps lock + PORTLEDS &= ~(1 << LEDNUM); + } else { + PORTLEDS |= (1 << LEDNUM); + } + if (LEDstate & LED_CAPS) { // light up caps lock + PORTLEDS &= ~(1 << LEDCAPS); + } else { + PORTLEDS |= (1 << LEDCAPS); + } + if (LEDstate & LED_SCROLL) { // light up caps lock + PORTLEDS &= ~(1 << LEDSCROLL); + } else { + PORTLEDS |= (1 << LEDSCROLL); + } +} + +/** + * The keymatrix-array contains positions of keys in the matrix. Here you can + * see which row is connected to which column when a key is pressed. This array + * probably has to be modified if this firmware is ported to a different + * keyboard. + * \sa modmatrix + */ +const uint8_t PROGMEM keymatrix[16][8] = { + // 0 1 2 3 4 5 6 7 + {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved }, // 0 + {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved }, // 1 + {KEY_ESCAPE, KEY_Tab, KEY_grave, KEY_1, KEY_Q, KEY_A, KEY_Z, KEY_Reserved }, // 2 + {KEY_Euro, KEY_capslock, KEY_F1, KEY_2, KEY_W, KEY_S, KEY_X, KEY_Reserved }, // 3 + {KEY_F4, KEY_F3, KEY_F2, KEY_3, KEY_E, KEY_D, KEY_C, KEY_Reserved }, // 4 + {KEY_G, KEY_T, KEY_5, KEY_4, KEY_R, KEY_F, KEY_V, KEY_B }, // 5 + {KEY_F5, KEY_DELETE, KEY_F9, KEY_F10, KEY_Reserved, KEY_Reserved, KEY_Return, KEY_Spacebar }, // 6 + {KEY_H, KEY_Y, KEY_6, KEY_7, KEY_U, KEY_J, KEY_M, KEY_N }, // 7 + {KEY_F6, KEY_rbracket, KEY_equals, KEY_8, KEY_I, KEY_K, KEY_comma, KEY_Reserved }, // 8 + {KEY_Reserved, KEY_F7, KEY_F8, KEY_9, KEY_O, KEY_L, KEY_dot, KEY_Reserved }, // 9 + {KEY_apostroph, KEY_lbracket, KEY_minus, KEY_0, KEY_P, KEY_semicolon, KEY_hash, KEY_slash }, // 10 + {KEY_Reserved, KEY_KP4, KEY_DeleteForward, KEY_F11, KEY_KP7, KEY_KP1, KEY_NumLock, KEY_DownArrow }, // 11 + {KEY_KP0, KEY_KP5, KEY_Insert, KEY_F12, KEY_KP8, KEY_KP2, KEY_KPslash, KEY_RightArrow }, // 12 + {KEY_KPcomma, KEY_KP6, KEY_PageUp, KEY_PageDown, KEY_KP9, KEY_KP3, KEY_KPasterisk, KEY_KPminus }, // 13 + {KEY_UpArrow, KEY_Reserved, KEY_Home, KEY_End, KEY_KPplus, KEY_KPenter, KEY_Pause, KEY_LeftArrow }, // 14 + {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_PrintScreen, KEY_ScrollLock, KEY_Reserved, KEY_Reserved, KEY_Reserved }, // 15 +}; + +/** + * The modmatrix-array contains positions of the modifier-keys in the matrix. + * It is built in the same way as the keymatrix-array. + * \sa keymatrix + */ +const uint8_t PROGMEM modmatrix[16][8] = { // contains positions of modifiers in the matrix + // 0 1 2 3 4 5 6 7 + {MOD_NONE, MOD_NONE, MOD_CONTROL_LEFT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_CONTROL_RIGHT, MOD_NONE }, // 0 + {MOD_NONE, MOD_SHIFT_LEFT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_SHIFT_RIGHT, MOD_NONE }, // 1 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 2 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 3 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 4 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 5 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 6 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 7 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 8 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 9 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 10 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 11 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 12 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 13 + {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE }, // 14 + {MOD_ALT_LEFT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_ALT_RIGHT}, // 15 +}; + +/** + * Checks if more than one bit in data is set. + * \param data value to check + * \return true if more than one bit is set + */ +static uint8_t bitcount2(uint16_t data) { + data &= (data - 1); + return data != 0; +} + +/** + * check if reportBuffer contains the key + * \param buffer buffer to check + * \param key key to search + * \return 1 if buffer contains key, 0 otherwise + */ +static uint8_t bufferContains(uint8_t* buffer, uint8_t key) { + for (uint8_t i = 2; i < sizeof(buffer); i++) { + if (buffer[i] == key) { + return 1; + } + } + return 0; +} + +/** + * Scan and debounce keypresses. This is the main worker function for normal + * keyboard operation, the code contains lot of comments. Basically, it first + * scans the keyboard state. If a change is detected, it initializes a counter + * that is decreased each time this function is called. If the counter reaches + * 1, that means that the same scan result has been scanned ten times in a row, + * so we can be pretty sure that the keys are in a certain state (as in: not + * bouncing). Then, the codes for keys and modifiers are searched from the two + * arrays, the USB-message to send the state is prepared. The return value of + * this function indicates if the message has to be sent. + * \return flag to indicate whether something has changed + */ +uint8_t scankeys(uint8_t* reportBuffer, uint8_t* oldReportBuffer, uint8_t sizeOfReportBuffer) { + static uint8_t debounce = 5; + uint8_t retval = 0; + for (uint8_t row = 0; row <= 15; row++) { + if (row <= 7) { + DDRROWS1 = (1 << row); + PORTROWS1 = ~(1 << row); + DDRROWS2 = 0x00; + PORTROWS2 = 0xff; + } else { + DDRROWS1 = 0x00; + PORTROWS1 = 0xff; + // (15 - row) looks a bit weird, you would expect (row - 8) here. + // This is because pins on PORTC are ordered in the other direction + // than on PORTA. With (15 - row), we have the bytes in the + // resulting matrix matching the pins of the keyboard connector. + DDRROWS2 = (1 << (15 - row)); + PORTROWS2 = ~(1 << (15 - row)); + } + _delay_us(30); + uint8_t data = ~PINCOLUMNS; + // check if we have to prevent ghost-keys + uint16_t rows= (PINROWS1 << 8) | PINROWS2; + if (bitcount2(~rows) && bitcount2(data)) { + // ghost-key situation detected + ghostmatrix[row] = data; + } else { + ghostmatrix[row] = 0x00; + } + if (data != curmatrix[row]) { + // if a change was detected + debounce = 10; // activate debounce counter + curmatrix[row] = data; // and store the result + } + } + if (debounce) { + // Count down, but avoid underflow + debounce--; + } + if (debounce == 1) { + // debounce counter expired, create report + uint8_t reportIndex = 2; // reportBuffer[0] contains modifiers + memset(reportBuffer, 0, sizeOfReportBuffer); // clear report buffer + for (uint8_t row = 0; row <= 15; row++) { // process all rows for key-codes + uint8_t data = curmatrix[row]; // restore buffer + if (data != 0xff) { // anything on this row? - optimization + for (uint8_t col = 0; col <= 7; col++) { // check every bit on this row + uint8_t key, modifier, isghostkey; + if (data & (1 << col)) { + key = pgm_read_byte(&keymatrix[row][col]); + modifier = pgm_read_byte(&modmatrix[row][col]); + isghostkey = ghostmatrix[row] & (1 << col); + } else { + key = KEY_Reserved; + modifier = MOD_NONE; + isghostkey = 0x00; + } + if (key != KEY_Reserved) { // keycode should be added to report + if (reportIndex >= sizeOfReportBuffer) { // too many keycodes + if (!retval & 0x02) { // Only fill buffer once + memset(reportBuffer+2, KEY_ErrorRollOver, sizeOfReportBuffer-2); + retval |= 0x02; // continue decoding to get modifiers + } + } else { + if (isghostkey) { + // we're in a ghost-key situation + if (bufferContains(oldReportBuffer, key)) { + // this key has been pressed before, so we still send it + reportBuffer[reportIndex] = key; // set next available entry + reportIndex++; + } + } else { + reportBuffer[reportIndex] = key; // set next available entry + reportIndex++; + } + } + } + if (modifier != MOD_NONE) { // modifier should be added to report + reportBuffer[0] |= modifier; + } + } + } + } + retval |= 0x01; // must have been a change at some point, since debounce is done + } + if(sizeof(reportBuffer) == 8) + PORTLEDS &= ~(1 << LEDNUM); + + return retval; +} + diff --git a/firmware/modelinterface.h b/firmware/modelinterface.h new file mode 100644 index 0000000..fd3e11d --- /dev/null +++ b/firmware/modelinterface.h @@ -0,0 +1,44 @@ +/** + * \file firmware/modelibmmodelm.c + * \brief Hardware specific part for IBM Model M keyboard + * \author Ronald Schaten + * \version $Id: main.c,v 1.6 2008/07/15 05:16:41 rschaten Exp $ + * + * License: GNU GPL v2 (see License.txt) + */ + +#include + +/** + * Initialize hardware. Configure ports as inputs and outputs, set USB reset + * condition, start timer and blink LEDs. + */ +void hardwareInit(void); + +/** + * Print the current state of the keyboard in a readable form. This function + * is used for debug-purposes only. + */ +void printMatrix(void); + +/** + * This function sets the LEDs according to the given data. + * \param LEDstate bitfield with LED info + */ +void setLeds(uint8_t LEDstate); + +/** + * Scan and debounce keypresses. This is the main worker function for normal + * keyboard operation, the code contains lot of comments. Basically, it first + * scans the keyboard state. If a change is detected, it initializes a counter + * that is decreased each time this function is called. If the counter reaches + * 1, that means that the same scan result has been scanned ten times in a row, + * so we can be pretty sure that the keys are in a certain state (as in: not + * bouncing). Then, the codes for keys and modifiers are searched from the two + * arrays, the USB-message to send the state is prepared. The return value of + * this function indicates if the message has to be sent. + * \param reportBuffer array with the current USB report + * \param oldReportBuffer array with the last USB report + * \return flag to indicate whether something has changed + */ +uint8_t scankeys(uint8_t* reportBuffer, uint8_t* oldReportBuffer, uint8_t sizeOfReportBuffer); diff --git a/firmware/tools.h b/firmware/tools.h new file mode 100644 index 0000000..b1d7dc3 --- /dev/null +++ b/firmware/tools.h @@ -0,0 +1,10 @@ +/** + * \file tools.h + * \brief TODO + * \author Ronald Schaten + * \version $Id$ + * + * License: TODO + */ + +void sendString(char* string);