Update | Update | ||||
------ | ------ | ||||
2015/05/05 Added keymaps for 107-key, 77-key and 50-key. Thanks, orihalcon @ geekhack! | 2015/05/05 Added keymaps for 107-key, 77-key and 50-key. Thanks, orihalcon @ geekhack! | ||||
2015/05/19 Fixed a protocol handling bug. | |||||
#define IBM4704_DATA_DDR DDRD | #define IBM4704_DATA_DDR DDRD | ||||
#define IBM4704_DATA_BIT 0 | #define IBM4704_DATA_BIT 0 | ||||
/* Pin interrupt on rising edge */ | |||||
#define IBM4704_INT_INIT() do { EICRA |= ((1<<ISC11)|(0<<ISC10)); } while (0) | |||||
#define IBM4704_INT_ON() do { EIMSK |= (1<<INT1); } while (0) | |||||
/* Pin interrupt on rising edge of clock */ | |||||
#define IBM4704_INT_INIT() do { EICRA |= ((1<<ISC11)|(1<<ISC10)); } while (0) | |||||
#define IBM4704_INT_ON() do { EIFR |= (1<<INTF1); EIMSK |= (1<<INT1); } while (0) | |||||
#define IBM4704_INT_OFF() do { EIMSK &= ~(1<<INT1); } while (0) | #define IBM4704_INT_OFF() do { EIMSK &= ~(1<<INT1); } while (0) | ||||
#define IBM4704_INT_VECT INT1_vect | #define IBM4704_INT_VECT INT1_vect | ||||
---------------- | ---------------- | ||||
Data bits are LSB first and Pairty is odd. Clock has around 60us high and 30us low part. | Data bits are LSB first and Pairty is odd. Clock has around 60us high and 30us low part. | ||||
____ __ __ __ __ __ __ __ __ __ ________ | |||||
Clock \____/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ | |||||
____ __ __ __ __ __ __ __ __ __ _______ | |||||
Clock \_____/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ | |||||
____ ____ ____ ____ ____ ____ ____ ____ ____ ____ | ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ | ||||
Data ____/ X____X____X____X____X____X____X____X____X____X________ | Data ____/ X____X____X____X____X____X____X____X____X____X________ | ||||
Start 0 1 2 3 4 5 6 7 P Stop | Start 0 1 2 3 4 5 6 7 P Stop | ||||
Start bit: can be long as 300-350us. | Start bit: can be long as 300-350us. | ||||
Inhibit: Pull Data line down to inhibit keyboard to send. | Inhibit: Pull Data line down to inhibit keyboard to send. | ||||
Timing: Host reads bit while Clock is hi. | |||||
Timing: Host reads bit while Clock is hi.(rising edge) | |||||
Stop bit: Keyboard pulls down Data line to lo after 9th clock. | Stop bit: Keyboard pulls down Data line to lo after 9th clock. | ||||
| `-----`--- scan code | | `-----`--- scan code | ||||
`------------- enable bit(0: enable repeat, 1: enable break) | `------------- enable bit(0: enable repeat, 1: enable break) | ||||
00-77 Enable repeat(78-7F: invalid scancode) | |||||
80-F7 Enable break(F8-FF: invalid scancode) | |||||
00-79 Enable repeat | |||||
80-F9 Enable break(FA-FF are used as other commands, see above.) | |||||
FE Resend(011ah) no need to use | FE Resend(011ah) no need to use | ||||
FF End(0114h) exits FC command mode. | FF End(0114h) exits FC command mode. | ||||
Response from keyboard: | Response from keyboard: | ||||
FD Out of bound - Invalid scancode | FD Out of bound - Invalid scancode | ||||
-- OK - No response means that command is accepted. | |||||
Examples: | Examples: | ||||
To enable break code of all keys. | To enable break code of all keys. |
static void enable_break(void) | static void enable_break(void) | ||||
{ | { | ||||
uint8_t ret; | |||||
print("Enable break: "); | print("Enable break: "); | ||||
// valid scancode: 00-79h | // valid scancode: 00-79h | ||||
for (uint8_t code = 0; code < 0x7A; code++) { | for (uint8_t code = 0; code < 0x7A; code++) { | ||||
while (ibm4704_send(0x80|code)) _delay_ms(1); | |||||
// get none when ok, get FD when out of bound | |||||
_delay_ms(5); | |||||
if ((ret = ibm4704_recv()) != 0xff) { | |||||
xprintf("c%02X:r%02X ", code, ret); | |||||
} | |||||
_delay_ms(1); | |||||
while (ibm4704_send(0x80|code)) _delay_ms(10); | |||||
_delay_ms(5); // wait for response | |||||
// No response(FF) when ok, FD when out of bound | |||||
xprintf("s%02X:r%02X ", code, ibm4704_recv()); | |||||
} | } | ||||
_delay_us(1000); | |||||
while (ibm4704_send(0xFF)) { _delay_ms(1); } // End | |||||
while (ibm4704_send(0xFF)) { _delay_ms(10); } // End | |||||
print("End\n"); | print("End\n"); | ||||
} | } | ||||
void matrix_init(void) | |||||
{ | |||||
debug_enable = true; | |||||
void matrix_setup(void) | |||||
{ | |||||
ibm4704_init(); | ibm4704_init(); | ||||
matrix_clear(); | |||||
} | |||||
_delay_ms(2000); // wait for starting up debug console | |||||
void matrix_init(void) | |||||
{ | |||||
debug_enable = true; | |||||
print("IBM 4704 converter\n"); | print("IBM 4704 converter\n"); | ||||
while (ibm4704_send(0xFE)) _delay_ms(1); // resend | |||||
_delay_ms(5); | |||||
matrix_clear(); | |||||
_delay_ms(2000); // wait for keyboard starting up | |||||
xprintf("Keyboard ID: %02X\n", ibm4704_recv()); | xprintf("Keyboard ID: %02X\n", ibm4704_recv()); | ||||
enable_break(); | enable_break(); | ||||
} | } |
power_down(WDTO_15MS); | power_down(WDTO_15MS); | ||||
} | } | ||||
__attribute__ ((weak)) void matrix_power_up(void) {} | |||||
__attribute__ ((weak)) void matrix_power_down(void) {} | |||||
bool suspend_wakeup_condition(void) | bool suspend_wakeup_condition(void) | ||||
{ | { | ||||
matrix_power_up(); | matrix_power_up(); |
#endif | #endif | ||||
__attribute__ ((weak)) void matrix_setup(void) {} | |||||
void keyboard_setup(void) | |||||
{ | |||||
matrix_setup(); | |||||
} | |||||
void keyboard_init(void) | void keyboard_init(void) | ||||
{ | { | ||||
timer_init(); | timer_init(); |
} | } | ||||
/* it runs once at early stage of startup before keyboard_init. */ | |||||
void keyboard_setup(void); | |||||
/* it runs once after initializing host side protocol, debug and MCU peripherals. */ | |||||
void keyboard_init(void); | void keyboard_init(void); | ||||
/* it runs repeatedly in main loop */ | |||||
void keyboard_task(void); | void keyboard_task(void); | ||||
/* it runs when host LED status is updated */ | |||||
void keyboard_set_leds(uint8_t leds); | void keyboard_set_leds(uint8_t leds); | ||||
__attribute__ ((weak)) void matrix_power_up(void) {} | |||||
__attribute__ ((weak)) void matrix_power_down(void) {} | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} | } | ||||
#endif | #endif |
uint8_t matrix_rows(void); | uint8_t matrix_rows(void); | ||||
/* number of matrix columns */ | /* number of matrix columns */ | ||||
uint8_t matrix_cols(void); | uint8_t matrix_cols(void); | ||||
/* intialize matrix for scaning. should be called once. */ | |||||
/* should be called at early stage of startup before matrix_init.(optional) */ | |||||
void matrix_setup(void); | |||||
/* intialize matrix for scaning. */ | |||||
void matrix_init(void); | void matrix_init(void); | ||||
/* scan all key states on matrix */ | /* scan all key states on matrix */ | ||||
uint8_t matrix_scan(void); | uint8_t matrix_scan(void); |
void ibm4704_init(void) | void ibm4704_init(void) | ||||
{ | { | ||||
inhibit(); // keep keyboard from sending | |||||
IBM4704_INT_INIT(); | IBM4704_INT_INIT(); | ||||
IBM4704_INT_ON(); | IBM4704_INT_ON(); | ||||
idle(); | |||||
idle(); // allow keyboard sending | |||||
} | } | ||||
/* | /* | ||||
return rbuf_dequeue(); | return rbuf_dequeue(); | ||||
} | } | ||||
uint8_t ibm4704_recv(void) | |||||
{ | |||||
if (rbuf_has_data()) { | |||||
return rbuf_dequeue(); | |||||
} else { | |||||
return -1; | |||||
} | |||||
} | |||||
/* | /* | ||||
Keyboard to Host | Keyboard to Host | ||||
---------------- | ---------------- | ||||
Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part. | Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part. | ||||
____ __ __ __ __ __ __ __ __ __ ________ | |||||
Clock \____/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ | |||||
____ __ __ __ __ __ __ __ __ __ _______ | |||||
Clock \_____/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ | |||||
____ ____ ____ ____ ____ ____ ____ ____ ____ ____ | ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ | ||||
Data ____/ X____X____X____X____X____X____X____X____X____X________ | Data ____/ X____X____X____X____X____X____X____X____X____X________ | ||||
Start 0 1 2 3 4 5 6 7 P Stop | Start 0 1 2 3 4 5 6 7 P Stop | ||||
Start bit: can be long as 300-350us. | Start bit: can be long as 300-350us. | ||||
Inhibit: Pull Data line down to inhibit keyboard to send. | Inhibit: Pull Data line down to inhibit keyboard to send. | ||||
Timing: Host reads bit while Clock is hi. | |||||
Timing: Host reads bit while Clock is hi.(rising edge) | |||||
Stop bit: Keyboard pulls down Data line to lo after 9th clock. | Stop bit: Keyboard pulls down Data line to lo after 9th clock. | ||||
*/ | */ | ||||
uint8_t ibm4704_recv(void) | |||||
{ | |||||
if (rbuf_has_data()) { | |||||
return rbuf_dequeue(); | |||||
} else { | |||||
return -1; | |||||
} | |||||
} | |||||
ISR(IBM4704_INT_VECT) | ISR(IBM4704_INT_VECT) | ||||
{ | { | ||||
static enum { | static enum { | ||||
INIT, START, BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, PARITY, | |||||
} state = INIT; | |||||
BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, PARITY, STOP | |||||
} state = BIT0; | |||||
// LSB first | // LSB first | ||||
static uint8_t data = 0; | static uint8_t data = 0; | ||||
// Odd parity | // Odd parity | ||||
static uint8_t parity = false; | static uint8_t parity = false; | ||||
ibm4704_error = 0; | ibm4704_error = 0; | ||||
// return unless falling edge | |||||
if (clock_in()) { goto RETURN; } // why this occurs? | |||||
state++; | |||||
switch (state) { | switch (state) { | ||||
case START: | |||||
// Data:Low | |||||
WAIT(data_hi, 10, state); | |||||
break; | |||||
case BIT0: | case BIT0: | ||||
case BIT1: | case BIT1: | ||||
case BIT2: | case BIT2: | ||||
} | } | ||||
if (!parity) | if (!parity) | ||||
goto ERROR; | goto ERROR; | ||||
break; | |||||
case STOP: | |||||
// Data:Low | |||||
WAIT(data_lo, 100, state); | |||||
rbuf_enqueue(data); | rbuf_enqueue(data); | ||||
ibm4704_error = IBM4704_ERR_NONE; | ibm4704_error = IBM4704_ERR_NONE; | ||||
goto DONE; | goto DONE; | ||||
default: | default: | ||||
goto ERROR; | goto ERROR; | ||||
} | } | ||||
state++; | |||||
goto RETURN; | goto RETURN; | ||||
ERROR: | ERROR: | ||||
ibm4704_error = state; | ibm4704_error = state; | ||||
while (ibm4704_send(0xFE)) _delay_ms(1); // resend | while (ibm4704_send(0xFE)) _delay_ms(1); // resend | ||||
xprintf("R:%02X%02X\n", state, data); | xprintf("R:%02X%02X\n", state, data); | ||||
DONE: | DONE: | ||||
state = INIT; | |||||
state = BIT0; | |||||
data = 0; | data = 0; | ||||
parity = false; | parity = false; | ||||
RETURN: | RETURN: |
/******************************************************************************* | /******************************************************************************* | ||||
* main | * main | ||||
******************************************************************************/ | ******************************************************************************/ | ||||
static void SetupHardware(void) | |||||
static void setup_mcu(void) | |||||
{ | { | ||||
/* Disable watchdog if enabled by bootloader/fuses */ | /* Disable watchdog if enabled by bootloader/fuses */ | ||||
MCUSR &= ~(1 << WDRF); | MCUSR &= ~(1 << WDRF); | ||||
/* Disable clock division */ | /* Disable clock division */ | ||||
clock_prescale_set(clock_div_1); | clock_prescale_set(clock_div_1); | ||||
} | |||||
static void setup_usb(void) | |||||
{ | |||||
// Leonardo needs. Without this USB device is not recognized. | // Leonardo needs. Without this USB device is not recognized. | ||||
USB_Disable(); | USB_Disable(); | ||||
int main(void) __attribute__ ((weak)); | int main(void) __attribute__ ((weak)); | ||||
int main(void) | int main(void) | ||||
{ | { | ||||
SetupHardware(); | |||||
setup_mcu(); | |||||
keyboard_setup(); | |||||
setup_usb(); | |||||
sei(); | sei(); | ||||
/* wait for USB startup & debug output */ | /* wait for USB startup & debug output */ |
// set for 16 MHz clock | // set for 16 MHz clock | ||||
CPU_PRESCALE(0); | CPU_PRESCALE(0); | ||||
keyboard_setup(); | |||||
// Initialize the USB, and then wait for the host to set configuration. | // Initialize the USB, and then wait for the host to set configuration. | ||||
// If the Teensy is powered without a PC connected to the USB port, | // If the Teensy is powered without a PC connected to the USB port, | ||||
// this will wait forever. | // this will wait forever. |