#include "project.h" #define KBCLK (1UL << 0) #define KBCLK_PORT GPIOA #define KBCLK_IRQ NVIC_EXTI0_IRQ #define KBDAT (1UL << 1) #define KBDAT_PORT GPIOA /*Cope with lost sync */ /*If we've had no bits for this long, it's definately the start of a new word, 72MHz units */ #define RESYNC_GAP (7200000) /* 100ms */ #define ATKBD_TIMEOUT 1000 /*how long to wait for the keyboard to respond to a command in ms */ typedef enum { STATE_START=0, STATE_BIT0, STATE_BIT1, STATE_BIT2, STATE_BIT3, STATE_BIT4, STATE_BIT5, STATE_BIT6, STATE_BIT7, STATE_PARITY, STATE_STOP, } atkbd_state; #define ATKBD_CMD_SETLEDS 0xed #define ATKBD_CMD_GSCANSET 0xf0 #define ATKBD_CMD_SSCANSET 0xf0 #define ATKBD_CMD_GETID 0xf2 #define ATKBD_CMD_SETREP 0xf3 #define ATKBD_CMD_ENABLE 0xf4 #define ATKBD_CMD_RESET_DIS 0xf5 #define ATKBD_CMD_RESET_DEF 0xf6 #define ATKBD_CMD_SETALL_MB 0xf8 #define ATKBD_CMD_SETALL_MBR 0xfa #define ATKBD_CMD_RESET_BAT 0xff #define ATKBD_CMD_RESEND 0xfe #define ATKBD_CMD_EX_ENABLE 0xea #define ATKBD_CMD_EX_SETLEDS 0xeb #define ATKBD_CMD_OK_GETID 0xe8 #define ATKBD_RET_ECHO 0xee #define ATKBD_RET_ACK 0xfa #define ATKBD_RET_NAK 0xfe #define ATKBD_RET_BAT 0xaa #define ATKBD_RET_EMUL0 0xe0 #define ATKBD_RET_EMUL1 0xe1 #define ATKBD_RET_RELEASE 0xf0 #define ATKBD_RET_HANJA 0xf1 #define ATKBD_RET_HANGEUL 0xf2 #define ATKBD_RET_ERR 0xff #define ATKBD_KEY_UNKNOWN 0 #define ATKBD_KEY_NULL 255 static int atkbd_ack; static int atkbd_nack; static int atkbd_bat; static int atkbd_echo; static uint32_t cycle_diff(uint32_t a,uint32_t b) { return b-a; } static void atkbd_dispatch(int emul,int key,int updown) { printf("KEY> %x %x %x\r\n",emul,key,updown); } static void atkbd_data_dispatch(uint8_t byte) { static int release; static int emul; printf("ATKBD < 0x%02x\r\n",byte); switch (byte) { case ATKBD_RET_ACK: atkbd_ack++; break; case ATKBD_RET_NAK: atkbd_nack++; break; case ATKBD_RET_BAT: atkbd_bat++; break; case ATKBD_RET_ECHO: atkbd_echo++; break; case ATKBD_RET_ERR: case ATKBD_KEY_UNKNOWN: case ATKBD_RET_HANJA: /*Don't handle japanese or korean for the moment*/ case ATKBD_RET_HANGEUL: /*Don't handle japanese or korean for the moment*/ /*All these need no action */ break; case ATKBD_RET_EMUL0: emul=1; break; case ATKBD_RET_EMUL1: emul=2; break; case ATKBD_RET_RELEASE: release=1; break; default: atkbd_dispatch(emul,byte,release); emul=0; release=0; } } void exti0_isr(void) { static uint32_t last_interrupt=0; static atkbd_state state=STATE_START; static uint8_t atkbd_byte; static int parity; uint32_t now,diff; int d; d=!!GET(KBDAT); exti_reset_request(KBCLK); now=dwt_read_cycle_counter(); diff=cycle_diff(last_interrupt,now); last_interrupt=now; if (diff>RESYNC_GAP) state=STATE_START; switch (state) { case STATE_START: atkbd_byte=0; parity=0; if (!d) state++; break; case STATE_BIT0: case STATE_BIT1: case STATE_BIT2: case STATE_BIT3: case STATE_BIT4: case STATE_BIT5: case STATE_BIT6: case STATE_BIT7: atkbd_byte|=d<<(state-STATE_BIT0); /* fall through*/ case STATE_PARITY: parity ^= d; state++; break; case STATE_STOP: if (d && parity) atkbd_data_dispatch(atkbd_byte); state=STATE_START; break; } } void atkbd_set(int clk,int dat) { if (clk) { MAP_INPUT_PU(KBCLK); } else { CLEAR(KBCLK); MAP_OUTPUT_PP(KBCLK); } if (dat) { MAP_INPUT_PU(KBDAT); } else { CLEAR(KBDAT); MAP_OUTPUT_PP(KBDAT); } } int atkbd_send(uint8_t d) { uint32_t then=ticks; int parity=1; uint32_t c; nvic_disable_irq(KBCLK_IRQ); #define PS2_CLOCK_SEIZE_DELAY 200 #define PS2_BIT_DELAY 10 atkbd_set(0,1); delay_us(PS2_CLOCK_SEIZE_DELAY); atkbd_set(0,0); delay_us(PS2_BIT_DELAY); atkbd_set(1,0); delay_us(PS2_BIT_DELAY); for (c=1;c<0x100;c<<=1) { while (GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; atkbd_set(1,c&d); parity^=!!(c&d); while (!GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; } while (GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; atkbd_set(1,parity); while (!GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; while (GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; while (!GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; atkbd_set(1,1); while (GET(KBDAT)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; while (GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; while (!GET(KBDAT)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; while (!GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; exti_reset_request(KBCLK); nvic_enable_irq(KBCLK_IRQ); printf("ATKBD > 0x%02x\r\n",d); return 0; err: atkbd_set(1,1); while (!GET(KBDAT)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; while (!GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err; exti_reset_request(KBCLK); nvic_enable_irq(KBCLK_IRQ); printf("ATKBD >! 0x%02x\r\n",d); return -1; } static int atkbd_reset(void) { uint32_t then=ticks; atkbd_bat=0; atkbd_send(ATKBD_CMD_RESET_BAT); while (!atkbd_bat) if (timed_out(then,ATKBD_TIMEOUT)) return -1; then=ticks; atkbd_ack=0; atkbd_send(ATKBD_CMD_SETALL_MBR); while (!atkbd_ack) if (timed_out(then,ATKBD_TIMEOUT)) return -1; return 0; } int atkbd_request_echo(void) { uint32_t then=ticks; atkbd_ack=0; atkbd_send(ATKBD_CMD_ECHO); while (!atkbd_echo) if (timed_out(then,ATKBD_TIMEOUT)) return -1; return 0; } int atkbd_set_leds(uint8_t leds) { uint32_t then=ticks; atkbd_ack=0; atkbd_send(ATKBD_CMD_SETLEDS); while (!atkbd_ack) if (timed_out(then,ATKBD_TIMEOUT)) return -1; then=ticks; atkbd_ack=0; atkbd_send(leds); while (!atkbd_ack) if (timed_out(then,ATKBD_TIMEOUT)) return -1; return 0; } void atkbd_init(void) { atkbd_set(1,1); delay_ms(200); nvic_enable_irq(NVIC_EXTI0_IRQ); exti_select_source(KBCLK, KBCLK_PORT); exti_set_trigger(KBCLK, EXTI_TRIGGER_FALLING); exti_enable_request(KBCLK); exti_reset_request(KBCLK); nvic_enable_irq(KBCLK_IRQ); atkbd_reset(); atkbd_request_echo(); atkbd_set_leds(0x07); }