295 lines
19 KiB
C
295 lines
19 KiB
C
/**
|
|
* \file firmware/modelsuntype5.c
|
|
* \brief Hardware specific part for IBM Model M keyboard
|
|
* \author Ronald Schaten <ronald@schatenseite.de>
|
|
* \version $Id: main.c,v 1.6 2008/07/15 05:16:41 rschaten Exp $
|
|
*
|
|
* License: GNU GPL v2 (see License.txt)
|
|
*/
|
|
|
|
#include <avr/io.h>
|
|
#include <avr/pgmspace.h>
|
|
#include <util/delay.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#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 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);
|
|
}
|
|
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_Reserved /*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_Reserved /*vol-*/, 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_Reserved /*vol+*/, 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_Reserved /*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_Reserved /*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_Reserved /*find*/, KEY_Reserved /*open*/, KEY_ESCAPE, KEY_Reserved /*help*/, KEY_Reserved, KEY_Reserved /*stop*/, KEY_Reserved /*props*/, KEY_Tab, KEY_Reserved /*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_Reserved /*cut*/, KEY_Reserved /*paste*/, KEY_1, KEY_Reserved /*any*/, KEY_Reserved, KEY_Reserved /*again*/, KEY_Reserved /*undo*/ , KEY_Q, KEY_Reserved /*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) {
|
|
if (memcmp(oldmatrix, curmatrix, sizeof(curmatrix)) != 0) {
|
|
// printMatrix();
|
|
memcpy(oldmatrix, curmatrix, sizeof(curmatrix));
|
|
}
|
|
// 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);
|
|
} 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;
|
|
}
|
|
|