ADB to USB keyboard converter
8 files changed, 809 insertions(+), 0 deletions(-) A ADB.txt A adb.c A adb.h A adb/Makefile A adb/README A adb/config.h A adb/keymap.c A adb/matrix.c
A ADB.txt => ADB.txt +134 -0
@@ 0,0 1,134 @@ ADB Protocol ============ Resources --------- Apple IIgs Hardware Reference Second Edition [p80(Chapter6 p121)] ftp://ftp.apple.asimov.net/pub/apple_II/documentation/Apple%20IIgs%20Hardware%20Reference.pdf ADB Keycode http://72.0.193.250/Documentation/macppc/adbkeycodes/ http://m0115.web.fc2.com/m0115.jpg ADB Signaling http://kbdbabel.sourceforge.net/doc/kbd_signaling_pcxt_ps2_adb.pdf ADB Overview & History http://en.wikipedia.org/wiki/Apple_Desktop_Bus Microchip Application Note: ADB device(with code for PIC16C) http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en011062 AVR ATtiny2131 ADB to PS/2 converter(Japanese) http://hp.vector.co.jp/authors/VA000177/html/KeyBoardA5DEA5CBA5A2II.html Pinouts ------- Female socket from the front 4o o3 2o o1 == 1: Data 2: Power SW(low when press Power key) 3: Vcc(5V) 4: GND Commands -------- ADB command is 1byte and consists of 4bit-address, 2bit-command type and 2bit-register. The commands are always sent by Host. Command format: 7 6 5 4 3 2 1 0 | | | |------------ address | |-------- command type | |---- register bits commands ------------------------------------------------------ - - - - 0 0 0 0 Send Request(reset all devices) A A A A 0 0 0 1 Flush(reset a device) - - - - 0 0 1 0 Reserved - - - - 0 0 1 1 Reserved - - - - 0 1 - - Reserved A A A A 1 0 R R Listen(write to a device) A A A A 1 1 R R Talk(read from a device) The command to read keycodes from keyboard is 0x2C which consist of keyboard address 2 and Talk against register 0. Address: 2: keyboard 3: mice Registers: 0: application(keyobard/mice use to store its data.) 1: application 2: application 3: status and command Communication ------------- This is a minimum information for keyboard communication. See "Resources" for detail. Signaling: ~~~~____________~~||||||||||||__~~~~~_~~|||||||||||||||__~~~~ |800us | |7 Command 0| | | |15-64 Data 0|Stopbit(0) +Attention | | | +Startbit(1) +Startbit(1) | +Tlt(140-260us) +stopbit(0) Bit cells: bit0: ______~~~ 65 :35us bit1: ___~~~~~~ 35 :65us bit0 low time: 60-70% of bit cell(42-91us) bit1 low time: 30-40% of bit cell(21-52us) bit cell time: 70-130us [from Apple IIgs Hardware Reference Second Edition] Criterion for bit0/1: After 55us if line is low/high then bit is 0/1. Attention & start bit: Host asserts low in 560-1040us then places start bit(1). Tlt(Stop to Start): Bus stays high in 140-260us then device places start bit(1). Global reset: Host asserts low in 2.8-5.2ms. All devices are forced to reset. Send request from device(Srq): Device can request to send at commad(Global only?) stop bit. keep low for 300us to request. Keyboard data(register0) This 16bit data can contains 2 keycodes and 2 released flags. First keycode is palced in upper nibble. When one keyocode is sent, lower nibble is 0xFF. Release flag is 1 when key is released. 15 14 . . . . . 8 7 6 . . . . . 0 | |keycode1 | |keycode2 |released(1) |released(1) Keycodes: Scancode consists of 7bit keycode and 1bit release flag. Device can send two keycodes at once. If just one keycode is sent keycode1 contains it and keyocode2 is 0xFF. Power switch: You can read the state from PSW line(active low) however the switch has a special scancode 0x7F7F, so you can also read from Data line. It uses 0xFFFF for release scancode. END_OF_ADB
A adb.c => adb.c +180 -0
@@ 0,0 1,180 @@ #include <stdbool.h> #include <util/delay.h> #include <avr/io.h> #include "adb.h" static inline void data_lo(void); static inline void data_hi(void); static inline bool data_in(void); #ifdef ADB_PSW_BIT static inline void psw_lo(void); static inline void psw_hi(void); static inline bool psw_in(void); #endif static inline void attention(void); static inline void place_bit0(void); static inline void place_bit1(void); static inline void send_byte(uint8_t data); static inline bool read_bit(void); static inline uint8_t read_byte(void); static inline uint8_t wait_data_lo(uint8_t us); static inline uint8_t wait_data_hi(uint8_t us); void adb_host_init(void) { data_hi(); #ifdef ADB_PSW_BIT psw_hi(); #endif } #ifdef ADB_PSW_BIT bool adb_host_psw(void) { return psw_in(); } #endif uint16_t adb_host_kbd_recv(void) { uint16_t data = 0; attention(); send_byte(0x2C); // Addr:2, Cmd:talk(11), Reg:0(00) place_bit0(); // Stopbit if (!wait_data_lo(0xFF)) // Stop to Start(140-260us) return 0; // No data to send if (!read_bit()) // Startbit(1) return -2; data = read_byte(); data = (data<<8) | read_byte(); if (read_bit()) // Stopbit(0) return -3; return data; } static inline void data_lo() { ADB_DDR |= (1<<ADB_DATA_BIT); ADB_PORT &= ~(1<<ADB_DATA_BIT); } static inline void data_hi() { ADB_PORT |= (1<<ADB_DATA_BIT); ADB_DDR &= ~(1<<ADB_DATA_BIT); } static inline bool data_in() { ADB_PORT |= (1<<ADB_DATA_BIT); ADB_DDR &= ~(1<<ADB_DATA_BIT); return ADB_PIN&(1<<ADB_DATA_BIT); } #ifdef ADB_PSW_BIT static inline void psw_lo() { ADB_DDR |= (1<<ADB_PSW_BIT); ADB_PORT &= ~(1<<ADB_PSW_BIT); } static inline void psw_hi() { ADB_PORT |= (1<<ADB_PSW_BIT); ADB_DDR &= ~(1<<ADB_PSW_BIT); } static inline bool psw_in() { ADB_PORT |= (1<<ADB_PSW_BIT); ADB_DDR &= ~(1<<ADB_PSW_BIT); return ADB_PIN&(1<<ADB_PSW_BIT); } #endif static inline void attention(void) { data_lo(); _delay_us(700); place_bit1(); } static inline void place_bit0(void) { data_lo(); _delay_us(65); data_hi(); _delay_us(35); } static inline void place_bit1(void) { data_lo(); _delay_us(35); data_hi(); _delay_us(65); } static inline void send_byte(uint8_t data) { for (int i = 0; i < 8; i++) { if (data&(0x80>>i)) place_bit1(); else place_bit0(); } } static inline bool read_bit(void) { // ADB Bit Cells // // bit0: ______~~~ // 65 :35us // // bit1: ___~~~~~~ // 35 :65us // // bit0 low time: 60-70% of bit cell(42-91us) // bit1 low time: 30-40% of bit cell(21-52us) // bit cell time: 70-130us // [from Apple IIgs Hardware Reference Second Edition] // // After 55us if data line is low/high then bit is 0/1. // Too simple to rely on? bool bit; wait_data_lo(75); // wait the beginning of bit cell _delay_us(55); bit = data_in(); wait_data_hi(36); // wait high part of bit cell return bit; } static inline uint8_t read_byte(void) { uint8_t data = 0; for (int i = 0; i < 8; i++) { data <<= 1; if (read_bit()) data = data | 1; } return data; } static inline uint8_t wait_data_lo(uint8_t us) { while (data_in() && us) { _delay_us(1); us--; } return us; } static inline uint8_t wait_data_hi(uint8_t us) { while (!data_in() && us) { _delay_us(1); us--; } return us; }
A adb.h => adb.h +18 -0
@@ 0,0 1,18 @@ #ifndef ADB_H #define ADB_H #include <stdbool.h> #if !(defined(ADB_PORT) && \ defined(ADB_PIN) && \ defined(ADB_DDR) && \ defined(ADB_DATA_BIT)) # error "ADB port setting is required in config.h" #endif // ADB host void adb_host_init(void); bool adb_host_psw(void); uint16_t adb_host_kbd_recv(void); #endif
A adb/Makefile => adb/Makefile +82 -0
@@ 0,0 1,82 @@ # Hey Emacs, this is a -*- makefile -*- #---------------------------------------------------------------------------- # WinAVR Makefile Template written by Eric B. Weddington, J�rg Wunsch, et al. # # Released to the Public Domain # # Additional material for this makefile was written by: # Peter Fleury # Tim Henigan # Colin O'Flynn # Reiner Patommel # Markus Pfaff # Sander Pool # Frederik Rouleau # Carlos Lamas # #---------------------------------------------------------------------------- # On command line: # # make all = Make software. # # make clean = Clean out built project files. # # make coff = Convert ELF to AVR COFF. # # make extcoff = Convert ELF to AVR Extended COFF. # # make program = Download the hex file to the device, using avrdude. # Please customize the avrdude settings below first! # # make debug = Start either simulavr or avarice as specified for debugging, # with avr-gdb or avr-insight as the front end for debugging. # # make filename.s = Just compile filename.c into the assembler code only. # # make filename.i = Create a preprocessed source file for use in submitting # bug reports to the GCC project. # # To rebuild project do "make clean" then "make all". #---------------------------------------------------------------------------- # Target file name (without extension). TARGET = tmk_adb # Directory common source filess exist COMMON_DIR = .. # Directory keyboard dependent files exist TARGET_DIR = . # keyboard dependent files TARGET_SRC = keymap.c \ matrix.c \ adb.c # MCU name, you MUST set this to match the board you are using # type "make clean" after changing this, so all files will be rebuilt #MCU = at90usb162 # Teensy 1.0 MCU = atmega32u4 # Teensy 2.0 #MCU = at90usb646 # Teensy++ 1.0 #MCU = at90usb1286 # Teensy++ 2.0 # Processor frequency. # Normally the first thing your program should do is set the clock prescaler, # so your program will run at the correct speed. You should also set this # variable to same clock speed. The _delay_ms() macro uses this, and many # examples use this variable to calculate timings. Do not add a "UL" here. F_CPU = 16000000 # Build Options # comment out to disable the options. # MOUSEKEY_ENABLE = yes # Mouse keys #PS2_MOUSE_ENABLE = yes # PS/2 mouse(TrackPoint) support USB_EXTRA_ENABLE = yes # Enhanced feature for Windows(Audio control and System control) #USB_NKRO_ENABLE = yes # USB Nkey Rollover include $(COMMON_DIR)/Makefile.common
A adb/README => adb/README +19 -0
@@ 0,0 1,19 @@ ADB to USB keyboard converter ============================= This firmware converts ADB keyboard protocol to USB. Build ----- 0. Connect ADB keyboard to Teensy by 3 lines(Vcc, GND, Data). PSW line is optional. See ADB.txt for details. 1. Define following macros for ADB connection in config.h: ADB_PORT ADB_PIN ADB_DDR ADB_DATA_BIT ADB_PSW_BIT 2. make 3. program Teensy. EOF
A adb/config.h => adb/config.h +47 -0
@@ 0,0 1,47 @@ #ifndef CONFIG_H #define CONFIG_H /* controller configuration */ #include "controller_teensy.h" #define VENDOR_ID 0xFEED #define PRODUCT_ID 0x0ADB #define MANUFACTURER t.m.k. #define PRODUCT ADB keyboard converter #define DESCRIPTION convert ADB keyboard to USB /* matrix size */ #define MATRIX_ROWS 16 // keycode bit: 3-0 #define MATRIX_COLS 8 // keycode bit: 6-4 /* define if matrix has ghost */ //#define MATRIX_HAS_GHOST /* USB NKey Rollover */ #ifdef USB_NKRO_ENABLE #endif /* mouse keys */ #ifdef MOUSEKEY_ENABLE # define MOUSEKEY_DELAY_TIME 192 #endif /* PS/2 mouse */ #ifdef PS2_MOUSE_ENABLE # define PS2_CLOCK_PORT PORTF # define PS2_CLOCK_PIN PINF # define PS2_CLOCK_DDR DDRF # define PS2_CLOCK_BIT 0 # define PS2_DATA_PORT PORTF # define PS2_DATA_PIN PINF # define PS2_DATA_DDR DDRF # define PS2_DATA_BIT 1 #endif /* ADB port setting */ #define ADB_PORT PORTF #define ADB_PIN PINF #define ADB_DDR DDRF #define ADB_DATA_BIT 0 //#define ADB_PSW_BIT 1 // optional #endif
A adb/keymap.c => adb/keymap.c +135 -0
@@ 0,0 1,135 @@ /* * Keymap for ADB keyboard */ #include <stdint.h> #include <stdbool.h> #include <avr/pgmspace.h> #include "usb_keyboard.h" #include "usb_keycodes.h" #include "print.h" #include "debug.h" #include "util.h" #include "keymap_skel.h" // Convert physical keyboard layout to matrix array. // This is a macro to define keymap easily in keyboard layout form. // TODO: ADB to USB default keymap // TODO: keymap macro template for m0116/m0115 /* Apple Keyboard m0116 K7F, \ K35, K12, K13, K14, K15, K17, K16, K1A, K1C, K19, K1D, K1B, K18, K33, K47, K51, K4B, K43, \ K30, K0C, K0D, K0E, K0F, K10, K11, K20, K22, K1F, K23, K21, K1E, K59, K5B, K5C, K4E, \ K36, K00, K01, K02, K03, K05, K04, K26, K28, K25, K29, K27, K24, K56, K57, K58, K45, \ K38, K06, K07, K08, K09, K0B, K2D, K2E, K2B, K2F, K2C, K7B, K53, K54, K55, \ K39, K3A, K37, K32, K31, K2A, K3B, K3C, K3D, K3E, K52, K41, K4C \ */ /* no tenkey K7F, \ K35, K12, K13, K14, K15, K17, K16, K1A, K1C, K19, K1D, K1B, K18, K33, \ K30, K0C, K0D, K0E, K0F, K10, K11, K20, K22, K1F, K23, K21, K1E, \ K36, K00, K01, K02, K03, K05, K04, K26, K28, K25, K29, K27, K24, \ K38, K06, K07, K08, K09, K0B, K2D, K2E, K2B, K2F, K2C, K7B, \ K39, K3A, K37, K32, K31, K2A, K3B, K3C, K3D, K3E \ */ #define KEYMAP( \ K7F, \ K35, K12, K13, K14, K15, K17, K16, K1A, K1C, K19, K1D, K1B, K18, K33, \ K30, K0C, K0D, K0E, K0F, K10, K11, K20, K22, K1F, K23, K21, K1E, \ K36, K00, K01, K02, K03, K05, K04, K26, K28, K25, K29, K27, K24, \ K38, K06, K07, K08, K09, K0B, K2D, K2E, K2B, K2F, K2C, K7B, \ K39, K3A, K37, K32, K31, K2A, K3B, K3C, K3D, K3E \ ) { \ { K00, K01, K02, K03, K04, K05, K06, K07 }, \ { K08, K09, 000, K0B, K0C, K0D, K0E, K0F }, \ { K10, K11, K12, K13, K14, K15, K16, K17 }, \ { K18, K19, K1A, K1B, K1C, K1D, K1E, K1F }, \ { K20, K21, K22, K23, K24, K25, K26, K27 }, \ { K28, K29, K2A, K2B, K2C, K2D, K2E, K2F }, \ { K30, K31, K32, K33, 000, K35, K36, K37 }, \ { K38, K39, K3A, K3B, K3C, K3D, K3E, 000 }, \ { 000, 000, 000, 000, 000, 000, 000, 000 }, \ { 000, 000, 000, 000, 000, 000, 000, 000 }, \ { 000, 000, 000, 000, 000, 000, 000, 000 }, \ { 000, 000, 000, 000, 000, 000, 000, 000 }, \ { 000, 000, 000, 000, 000, 000, 000, 000 }, \ { 000, 000, 000, 000, 000, 000, 000, 000 }, \ { 000, 000, 000, 000, 000, 000, 000, 000 }, \ { 000, 000, 000, K7B, 000, 000, 000, K7F }, \ } #define KEYCODE(layer, row, col) (pgm_read_byte(&keymaps[(layer)][(row)][(col)])) // Assign Fn key(0-7) to a layer to which switch with the Fn key pressed. static const uint8_t PROGMEM fn_layer[] = { 0, // FN_0 0, // FN_1 0, // FN_2 0, // FN_3 0, // FN_4 0, // FN_5 0, // FN_6 0 // FN_7 }; // Assign Fn key(0-7) to a keycode sent when release Fn key without use of the layer. // See layer.c for details. static const uint8_t PROGMEM fn_keycode[] = { KB_NO, // FN_0 KB_NO, // FN_1 KB_NO, // FN_2 KB_NO, // FN_3 KB_NO, // FN_4 KB_NO, // FN_5 KB_NO, // FN_6 KB_NO // FN_7 }; static const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { /* Layer 0: Default Layer * ,---------------------------------------------------------. * |Esc| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| =|Backs| * |---------------------------------------------------------| * |Tab | Q| W| E| R| T| Y| U| I| O| P| [| ]| | * |-----------------------------------------------------' | * |Contro| A| S| D| F| G| H| J| K| L|Fn3| '|Return| * |---------------------------------------------------------| * |Shift | Z| X| C| V| B| N| M| ,| .| /|Shift | * |---------------------------------------------------------| * |Shi|Alt|Gui l `| | \|Lef|Rig|Dow|Up | * `---------------------------------------------------------' */ KEYMAP(KB_PWR, \ KB_ESC, KB_1, KB_2, KB_3, KB_4, KB_5, KB_6, KB_7, KB_8, KB_9, KB_0, KB_MINS,KB_EQL, KB_BSPC, \ KB_TAB, KB_Q, KB_W, KB_E, KB_R, KB_T, KB_Y, KB_U, KB_I, KB_O, KB_P, KB_LBRC,KB_RBRC, \ KB_LCTL,KB_A, KB_S, KB_D, KB_F, KB_G, KB_H, KB_J, KB_K, KB_L, KB_SCLN,KB_QUOT,KB_ENT, \ KB_LSFT,KB_Z, KB_X, KB_C, KB_V, KB_B, KB_N, KB_M, KB_COMM,KB_DOT, KB_SLSH,KB_RSFT, \ KB_LSFT,KB_LALT,KB_LGUI,KB_GRV, KB_SPC, KB_BSLS,KB_LEFT,KB_RGHT,KB_DOWN,KB_UP), }; uint8_t keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t col) { return KEYCODE(layer, row, col); } uint8_t keymap_fn_layer(uint8_t fn_bits) { return pgm_read_byte(&fn_layer[biton(fn_bits)]); } uint8_t keymap_fn_keycode(uint8_t fn_bits) { return pgm_read_byte(&fn_keycode[(biton(fn_bits))]); } // define a condition to enter special function mode bool keymap_is_special_mode(uint8_t fn_bits) { //return (usb_keyboard_mods == (BIT_LCTRL | BIT_LSHIFT | BIT_LALT | BIT_LGUI)); return (usb_keyboard_mods == (BIT_RSHIFT)); }
A adb/matrix.c => adb/matrix.c +194 -0
@@ 0,0 1,194 @@ /* * scan matrix */ #include <stdint.h> #include <stdbool.h> #include <avr/io.h> #include <util/delay.h> #include "print.h" #include "util.h" #include "debug.h" #include "adb.h" #include "matrix_skel.h" #if (MATRIX_COLS > 16) # error "MATRIX_COLS must not exceed 16" #endif #if (MATRIX_ROWS > 255) # error "MATRIX_ROWS must not exceed 255" #endif static bool _matrix_is_modified = false; // matrix state buffer(1:on, 0:off) #if (MATRIX_COLS <= 8) static uint8_t *matrix; static uint8_t _matrix0[MATRIX_ROWS]; #else static uint16_t *matrix; static uint16_t _matrix0[MATRIX_ROWS]; #endif #ifdef MATRIX_HAS_GHOST static bool matrix_has_ghost_in_row(uint8_t row); #endif static void _register_key(uint8_t key); inline uint8_t matrix_rows(void) { return MATRIX_ROWS; } inline uint8_t matrix_cols(void) { return MATRIX_COLS; } void matrix_init(void) { adb_host_init(); // initialize matrix state: all keys off for (uint8_t i=0; i < MATRIX_ROWS; i++) _matrix0[i] = 0x00; matrix = _matrix0; print_enable = true; debug_enable = true; debug_matrix = true; debug_keyboard = true; debug_mouse = true; print("debug enabled.\n"); return; } uint8_t matrix_scan(void) { uint16_t codes; uint8_t key0, key1; _matrix_is_modified = false; codes = adb_host_kbd_recv(); key0 = codes>>8; key1 = codes&0xFF; if (debug_matrix) { //print("adb_host_kbd_recv: "); phex16(codes); print("\n"); } if (codes == 0) { // no keys return 0; } else if (key0 == 0xFF && key1 != 0xFF) { // error return codes&0xFF; } else { _matrix_is_modified = true; _register_key(key0); if (key1 != 0xFF) // key1 is 0xFF when no second key. _register_key(key1); } return 1; } bool matrix_is_modified(void) { return _matrix_is_modified; } inline bool matrix_has_ghost(void) { #ifdef MATRIX_HAS_GHOST for (uint8_t i = 0; i < MATRIX_ROWS; i++) { if (matrix_has_ghost_in_row(i)) return true; } #endif return false; } inline bool matrix_is_on(uint8_t row, uint8_t col) { return (matrix[row] & (1<<col)); } inline #if (MATRIX_COLS <= 8) uint8_t matrix_get_row(uint8_t row) #else uint16_t matrix_get_row(uint8_t row) #endif { return matrix[row]; } void matrix_print(void) { #if (MATRIX_COLS <= 8) print("\nr/c 01234567\n"); #else print("\nr/c 0123456789ABCDEF\n"); #endif for (uint8_t row = 0; row < matrix_rows(); row++) { phex(row); print(": "); #if (MATRIX_COLS <= 8) pbin_reverse(matrix_get_row(row)); #else pbin_reverse16(matrix_get_row(row)); #endif #ifdef MATRIX_HAS_GHOST if (matrix_has_ghost_in_row(row)) { print(" <ghost"); } #endif print("\n"); } } uint8_t matrix_key_count(void) { uint8_t count = 0; for (uint8_t i = 0; i < MATRIX_ROWS; i++) { #if (MATRIX_COLS <= 8) count += bitpop(matrix[i]); #else count += bitpop16(matrix[i]); #endif } return count; } #ifdef MATRIX_HAS_GHOST inline static bool matrix_has_ghost_in_row(uint8_t row) { // no ghost exists in case less than 2 keys on if (((matrix[row] - 1) & matrix[row]) == 0) return false; // ghost exists in case same state as other row for (uint8_t i=0; i < MATRIX_ROWS; i++) { if (i != row && (matrix[i] & matrix[row]) == matrix[row]) return true; } return false; } #endif inline static void _register_key(uint8_t key) { uint8_t col, row; col = key&0x07; row = (key>>3)&0x0F; if (key&0x80) { matrix[row] &= ~(1<<col); } else { matrix[row] |= (1<<col); } }