Acorn Electron keyboard scanner, turns an old Acorn Electron into a USB keyboard.

Dependencies:   USBDevice mbed

Committer:
IH
Date:
Wed Jun 18 07:42:47 2014 +0000
Revision:
2:9352b1232e6d
Parent:
1:84cd616cc684
Head comment added about keyboard layouts

Who changed what in which revision?

UserRevisionLine numberNew contents of line
IH 2:9352b1232e6d 1 /*
IH 2:9352b1232e6d 2 * This is a simple mbed program for driving
IH 2:9352b1232e6d 3 * an Acorn Electron keyboard from a KL25Z mbed
IH 2:9352b1232e6d 4 * board.
IH 2:9352b1232e6d 5 *
IH 2:9352b1232e6d 6 * It is placed in the public domain by its author,
IH 2:9352b1232e6d 7 * Ian Harvey. Note that there is NO WARRANTY.
IH 2:9352b1232e6d 8 */
IH 2:9352b1232e6d 9
IH 2:9352b1232e6d 10 /* About the Electron keyboard -----------------
IH 2:9352b1232e6d 11
IH 2:9352b1232e6d 12 The keyboard itself has 14 'columns' and 4 'rows'.
IH 2:9352b1232e6d 13 The column being scanned is driven low and four row
IH 2:9352b1232e6d 14 bits are read out. The keyboard has diodes on the columns
IH 2:9352b1232e6d 15 and pull-ups on the rows, so there's no need to tri-state
IH 2:9352b1232e6d 16 un-driven columns or activate pullups. The break key
IH 2:9352b1232e6d 17 is wired separately and connects directly to ground
IH 2:9352b1232e6d 18 when preseed. Connections are as follows:
IH 2:9352b1232e6d 19
IH 2:9352b1232e6d 20 1. Break key PTE5
IH 2:9352b1232e6d 21 2. Caps lock LED PTE4
IH 2:9352b1232e6d 22 3. Ground
IH 2:9352b1232e6d 23 4. V+ for LED
IH 2:9352b1232e6d 24 5. Row 3 PTE3
IH 2:9352b1232e6d 25 6. Row 2 PTE2
IH 2:9352b1232e6d 26 7. Row 1 PTB11
IH 2:9352b1232e6d 27 8. Row 0 PTB10
IH 2:9352b1232e6d 28 9. Col 13 PTA1
IH 2:9352b1232e6d 29 10. Col 12 PTC7
IH 2:9352b1232e6d 30 11. Col 11 PTA2
IH 2:9352b1232e6d 31 12. Col 10 PTC0
IH 2:9352b1232e6d 32 13. Col 9 PTD4
IH 2:9352b1232e6d 33 14. Col 8 PTC3
IH 2:9352b1232e6d 34 15. Col 7 PTA12
IH 2:9352b1232e6d 35 16. Col 6 PTC4
IH 2:9352b1232e6d 36 17. Col 5 PTA4
IH 2:9352b1232e6d 37 18. Col 4 PTC5
IH 2:9352b1232e6d 38 19. Col 3 PTA5
IH 2:9352b1232e6d 39 20. Col 2 PTC6
IH 2:9352b1232e6d 40 21. Col 1 PTC8
IH 2:9352b1232e6d 41 22. Col 0 PTC10
IH 2:9352b1232e6d 42
IH 2:9352b1232e6d 43 Pin "1" (my numbering) is closest to the corner
IH 2:9352b1232e6d 44 of the board, and pin "22" is next to the space
IH 2:9352b1232e6d 45 bar. The connections of the keys follow the
IH 2:9352b1232e6d 46 layout in 'hid_keys' below, so KEY_PERIOD is
IH 2:9352b1232e6d 47 col 0 row 0, KEY_L is col 0 row 1, and so on.
IH 2:9352b1232e6d 48 We're using the following modifications:
IH 2:9352b1232e6d 49
IH 2:9352b1232e6d 50 "CAPS LK" -> KEY_TAB
IH 2:9352b1232e6d 51 "[ ] COPY" -> KEY_OPEN_SQUARE
IH 2:9352b1232e6d 52 "DELETE" -> KEY_LEFT_ALT
IH 2:9352b1232e6d 53 ": *" -> KEY_SINGLE_QUOTE
IH 2:9352b1232e6d 54
IH 2:9352b1232e6d 55 The firmware also treats the 'break' key as
IH 2:9352b1232e6d 56 'row 4' all on its own (mapped to KEY_BACKSPACE).
IH 2:9352b1232e6d 57
IH 2:9352b1232e6d 58 When 'DELETE' (the LEFT_ALT key) is pressed,
IH 2:9352b1232e6d 59 the keyboard map in 'alt_keys' is used, so
IH 2:9352b1232e6d 60 e.g. KEY_MINUS becomes KEY_EQUALS.
IH 2:9352b1232e6d 61
IH 2:9352b1232e6d 62 Keyboard layout
IH 2:9352b1232e6d 63 ---------------
IH 2:9352b1232e6d 64
IH 2:9352b1232e6d 65 Note that the keycaps on the Electron keyboard
IH 2:9352b1232e6d 66 are considerably different to those on a regular
IH 2:9352b1232e6d 67 PC-style one: shift-6 is labelled '&' not '^',
IH 2:9352b1232e6d 68 shift-';' is labelled '+' not ':', and so on.
IH 2:9352b1232e6d 69 The code here *doesn't* change which key code
IH 2:9352b1232e6d 70 is sent when shift is pressed, so the characters
IH 2:9352b1232e6d 71 you get are those you'd get on a PC keyboard -
IH 2:9352b1232e6d 72 after some experimentation I personally preferred
IH 2:9352b1232e6d 73 this. If this isn't to your taste, creating a
IH 2:9352b1232e6d 74 custom keyboard layout on the PC/Pi may be the best
IH 2:9352b1232e6d 75 way to fix this.
IH 2:9352b1232e6d 76
IH 2:9352b1232e6d 77 I tend to use a 'US' keyboard layout with this
IH 2:9352b1232e6d 78 keyboard, so shift-3 generates a '#', not a
IH 2:9352b1232e6d 79 UK pound sign, and the single/double quotes are
IH 2:9352b1232e6d 80 next to the ';' key (labelled ': *' on the Electron).
IH 2:9352b1232e6d 81
IH 2:9352b1232e6d 82 ---------------------------------------------- */
IH 2:9352b1232e6d 83
IH 0:9fd3dad2dc25 84 #include "mbed.h"
IH 0:9fd3dad2dc25 85 #include "USBKeyboard.h"
IH 0:9fd3dad2dc25 86 #include "hid_keys.h"
IH 0:9fd3dad2dc25 87
IH 0:9fd3dad2dc25 88 #define MAX_ROWS 5
IH 0:9fd3dad2dc25 89 #define MAX_COLS 14
IH 0:9fd3dad2dc25 90 #define REPORT_LEN 9
IH 0:9fd3dad2dc25 91 #define REPORT_ID_KEYBOARD 1
IH 0:9fd3dad2dc25 92
IH 0:9fd3dad2dc25 93 const uint8_t hid_keys[MAX_ROWS * MAX_COLS] =
IH 0:9fd3dad2dc25 94 {
IH 0:9fd3dad2dc25 95 KEY_PERIOD, KEY_L, KEY_O, KEY_9, KEY_BACKSPACE,
IH 0:9fd3dad2dc25 96 KEY_SLASH, KEY_SEMICOLON, KEY_P, KEY_0, KEY_NONE,
IH 1:84cd616cc684 97 KEY_NONE, KEY_SINGLE_QUOTE,KEY_UP_ARROW, KEY_MINUS, KEY_NONE,
IH 1:84cd616cc684 98 KEY_LEFT_ALT, KEY_ENTER, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_NONE,
IH 1:84cd616cc684 99
IH 0:9fd3dad2dc25 100 KEY_SPACE, KEY_NONE, KEY_OPEN_SQUARE, KEY_RIGHT_ARROW, KEY_NONE,
IH 0:9fd3dad2dc25 101 KEY_COMMA, KEY_K, KEY_I, KEY_8, KEY_NONE,
IH 0:9fd3dad2dc25 102 KEY_M, KEY_J, KEY_U, KEY_7, KEY_NONE,
IH 0:9fd3dad2dc25 103 KEY_N, KEY_H, KEY_Y, KEY_6, KEY_NONE,
IH 1:84cd616cc684 104
IH 0:9fd3dad2dc25 105 KEY_B, KEY_G, KEY_T, KEY_5, KEY_NONE,
IH 0:9fd3dad2dc25 106 KEY_V, KEY_F, KEY_R, KEY_4, KEY_NONE,
IH 0:9fd3dad2dc25 107 KEY_C, KEY_D, KEY_E, KEY_3, KEY_NONE,
IH 0:9fd3dad2dc25 108 KEY_X, KEY_S, KEY_W, KEY_2, KEY_NONE,
IH 1:84cd616cc684 109
IH 0:9fd3dad2dc25 110 KEY_Z, KEY_A, KEY_Q, KEY_1, KEY_NONE,
IH 0:9fd3dad2dc25 111 KEY_LEFT_SHIFT, KEY_LEFT_CTRL, KEY_TAB, KEY_ESC, KEY_NONE,
IH 0:9fd3dad2dc25 112 };
IH 0:9fd3dad2dc25 113
IH 1:84cd616cc684 114 const uint8_t alt_keys[MAX_ROWS * MAX_COLS] =
IH 1:84cd616cc684 115 {
IH 1:84cd616cc684 116 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 117 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 118 KEY_NONE, KEY_NONE, KEY_HASH_TILDE, KEY_EQUALS, KEY_NONE,
IH 1:84cd616cc684 119 KEY_NONE, KEY_NONE, KEY_NONE, KEY_BACKTICK_TILDE, KEY_NONE,
IH 1:84cd616cc684 120
IH 1:84cd616cc684 121 KEY_NONE, KEY_NONE, KEY_CLOSE_SQUARE, KEY_BACKSLASH, KEY_NONE,
IH 1:84cd616cc684 122 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 123 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 124 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 125
IH 1:84cd616cc684 126 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 127 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 128 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 129 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 130
IH 1:84cd616cc684 131 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 132 KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE,
IH 1:84cd616cc684 133
IH 1:84cd616cc684 134 };
IH 1:84cd616cc684 135
IH 0:9fd3dad2dc25 136
IH 0:9fd3dad2dc25 137 BusOut leds(LED1, LED2, LED3);
IH 0:9fd3dad2dc25 138
IH 0:9fd3dad2dc25 139 BusOut scanCols(
IH 0:9fd3dad2dc25 140 PTC10,PTC8,
IH 0:9fd3dad2dc25 141 PTC6, PTA5,
IH 0:9fd3dad2dc25 142 PTC5, PTA4,
IH 0:9fd3dad2dc25 143 PTC4, PTA12,
IH 0:9fd3dad2dc25 144 PTC3, PTD4,
IH 0:9fd3dad2dc25 145 PTC0, PTA2,
IH 0:9fd3dad2dc25 146 PTC7, PTA1
IH 0:9fd3dad2dc25 147 );
IH 0:9fd3dad2dc25 148
IH 0:9fd3dad2dc25 149 BusIn inRows(
IH 0:9fd3dad2dc25 150 PTB10,
IH 0:9fd3dad2dc25 151 PTB11,
IH 0:9fd3dad2dc25 152 PTE2,
IH 0:9fd3dad2dc25 153 PTE3,
IH 0:9fd3dad2dc25 154 PTE5 );
IH 0:9fd3dad2dc25 155
IH 0:9fd3dad2dc25 156 DigitalOut extLed(PTE4);
IH 0:9fd3dad2dc25 157
IH 0:9fd3dad2dc25 158 USBKeyboard kbd;
IH 0:9fd3dad2dc25 159
IH 0:9fd3dad2dc25 160 static int scanColumn(int col)
IH 0:9fd3dad2dc25 161 {
IH 0:9fd3dad2dc25 162 int rowBits;
IH 0:9fd3dad2dc25 163 // Drive output low to scan
IH 0:9fd3dad2dc25 164 scanCols.write(0x3FFF ^ (1 << col));
IH 0:9fd3dad2dc25 165 leds.write(col >> 1);
IH 1:84cd616cc684 166 wait(0.001);
IH 0:9fd3dad2dc25 167 rowBits = inRows.read();
IH 0:9fd3dad2dc25 168 scanCols.write(0x3FFF);
IH 0:9fd3dad2dc25 169 // Inputs also active-low
IH 0:9fd3dad2dc25 170 return rowBits ^ 0x1F;
IH 0:9fd3dad2dc25 171 }
IH 1:84cd616cc684 172
IH 1:84cd616cc684 173 static const uint8_t altKeys =
IH 1:84cd616cc684 174 MODIFIER_BIT(KEY_LEFT_ALT) | MODIFIER_BIT(KEY_RIGHT_ALT);
IH 1:84cd616cc684 175
IH 1:84cd616cc684 176
IH 0:9fd3dad2dc25 177 int main()
IH 0:9fd3dad2dc25 178 {
IH 0:9fd3dad2dc25 179 // Setup
IH 0:9fd3dad2dc25 180 inRows.mode(PullUp);
IH 0:9fd3dad2dc25 181 extLed = 1;
IH 0:9fd3dad2dc25 182
IH 0:9fd3dad2dc25 183 // Run loop
IH 0:9fd3dad2dc25 184 while(1)
IH 0:9fd3dad2dc25 185 {
IH 0:9fd3dad2dc25 186 int col, ocount;
IH 0:9fd3dad2dc25 187 HID_REPORT report;
IH 1:84cd616cc684 188 uint8_t keyIfAlt = KEY_NONE;
IH 0:9fd3dad2dc25 189
IH 0:9fd3dad2dc25 190 report.data[0] = REPORT_ID_KEYBOARD;
IH 0:9fd3dad2dc25 191 report.data[1] = 0; // modifiers
IH 0:9fd3dad2dc25 192 report.data[2] = 0;
IH 0:9fd3dad2dc25 193 ocount = 3;
IH 0:9fd3dad2dc25 194
IH 0:9fd3dad2dc25 195 for (col=0; col < MAX_COLS; col++)
IH 0:9fd3dad2dc25 196 {
IH 0:9fd3dad2dc25 197 int row;
IH 0:9fd3dad2dc25 198 int rowBits = scanColumn(col);
IH 0:9fd3dad2dc25 199 if ( !rowBits )
IH 0:9fd3dad2dc25 200 continue;
IH 0:9fd3dad2dc25 201
IH 0:9fd3dad2dc25 202 for (row=0; row < MAX_ROWS; row++)
IH 0:9fd3dad2dc25 203 {
IH 0:9fd3dad2dc25 204 if ( rowBits & (1 << row) )
IH 0:9fd3dad2dc25 205 {
IH 0:9fd3dad2dc25 206 uint8_t key = hid_keys[col * MAX_ROWS + row];
IH 0:9fd3dad2dc25 207 if ( IS_MODIFIER(key) )
IH 0:9fd3dad2dc25 208 report.data[1] |= MODIFIER_BIT(key);
IH 0:9fd3dad2dc25 209 else if ( key != KEY_NONE )
IH 0:9fd3dad2dc25 210 {
IH 0:9fd3dad2dc25 211 if ( ocount < REPORT_LEN )
IH 0:9fd3dad2dc25 212 report.data[ocount++] = key;
IH 0:9fd3dad2dc25 213 }
IH 1:84cd616cc684 214
IH 1:84cd616cc684 215 key = alt_keys[col * MAX_ROWS + row];
IH 1:84cd616cc684 216 if ( key != KEY_NONE )
IH 1:84cd616cc684 217 keyIfAlt = key;
IH 0:9fd3dad2dc25 218 //kbd.printf("c%dr%d ", col, row);
IH 0:9fd3dad2dc25 219 }
IH 0:9fd3dad2dc25 220 }
IH 0:9fd3dad2dc25 221 }
IH 0:9fd3dad2dc25 222
IH 1:84cd616cc684 223 if ( (report.data[1] & altKeys) != 0 &&
IH 1:84cd616cc684 224 keyIfAlt != KEY_NONE
IH 1:84cd616cc684 225 )
IH 1:84cd616cc684 226 {
IH 1:84cd616cc684 227 report.data[3] = keyIfAlt;
IH 1:84cd616cc684 228 ocount = 4; // Zero out the rest
IH 1:84cd616cc684 229 report.data[1] &= ~altKeys; // And put alt key up
IH 1:84cd616cc684 230 }
IH 1:84cd616cc684 231
IH 0:9fd3dad2dc25 232 while( ocount < REPORT_LEN )
IH 0:9fd3dad2dc25 233 report.data[ocount++] = KEY_NONE;
IH 0:9fd3dad2dc25 234
IH 0:9fd3dad2dc25 235 report.length = REPORT_LEN;
IH 0:9fd3dad2dc25 236 kbd.send(&report);
IH 0:9fd3dad2dc25 237 }
IH 0:9fd3dad2dc25 238 }