This is my go on a custom layout keyboard. My idea was to keep everything as familiar as possible while improving a little on the horrible stagger of the regular qwerty layout.
I made a pcb design using the software from
http://expresspcb.com/ which I then printed and ironed onto a copper laminate for etching. The
home brew etching method worked like a charm. I used a single-sided pcb and a lot of jumpers to connect the columns, the rows are drawn solid.
The resulting pcb design. Not all solders came out great... I relly regretted my decision on using wires to connect the controller when soldering them. It was a mess getting everything into there =P Next time I will use narrower traces, to be able to draw them all up to the controller, and minimize the use of wires.
And the front with some blank filco keys. The switches are the plate mounted ones. I didn't care much to have to drill all those extra holes... I used a dremel
drill stand and good quality carbide drill bits. The drill stand was not quite sturdy enough to handle the 4mm bit so some of those holes got a bit large. All the switches are straight enough not to jam the keys at least. The wider ones will have stabilizers to keep them in place too. Also I might get a plate cut for me at some point in the future.
Using a teensy as the controller.
Here is the code. I'm certainly no ace at c programming so there is surely a lot that can be done to improve on it. This example.c file contains all my own code. It goes together with the
usb_keyboard project.
The key mapping layout somewhat arbitrary. I will be trying different ones, moving keys around.
This code is somewhat outdated and contains at least one pretty severe bug. Updated code will be available in my
keyboard design guide.
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "usb_keyboard.h"
#define bool uint8_t
#define true 1
#define false 0
#define _PINB (uint8_t *const)&PINB
#define _PORTC (uint8_t *const)&PORTC
#define _PORTD (uint8_t *const)&PORTD
#define _PORTF (uint8_t *const)&PORTF
#define NULL 0
#define NA 0
#define KEY_MACRO 0
#define KEY_FN 0
#define FN_KEY1_ID 11*6+0
#define FN_KEY2_ID 2*6+0
#define MACRO_KEY_ID 0*6+0
const uint8_t is_modifier[84] = {
1, 1, 1, 0, 0, 0, // COL 0
NA, NA, 0, 0, 0, 0, // COL 1
1, 0, 0, 0, 0, 0, // COL 2
1, 0, 0, 0, 0, 0, // COL 3
0, 0, 0, 0, 0, 0, // COL 4
0, 0, 0, 0, 0, 0, // COL 5
0, 0, 0, 0, 0, 0, // COL 6
0, 0, 0, 0, 0, 0, // COL 7
0, 0, 0, 0, 0, 0, // COL 8
0, 0, 0, 0, 0, 0, // COL 9
1, 0, 0, 0, 0, 0, // COL 10
1, 0, 0, 0, 0, 0, // COL 11
NA, NA, 0, 0, 0, 0, // COL 12
1, 1, 0, 0, 0, 0 // COL 13
};
const uint8_t layer1[84] = {
//ROW 0 ROW 1 ROW 2 ROW 3 ROW 4 ROW5
KEY_MACRO, KEY_LEFT_SHIFT, KEY_LEFT_CTRL, KEY_TAB, KEY_ESC, NULL, // COL 0
NA, NA, KEY_A, KEY_Q, KEY_TILDE, KEY_F1, // COL 1
KEY_FN, KEY_Z, KEY_S, KEY_W, KEY_1, KEY_F2, // COL 2
KEY_LEFT_ALT, KEY_X, KEY_D, KEY_E, KEY_2, KEY_F3, // COL 3
NA, KEY_C, KEY_F, KEY_R, KEY_3, KEY_F4, // COL 4
NA, KEY_V, KEY_G, KEY_T, KEY_4, KEY_F5, // COL 5
KEY_SLASH, KEY_BACKSLASH, KEY_LEFT_BRACE, KEY_RIGHT_BRACE, KEY_5, KEY_F6, // COL 6
KEY_SPACE, KEY_B, KEY_QUOTE, KEY_MINUS, KEY_EQUAL, KEY_F7, // COL 7
NA, KEY_N, KEY_H, KEY_Y, KEY_6, KEY_F8, // COL 8
NA, KEY_M, KEY_J, KEY_U, KEY_7, KEY_F9, // COL 9
KEY_RIGHT_ALT, KEY_COMMA, KEY_K, KEY_I, KEY_8, KEY_F10, // COL 10
KEY_FN, KEY_PERIOD, KEY_L, KEY_O, KEY_9, KEY_F11, // COL 11
NA, NA, KEY_SEMICOLON, KEY_P, KEY_0, KEY_F12, // COL 12
KEY_RIGHT_CTRL, KEY_RIGHT_SHIFT, KEY_ENTER, KEY_BACKSPACE, KEY_DELETE, NULL // COL 13
};
const uint8_t layer2[84] = {
//ROW 0 ROW 1 ROW 2 ROW 3 ROW 4 ROW5
NULL, KEY_LEFT_SHIFT, KEY_LEFT_CTRL, KEY_TAB, KEY_ESC, NULL, // COL 0
NA, NA, KEY_DELETE, KEY_INSERT, NULL, NULL, // COL 1
KEY_FN, NULL, KEY_LEFT, KEY_HOME, KEY_PRINTSCREEN, NULL, // COL 2
KEY_LEFT_ALT, NULL, KEY_DOWN, KEY_UP, KEY_SCROLL_LOCK, NULL, // COL 3
NULL, NULL, KEY_RIGHT, KEY_END, KEY_PAUSE, NULL, // COL 4
NULL, NULL, KEY_PAGE_DOWN, KEY_PAGE_UP, NULL, NULL, // COL 5
NULL, NULL, NULL, NULL, NULL, NULL, // COL 6
KEY_SPACE, NULL, NULL, NULL, NULL, NULL, // COL 7
NULL, KEYPAD_0, KEYPAD_PERIOD, KEY_NUM_LOCK, NULL, NULL, // COL 8
NULL, KEYPAD_1, KEYPAD_4, KEYPAD_7, KEYPAD_PLUS, NULL, // COL 9
KEY_RIGHT_ALT, KEYPAD_2, KEYPAD_5, KEYPAD_8, KEYPAD_MINUS, NULL, // COL 10
KEY_FN, KEYPAD_3, KEYPAD_6, KEYPAD_9, KEYPAD_ASTERIX, NULL, // COL 11
NA, NA, NULL, NULL, KEYPAD_SLASH, NULL, // COL 12
KEY_RIGHT_CTRL, KEY_RIGHT_SHIFT, KEY_ENTER, KEY_BACKSPACE, KEY_DELETE, NULL // COL 13
};
const uint8_t *layout = layer1;
uint8_t *const row_port[6] = { _PINB, _PINB, _PINB, _PINB, _PINB, _PINB};
const uint8_t row_bit[6] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20};
uint8_t *const col_port[14] = {_PORTD, _PORTD, _PORTD, _PORTD, _PORTD, _PORTC, _PORTD, _PORTD, _PORTF, _PORTF, _PORTF, _PORTF, _PORTF, _PORTF};
const uint8_t col_bit[14] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x40, 0x40, 0x80, 0x01, 0x02, 0x10, 0x20, 0x40, 0x80};
bool pressed[84];
uint8_t queue[7] = {0,0,0,0,0,0,0};
uint8_t mod_keys = 0;
void init(void);
void send(void);
void poll(void);
void key_press(uint8_t key_id);
void key_release(uint8_t key_id);
int main(void) {
init();
for(;;) poll();
}
void send(void) {
uint8_t i;
if(pressed[FN_KEY1_ID] || pressed[FN_KEY2_ID])
layout = layer2;
else
layout = layer1;
for(i=0; i<6; i++)
keyboard_keys[i] = layout[queue[i]];
keyboard_modifier_keys = mod_keys;
usb_keyboard_send();
}
void poll() {
uint8_t row, col, key_id;
_delay_ms(5);
for(col=0; col<14; col++) {
*col_port[col] &= ~col_bit[col];
_delay_us(1);
for(row=0; row<6; row++) {
key_id = col*6+row;
if(!(*row_port[row] & row_bit[row])) {
if(!pressed[key_id])
key_press(key_id);
}
else if(pressed[key_id])
key_release(key_id);
}
*col_port[col] |= col_bit[col];
}
}
void key_press(uint8_t key_id) {
uint8_t i;
pressed[key_id] = true;
if(is_modifier[key_id])
mod_keys |= layer1[key_id];
else {
for(i=5; i>0; i--) queue[i] = queue[i-1];
queue[0] = key_id;
}
send();
}
void key_release(uint8_t key_id) {
uint8_t i;
pressed[key_id] = false;
if(is_modifier[key_id])
mod_keys &= ~layout[key_id];
else {
for(i=0; i<6; i++) if(queue[i]==key_id) break;
for(; i<6; i++) queue[i] = queue[i+1];
}
send();
}
void init(void) {
uint8_t i;
CLKPR = 0x80; CLKPR = 0;
usb_init();
while(!usb_configured());
_delay_ms(1000);
// PORTB is set as input with pull-up resistors
// PORTC,D,F are set to high output
DDRB = 0x00; DDRC = 0xFF; DDRD = 0xFF; DDRF = 0xFF;
PORTB = 0xFF; PORTC = 0xFF; PORTD = 0xFF; PORTF = 0xFF;
for(i=0; i<84; i++) pressed[i] = false;
}