ps2.c File Reference

PS/2 protocol implementation. More...

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include "ioconfig.h"
#include "ps2.h"

Include dependency graph for ps2.c:


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

Detailed Description

PS/2 protocol implementation.

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 Documentation

 
#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.


Enumeration Type Documentation

enum _state

PS2 protocol states.

Enumerator:
IDLE  Idle waiting.
RX_DATA  Receiving data.
RX_PARITY  Receiving parity bit.
RX_STOP  Receiving stop bit.
TX_REQ0  Requesting to send.
TX_DATA  Transmitting data.
TX_PARITY  Transmitting parity bit.
TX_STOP  Transmitting stop bit.
TX_ACK  Waiting ACK.
TX_END  Waiting for TX end.
ERROR  Error state.
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 };


Function Documentation

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 }

Here is the call graph for this function:

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 }

Here is the call graph for this function:

uint8_t ps2_avail (  ) 

Check if the input buffer contains at least one byte.

00116                     {
00117     return rx_head != rx_tail;
00118 } 

uint8_t ps2_busy (  ) 

Check if PS/2 statemachine is in IDLE state.

00065 { return state != IDLE; }

void ps2_clk ( uint8_t  c  ) 

Set clk.

00108                         {
00109     c ? (PS2PORT |= _BV(PS2CLK)) : (PS2PORT &= ~_BV(PS2CLK));
00110 }

void ps2_dat ( uint8_t  d  ) 

Set dat.

00112                         {
00113     d ? (PS2PORT |= _BV(PS2DAT)) : (PS2PORT &= ~_BV(PS2DAT));
00114 }

void ps2_dir ( uint8_t  dat_in,
uint8_t  clk_in 
)

Set busses direction (1 == in).

00103                                              {
00104     dat_in ? (PS2DDR &= ~_BV(PS2DAT)) : (PS2DDR |= _BV(PS2DAT));
00105     clk_in ? (PS2DDR &= ~_BV(PS2CLK)) : (PS2DDR |= _BV(PS2CLK));
00106 }

void ps2_enable_recv ( uint8_t   ) 

Suspend or enable PS/2 device by pulling clock line low.

Parameters:
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 }

Here is the call graph for this function:

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 }

Here is the call graph for this function:

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 }

Here is the call graph for this function:

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 }

Here is the call graph for this function:


Generated on Fri Nov 13 04:21:25 2009 for [M]ouse by  doxygen 1.5.9