/** * \file firmware/modelsuntype5.c * \brief Hardware specific part for Sun Type 5 keyboard * \author Ronald Schaten * \version $Id$ * * License: GNU GPL v2 (see License.txt) */ #include #include #include #include #include #include "keycodes.h" #include "tools.h" #include "modelinterface.h" #include "usbdrv.h" /* ----------------------- hardware I/O abstraction ------------------------ */ #define PORTLEDS PORTB ///< port on which the LEDs are connected #define PINLEDS PINB ///< port on which the LEDs are connected #define DDRLEDS DDRB ///< port on which the LEDs are connected #define LEDSCROLL PINB7 ///< address of the scroll-lock LED #define LEDNUM PINB6 ///< address of the num-lock LED #define LEDCOMP PINB5 ///< address of the compose LED #define LEDCAPS PINB4 ///< address of the caps-lock LED #define SRCLOCKON PORTC |= (1 << PC5) #define SRCLOCKOFF PORTC &= ~(1 << PC5) #define SRDATAON PORTC |= (1 << PC6) #define SRDATAOFF PORTC &= ~(1 << PC6) #define SRSTROBEON PORTC |= (1 << PC7) #define SRSTROBEOFF PORTC &= ~(1 << PC7) uint16_t curmatrix[22]; ///< contains current state of the keyboard uint16_t oldmatrix[22]; ///< contains old state of the keyboard uint16_t ghostmatrix[22]; ///< contains pressed keys that belong to a ghost-key situation void hardwareInit(void) { // configure ports DDRA = 0x00; PORTA = 0xff; DDRB = (1 << PB4) |(1 << PB5) | (1 << PB6) | (1 << PB7); PORTB = (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3); DDRC = (1 << PC5) | (1 << PC6) | (1 << PC7); PORTC = (1 << PC0) | (1 << PC1) | (1 << PC2) | (1 << PC3) | (1 << PC4); DDRD &= ~((1 << PD4) | (1 << PD5) | (1 << PD6) | (1 << PD7)); PORTD |= (1 << PD4) | (1 << PD5) | (1 << PD6) | (1 << PD7); DDRD |= (1 << PD0) | (1 << PD2); // needed for USB reset _delay_us(11); // delay >10ms for USB reset DDRD &= ~((1 << PD0) | (1 << PD2)); // 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) | (1 << LEDCOMP)); _delay_ms(50); PORTLEDS |= ((1 << LEDNUM) | (1 << LEDCAPS) | (1 << LEDSCROLL) | (1 << LEDCOMP)); // clean shift registers -- set all row lines to 1, except for KEYROW SRDATAON; SRSTROBEOFF; for (uint8_t i = 0; i < 21; i++) { SRCLOCKON; SRCLOCKOFF; } SRSTROBEON; SRSTROBEOFF; } /** * 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 < 22; i++) { char buffer[10]; sprintf(buffer, "%4x", ~(curmatrix[i] | 0xe000)); for(int j= 0; j < strlen(buffer); j++) { if(buffer[j] == '0') buffer[j]= ' '; } sendString(buffer); if (i == 11) { sendString(":"); } else { sendString("."); } } sendString("\n"); } void setSpeed(uint8_t speed) { // not used in this model/version } void setLeds(uint8_t LEDstate) { if (LEDstate & LED_NUM) { // light up num 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 scroll lock PORTLEDS &= ~(1 << LEDSCROLL); } else { PORTLEDS |= (1 << LEDSCROLL); } if (LEDstate & LED_COMPOSE) { // light up compose PORTLEDS &= ~(1 << LEDCOMP); } else { PORTLEDS |= (1 << LEDCOMP); } } /** * 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[22][13] = { // 0 1 2 3 4 5 6 7 8 9 10 11 12 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_dot, KEY_F8, KEY_F10, KEY_Reserved, KEY_9, KEY_minus, KEY_P, KEY_K}, // 0 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_slash, KEY_equals, KEY_F11, KEY_Reserved, KEY_0, KEY_lbracket, KEY_semicolon, KEY_L}, // 1 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_LeftArrow, KEY_DownArrow, KEY_grave, KEY_F12, KEY_Reserved, KEY_Euro, KEY_DELETE, KEY_rbracket, KEY_apostroph}, // 2 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_RightArrow, KEY_UpArrow, KEY_Insert, KEY_PrintScreen, KEY_Reserved, KEY_NumLock, KEY_DeleteForward, KEY_Return, KEY_KP4}, // 3 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_KP2, KEY_KP5, KEY_ScrollLock, KEY_Mute, KEY_Reserved, KEY_KPslash, KEY_Home, KEY_End, KEY_KP7}, // 4 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_KP3, KEY_KP6, KEY_Pause, KEY_Volume_Down, KEY_Reserved, KEY_KPasterisk, KEY_PageUp, KEY_PageDown, KEY_KP8}, // 5 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_KPcomma, KEY_KP0, KEY_KPminus, KEY_Volume_Up, KEY_Reserved, KEY_KP9, KEY_KPplus, KEY_KPenter, KEY_KP1}, // 6 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved}, // 7 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Application /*compose*/, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved}, // 8 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Power, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved}, // 9 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved}, // 10 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved}, // 11 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_capslock, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved}, // 12 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Find, KEY_Select /*open*/, KEY_ESCAPE, KEY_Help, KEY_Reserved, KEY_Stop, KEY_Menu /*props*/, KEY_Tab, KEY_Execute /*front*/}, // 13 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved}, // 14 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Cut, KEY_Paste, KEY_1, KEY_Cancel /*any*/, KEY_Reserved, KEY_Again, KEY_Undo, KEY_Q, KEY_Copy}, // 15 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Z, KEY_2, KEY_F1, KEY_Reserved, KEY_3, KEY_E, KEY_W, KEY_D}, // 16 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_C, KEY_X, KEY_4, KEY_F2, KEY_Reserved, KEY_5, KEY_R, KEY_F, KEY_A}, // 17 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_V, KEY_N, KEY_F3, KEY_F5, KEY_Reserved, KEY_6, KEY_T, KEY_G, KEY_S}, // 18 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_B, KEY_M, KEY_F4, KEY_F6, KEY_Reserved, KEY_7, KEY_U, KEY_Y, KEY_H}, // 19 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved}, // 20 {KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Reserved, KEY_Spacebar, KEY_comma, KEY_F7, KEY_F9, KEY_Reserved, KEY_8, KEY_O, KEY_I, KEY_J}, // 21 }; /** * 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[22][13] = { // contains positions of modifiers in the matrix // 0 1 2 3 4 5 6 7 8 9 10 11 12 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 0 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 1 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, 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, 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, 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, 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, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 6 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_ALT_RIGHT, 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, 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, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 9 {MOD_NONE, MOD_GUI_RIGHT, MOD_GUI_LEFT, MOD_NONE, MOD_NONE, 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_ALT_LEFT, 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, 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, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 13 {MOD_SHIFT_RIGHT, MOD_NONE, MOD_NONE, MOD_SHIFT_LEFT, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 14 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 15 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 16 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 17 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 18 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 19 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_CONTROL_LEFT}, // 20 {MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE, MOD_NONE}, // 21 }; /** * 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; SRDATAOFF; // mark first bit as the active one SRCLOCKON; SRCLOCKOFF; // trigger clock to shift first bit into the register SRDATAON; // all further bits will be inactive for (uint8_t row = 0; row < 22; row++) { SRSTROBEON; SRSTROBEOFF; // copy current register values to output _delay_us(30); uint16_t data = ((PINC & 0x1f) << 8) | PINA; // we need the lower five bits of PINC and PINA if (data != curmatrix[row]) { // if a change was detected debounce = 10; // activate debounce counter curmatrix[row] = data; // and store the result ghostmatrix[row] = 0; } SRCLOCKON; SRCLOCKOFF; // trigger clock to shift register values } if (debounce) { // Count down, but avoid underflow debounce--; } if (debounce == 1) { memset(ghostmatrix, 0, sizeof(ghostmatrix)); for (uint8_t i = 0; i < 21; i++) { uint16_t keys = (~curmatrix[i]) & 0x1fff; if (bitcount2(keys)) { // check if 2 or more keys are pressed for (uint8_t j = i + 1; j < 22; j++) { uint16_t common_columns = keys & (~curmatrix[j]); if (bitcount2(common_columns)) { // 2 or more columns in common => ghostkeys ghostmatrix[i] |= common_columns; ghostmatrix[j] |= common_columns; } } } } // 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 < 22; row++) { // process all rows for key-codes uint16_t data = curmatrix[row]; // restore buffer if (data != 0x1fff) { // anything on this row? - optimization for (uint8_t col = 0; col < 13; 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)) != 0; } 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 } return retval; }