Created
July 10, 2016 17:32
-
-
Save cheery/10c7b2a55ceb8353b8f5a5dfb2f59e84 to your computer and use it in GitHub Desktop.
Keyboard driver to my yet unnamed keyboard
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Keyboard firmware, for a custom keyboard that is yet unnamed. | |
#include <HID.h> | |
#if !defined(_USING_HID) | |
#error HID required | |
#endif | |
// I didn't stop looking what the HID report descriptor here means. | |
// It looks like it describes the shape of my keyboard report and the ID | |
// of the report. | |
// If you wonder what each thing means in that list, go fetch the USB spec. | |
static const uint8_t _hidReportDescriptor[] PROGMEM = { | |
// Keyboard | |
0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 47 | |
0x09, 0x06, // USAGE (Keyboard) | |
0xa1, 0x01, // COLLECTION (Application) | |
0x85, 0x02, // REPORT_ID (2) | |
0x05, 0x07, // USAGE_PAGE (Keyboard) | |
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) | |
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) | |
0x15, 0x00, // LOGICAL_MINIMUM (0) | |
0x25, 0x01, // LOGICAL_MAXIMUM (1) | |
0x75, 0x01, // REPORT_SIZE (1) | |
0x95, 0x08, // REPORT_COUNT (8) | |
0x81, 0x02, // INPUT (Data,Var,Abs) | |
0x95, 0x01, // REPORT_COUNT (1) | |
0x75, 0x08, // REPORT_SIZE (8) | |
0x81, 0x03, // INPUT (Cnst,Var,Abs) | |
0x95, 0x06, // REPORT_COUNT (6) | |
0x75, 0x08, // REPORT_SIZE (8) | |
0x15, 0x00, // LOGICAL_MINIMUM (0) | |
0x25, 0x80, // LOGICAL_MAXIMUM (0x80, was 0x65, consider change USAGE_MAXIMUM too when you change this) | |
0x05, 0x07, // USAGE_PAGE (Keyboard) | |
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) | |
0x29, 0x80, // USAGE_MAXIMUM (Keyboard Application) | |
0x81, 0x00, // INPUT (Data,Ary,Abs) | |
0xc0, // END_COLLECTION | |
}; | |
typedef struct | |
{ | |
uint8_t modifiers; | |
uint8_t reserved; | |
uint8_t keys[6]; | |
} KeyReport; | |
// I'm not sure whether these things are required to be wrapped into this silly small class. | |
// There was not much pressure to reduce code smells so I left it in here when I got it working. | |
class USBKeyboard { | |
private: | |
KeyReport keyReport; | |
public: | |
USBKeyboard(void) { | |
static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor)); | |
HID().AppendDescriptor(&node); | |
keyReport.modifiers = 0; | |
keyReport.reserved = 0; | |
keyReport.keys[0] = 0; | |
keyReport.keys[1] = 0; | |
keyReport.keys[2] = 0; | |
keyReport.keys[3] = 0; | |
keyReport.keys[4] = 0; | |
keyReport.keys[5] = 0; | |
} | |
void sendReport() { | |
HID().SendReport(2,&keyReport,sizeof(KeyReport)); | |
} | |
void begin() { | |
} | |
void end() { | |
} | |
void press(int k) { | |
if (0xE0 <= k && k <= 0xE8) { | |
keyReport.modifiers |= 1 << (k - 0xE0); | |
sendReport(); | |
return; | |
} | |
if (keyReport.keys[0] != k && keyReport.keys[1] != k && | |
keyReport.keys[2] != k && keyReport.keys[3] != k && | |
keyReport.keys[4] != k && keyReport.keys[5] != k) { | |
for (int i=0; i<6; i++) { | |
if (keyReport.keys[i] == 0x00) { | |
keyReport.keys[i] = k; | |
break; | |
} | |
} | |
} | |
sendReport(); | |
} | |
void release(int k) { | |
if (0xE0 <= k && k <= 0xE8) { | |
keyReport.modifiers &= ~(1 << (k - 0xE0)); | |
sendReport(); | |
return; | |
} | |
for (int i=0; i<6; i++) { | |
if (0 != k && keyReport.keys[i] == k) { | |
keyReport.keys[i] = 0x00; | |
} | |
} | |
sendReport(); | |
} | |
}; | |
USBKeyboard usb_keyboard; | |
// Keys are wired in a matrix, which is being rapidly polled every moment. | |
// Row pins are normally set high, and during scanning | |
// each pin lowers momentarily for reading. | |
// The column pins are configured with pullup so | |
// we don't need external pull-up resistors in our circuit. | |
#define COLC 7 | |
#define ROWC 6 | |
const int rows[ROWC] = {0, 1, 2, 3, 4, 5}; | |
const int cols[COLC] = {6, 7, 8, 9, 10, 11, 12}; | |
// http://www.mindrunway.ru/IgorPlHex/USBKeyScan.pdf | |
// https://www.arduino.cc/en/Reference/KeyboardModifiers | |
#define MLCT 0xE0 | |
#define MLSH 0xE1 | |
#define MLAL 0xE2 | |
#define MLGU 0xE3 | |
// We need the mapping because our keys do not directly map to ascii. | |
const int mapping[ROWC*COLC] = { | |
0x2A, 0x64, 0x35, 0x68, 0x69, 0x2E, MLCT, | |
0x4C, 0x31, 0x6A, 0x6B, 0x6C, 0x6D, MLAL, | |
0x29, 0x1E, 0x1F, 0x20, 0x21, 0x22, MLGU, | |
0x2B, 0x14, 0x1A, 0x08, 0x15, 0x17, 0x4A, | |
0x39, 0x04, 0x16, 0x07, 0x09, 0x0A, 0x4D, | |
MLSH, 0x1D, 0x1B, 0x06, 0x19, 0x05, 0x00, | |
}; | |
// And we need a state table because the keyboard protocol must | |
// send state changes rather than states themselves. | |
int state[ROWC*COLC]; | |
void setup() { | |
//Serial.begin(9600); | |
usb_keyboard.begin(); | |
for (int x = 0; x < COLC; x++) { | |
pinMode(cols[x], INPUT_PULLUP); | |
} | |
for (int y = 0; y < ROWC; y++) { | |
pinMode(rows[y], OUTPUT); | |
digitalWrite(rows[y], HIGH); | |
} | |
for (int y = 0; y < ROWC; y++) { | |
for (int x = 0; x < COLC; x++) { | |
state[y*COLC + x] = HIGH; | |
} | |
} | |
} | |
void loop() { | |
for (int y = 0; y < ROWC; y++) { | |
digitalWrite(rows[y], LOW); | |
delayMicroseconds(500); // wait a little bit for signal to stabilize. | |
for (int x = 0; x < COLC; x++) { | |
int now = digitalRead(cols[x]); | |
int was = state[y*COLC + x]; | |
if (now != was) { | |
if (now == HIGH) { | |
//Serial.print(mapping[y*COLC + x], HEX); | |
//Serial.println("H"); | |
usb_keyboard.release(mapping[y*COLC + x]); | |
} else { | |
usb_keyboard.press(mapping[y*COLC + x]); | |
} | |
} | |
state[y*COLC + x] = now; | |
} | |
digitalWrite(rows[y], HIGH); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment