refactored code to enable implementation on different hardware
This commit is contained in:
parent
ecafd0c256
commit
28c5b019c7
@ -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:
|
||||
|
304
firmware/main.c
304
firmware/main.c
@ -210,8 +210,6 @@
|
||||
* <b>(c) 2008 by Ronald Schaten - http://www.schatenseite.de</b>
|
||||
*/
|
||||
|
||||
#define F_CPU 12000000L ///< we use a 12MHz crystal
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/pgmspace.h>
|
||||
@ -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)) {
|
||||
|
311
firmware/modelibmmodelm.c
Normal file
311
firmware/modelibmmodelm.c
Normal file
@ -0,0 +1,311 @@
|
||||
/**
|
||||
* \file firmware/modelibmmodelm.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"
|
||||
|
||||
/* ----------------------- 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;
|
||||
}
|
||||
|
44
firmware/modelinterface.h
Normal file
44
firmware/modelinterface.h
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* \file firmware/modelibmmodelm.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 <stdint.h>
|
||||
|
||||
/**
|
||||
* 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);
|
10
firmware/tools.h
Normal file
10
firmware/tools.h
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* \file tools.h
|
||||
* \brief TODO
|
||||
* \author Ronald Schaten <ronald@schatenseite.de>
|
||||
* \version $Id$
|
||||
*
|
||||
* License: TODO
|
||||
*/
|
||||
|
||||
void sendString(char* string);
|
Loading…
Reference in New Issue
Block a user