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);