#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include "ioconfig.h"
#include "ps2.h"
Defines | |
#define | ps2_datin() ((PS2PIN & _BV(PS2DAT)) ? 0200 : 0) |
Read PS2 data into bit 7. | |
#define | ps2_clkin() ((PS2PIN & _BV(PS2CLK)) ? 0200 : 0) |
Read PS2 clk into bit 7. | |
Enumerations | |
enum | _state { IDLE = 0, RX_DATA, RX_PARITY, RX_STOP, TX_REQ0, TX_DATA, TX_PARITY, TX_STOP, TX_ACK, TX_END, ERROR = 255 } |
PS2 protocol states. More... | |
Functions | |
void | ps2_dir (uint8_t, uint8_t) |
Set busses direction (1 == in). | |
void | ps2_clk (uint8_t) |
Set clk. | |
void | ps2_dat (uint8_t) |
Set dat. | |
uint8_t | ps2_busy () |
Check if PS/2 statemachine is in IDLE state. | |
void | ps2_init () |
Init PS/2 related I/O and interrupts. | |
void | ps2_recover () |
Begin error recovery: disable reception and wait for timer interrupt. | |
void | ps2_enable_recv (uint8_t enable) |
Suspend or enable PS/2 device by pulling clock line low. | |
uint8_t | ps2_avail () |
Check if the input buffer contains at least one byte. | |
uint8_t | ps2_getbyte () |
Get one byte from input buffer. ps_avail() must be checked before doing so. | |
void | ps2_sendbyte (uint8_t byte) |
Transmit one byte and wait for completion. | |
ISR (INT0_vect, ISR_NOBLOCK) | |
ISR (TIMER0_OVF_vect) | |
transmit timer and error recovery vector |
This implementation is entirely interrupt-driven so all comms happens in background. Clock is tied to INT0 pin and events are handled in INT0 ISR handler.
Events not triggered by clock (end of transmission, transmission request, watchdog, error recovery) use Timer0. Watch out how state changes in different handlers.
#define ps2_clkin | ( | ) | ((PS2PIN & _BV(PS2CLK)) ? 0200 : 0) |
Read PS2 clk into bit 7.
#define ps2_datin | ( | ) | ((PS2PIN & _BV(PS2DAT)) ? 0200 : 0) |
Read PS2 data into bit 7.
enum _state |
PS2 protocol states.
00045 { 00046 IDLE = 0, 00047 RX_DATA, 00048 RX_PARITY, 00049 RX_STOP, 00050 00051 TX_REQ0, 00052 TX_DATA, 00053 TX_PARITY, 00054 TX_STOP, 00055 TX_ACK, 00056 TX_END, 00057 00058 ERROR = 255 00059 };
ISR | ( | TIMER0_OVF_vect | ) |
transmit timer and error recovery vector
00235 { 00236 static uint8_t barkcnt = 0; 00237 00238 switch (state) { 00239 case ERROR: 00240 state = IDLE; 00241 ps2_clk(0); 00242 ps2_dat(0); 00243 ps2_enable_recv(1); 00244 00245 // stop timer 00246 TIMSK &= ~_BV(TOIE0); 00247 TCCR0 = 0; 00248 break; 00249 case TX_REQ0: 00250 // load the timer to serve as a watchdog 00251 // after 20 barks this is an error 00252 barkcnt = 20; 00253 TIMSK |= _BV(TOIE0); // enable TMR0 interrupt 00254 TCNT0 = 0;//255; // 20*255*256/8e6 == 163ms 00255 TCCR0 = 4; // prescaler = /256, go! 00256 00257 // waited for 100us after pulling clock low, pull data low 00258 ps2_dat(0); 00259 ps2_dir(0,0); 00260 00261 // release the clock line 00262 ps2_dir(0,1); 00263 00264 GIFR |= _BV(INTF0); // clear INT0 flag 00265 GICR |= _BV(INT0); // enable INT0 @(negedge clk) 00266 00267 // see you in INT0 handler 00268 bits = 8; 00269 parity = 0; 00270 00271 state = TX_DATA; 00272 break; 00273 case TX_END: 00274 // wait until both clk and dat are up, that will be all 00275 if (ps2_clkin() && ps2_datin()) { 00276 TIMSK &= ~_BV(TOIE0); 00277 TCCR0 = 0; 00278 state = IDLE; 00279 } else { 00280 if (waitcnt == 0) { 00281 state = ERROR; 00282 ps2_recover(); 00283 } else { 00284 waitcnt--; 00285 } 00286 } 00287 break; 00288 default: 00289 // watchdog barked: probably not a mouse! 00290 if (barkcnt == 0) { 00291 state = ERROR; 00292 ps2_recover(); 00293 } else { 00294 barkcnt--; 00295 } 00296 break; 00297 } 00298 }
ISR | ( | INT0_vect | , | |
ISR_NOBLOCK | ||||
) |
Happens every negative PS2 clock transition.
ISR_NOBLOCK because nothing here is really critical, while C1351 emulation is really time critical.
00148 { 00149 uint8_t ps2_indat = ps2_datin(); 00150 switch (state) { 00151 case ERROR: 00152 break; 00153 00154 // Receive states 00155 00156 case IDLE: 00157 if (ps2_indat == 0) { 00158 state = RX_DATA; 00159 bits = 8; 00160 parity = 0; 00161 recv_byte = 0; 00162 } else { 00163 state = ERROR; 00164 } 00165 break; 00166 case RX_DATA: 00167 recv_byte = (recv_byte >> 1) | ps2_indat; 00168 parity ^= ps2_indat; 00169 00170 if (--bits == 0) { 00171 state = RX_PARITY; 00172 } 00173 break; 00174 case RX_PARITY: 00175 parity ^= ps2_indat; 00176 if (parity) { 00177 state = RX_STOP; 00178 } else { 00179 state = ERROR; 00180 } 00181 break; 00182 case RX_STOP: 00183 if (!ps2_indat) { 00184 state = ERROR; 00185 } else { 00186 rx_buf[rx_head] = recv_byte; 00187 rx_head = (rx_head + 1) % PS2_RXBUF_LEN; 00188 00189 state = IDLE; 00190 } 00191 break; 00192 00193 // Transmit states 00194 00195 case TX_REQ0: 00196 // state will be switched in timer interrupt handler 00197 break; 00198 case TX_DATA: 00199 ps2_dat(tx_byte & 001); 00200 parity ^= tx_byte & 001; 00201 tx_byte >>= 1; 00202 if (--bits == 0) { 00203 state = TX_PARITY; 00204 } 00205 break; 00206 case TX_PARITY: 00207 ps2_dat(parity ^ 001); 00208 state = TX_STOP; 00209 break; 00210 case TX_STOP: 00211 ps2_dat(0); 00212 ps2_dir(1,1); 00213 state = TX_ACK; 00214 break; 00215 case TX_ACK: 00216 if (ps2_indat) { 00217 state = ERROR; 00218 } else { 00219 // this will end in TMR0 interrupt 00220 state = TX_END; 00221 00222 waitcnt = 50; // after 100us it's an error 00223 TIMSK |= _BV(TOIE0); // enable TMR0 interrupt 00224 TCNT0 = 255-2; // 4 counts: 2us 00225 TCCR0 = 2; // prescaler = f/8: go! 00226 } 00227 break; 00228 case TX_END: 00229 break; 00230 } 00231 ps2_recover(); 00232 }
uint8_t ps2_avail | ( | ) |
uint8_t ps2_busy | ( | ) |
void ps2_clk | ( | uint8_t | c | ) |
void ps2_dat | ( | uint8_t | d | ) |
void ps2_dir | ( | uint8_t | dat_in, | |
uint8_t | clk_in | |||
) |
void ps2_enable_recv | ( | uint8_t | ) |
Suspend or enable PS/2 device by pulling clock line low.
1 | = enable |
00088 { 00089 if (enable) { 00090 state = IDLE; 00091 ps2_dir(1,1); 00092 // enable INT0 interruptt 00093 GIFR |= _BV(INTF0); 00094 GICR |= _BV(INT0); 00095 } else { 00096 // disable INT0, then everything else 00097 GICR &= ~_BV(INT0); 00098 ps2_clk(0); 00099 ps2_dir(1,0); 00100 } 00101 }
uint8_t ps2_getbyte | ( | ) |
Get one byte from input buffer. ps_avail() must be checked before doing so.
00120 { 00121 uint8_t result = rx_buf[rx_tail]; 00122 rx_tail = (rx_tail + 1) % PS2_RXBUF_LEN; 00123 00124 return result; 00125 }
void ps2_init | ( | ) |
Init PS/2 related I/O and interrupts.
00067 { 00068 state = IDLE; 00069 rx_head = 0; 00070 rx_tail = 0; 00071 ps2_enable_recv(0); 00072 00073 MCUCR |= _BV(ISC01); // falling edge for INT00 00074 TIMSK &= ~_BV(TOIE0); 00075 }
void ps2_recover | ( | ) |
Begin error recovery: disable reception and wait for timer interrupt.
00078 { 00079 if (state == ERROR) { 00080 ps2_enable_recv(0); 00081 TCNT0 = 255-35; // approx 1ms 00082 TIMSK |= _BV(TOIE0); 00083 00084 TCCR0 = 4; // enable: clk/256 00085 } 00086 }
void ps2_sendbyte | ( | uint8_t | byte | ) |
Transmit one byte and wait for completion.
00127 { 00128 while (state != IDLE); 00129 00130 // 1. pull clk low for 100us 00131 ps2_enable_recv(0); 00132 00133 tx_byte = byte; 00134 state = TX_REQ0; 00135 00136 // 128us 00137 TCNT0 = 255-4; 00138 TIMSK |= _BV(TOIE0); 00139 TCCR0 = 4; 00140 00141 while (state != IDLE); 00142 }