Created
November 1, 2013 20:31
-
-
Save buzztiaan/7271501 to your computer and use it in GitHub Desktop.
working PS2 mousedriver for LM4F with energia!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// attempt at PS2 implementation | |
// code stolen from http://hippy.blogs.exetel.com.au/index.php?/archives/24-MA2onPC,-a-Trackball-and-a-Stellaris-Launchpad.html | |
#define CHECK_BIT(var,pos) ((var) & (1<<(pos))) | |
#define PS2_clockpin PE_2 | |
#define PS2_datapin PE_3 | |
// possible signal states | |
enum | |
{ | |
PS2_STATE_IDLE, | |
PS2_STATE_DATA, | |
PS2_STATE_PARITY, | |
PS2_STATE_STOP, | |
PS2_STATE_DONE | |
}; | |
// PS/2 transmission mode | |
enum | |
{ | |
HOST_TO_SLAVE, | |
SLAVE_TO_HOST | |
}; | |
// slave states | |
enum | |
{ | |
SLAVE_DEAD, | |
SLAVE_INITIALIZED, | |
SLAVE_ACK_EXPECTED, | |
SLAVE_DATA_EXPECTED | |
}; | |
// ps/2 byte buffers | |
volatile unsigned long g_ulRXCode = 0; // RX | |
volatile unsigned char g_ulTXCode = 0; // TX | |
// state machines | |
volatile unsigned char g_ucPS2RXState; // current RX state | |
volatile unsigned char g_ucPS2TXState = PS2_STATE_IDLE; // current TX state | |
volatile unsigned char g_ucPS2Mode = SLAVE_TO_HOST; // current transmission mode | |
volatile unsigned char g_ucPS2SlaveState = SLAVE_DEAD; // current state of the slave | |
volatile unsigned char g_ucPS2ExpectAck = 0; // are we expecting an ACK? | |
// extraction of the three byte movement message | |
volatile unsigned char g_ucMovSeq = 0; | |
volatile unsigned char g_ucMovHeader = 0; | |
volatile unsigned char g_ucDeltaX = 0; | |
volatile unsigned char g_ucDeltaY = 0; | |
unsigned char g_ucParity; | |
unsigned char g_ucTXParity; | |
unsigned char g_ucRXDataBitCount; | |
unsigned char g_ucTXDataBitCount; | |
// read ps/2 data pin. | |
char PS2ReadDat(void) | |
{ | |
if(digitalRead(PS2_datapin)) | |
{ | |
return(1); | |
} | |
else | |
{ | |
return(0); | |
} | |
} | |
// check the parity. | |
char getParity(unsigned n) | |
{ | |
char parity = 0; | |
while (n) | |
{ | |
parity = !parity; | |
n = n & (n - 1); | |
} | |
return parity; | |
} | |
void PS2Send(unsigned char txb) | |
{ | |
// setup to send a byte to the slave PS/2 device | |
g_ucPS2Mode = HOST_TO_SLAVE; | |
g_ulTXCode = txb; | |
g_ucTXDataBitCount = 0; | |
g_ucPS2TXState = PS2_STATE_DATA; | |
// disable interrupts on port a | |
noInterrupts(); //IntDisable(INT_GPIOA); | |
// flip pins to outputs | |
//GPIOPinTypeGPIOOutput(PS2_BASE, CLK_PIN | DAT_PIN); | |
pinMode(PS2_clockpin, OUTPUT); | |
pinMode(PS2_datapin, OUTPUT); | |
// CLK low for 60us | |
digitalWrite(PS2_clockpin, LOW); //GPIOPinWrite(PS2_BASE, CLK_PIN, 0); | |
delay(60); | |
// data low | |
digitalWrite(PS2_datapin, LOW); //GPIOPinWrite(PS2_BASE, DAT_PIN, 0); | |
// clk high | |
digitalWrite(PS2_clockpin, HIGH); ////GPIOPinWrite(PS2_BASE, CLK_PIN, CLK_PIN); | |
// clock input mode | |
pinMode(PS2_clockpin, INPUT); //GPIOPinTypeGPIOInput(PS2_BASE, CLK_PIN); | |
// re-enable interrupts | |
interrupts(); //IntEnable(INT_GPIOA); | |
// now the interrupt routine will tx the data with the incoming clock, then flips data pin back to input. | |
} | |
void PS2_interrupt() { | |
switch(g_ucPS2Mode) { | |
case SLAVE_TO_HOST: // PS2 device talks to us | |
{ | |
switch(g_ucPS2RXState) { | |
case PS2_STATE_IDLE: { | |
if(PS2ReadDat() == 0) { | |
g_ucPS2RXState = PS2_STATE_DATA; | |
g_ulRXCode = 0; | |
g_ucParity = 0; | |
g_ucRXDataBitCount = 0; | |
} else { | |
g_ucPS2RXState = PS2_STATE_IDLE; | |
} | |
break; | |
} | |
case PS2_STATE_DATA: { | |
g_ulRXCode >>= 1; // shift | |
if(PS2ReadDat()) { // read a bit | |
g_ulRXCode |= 0x80; // set the bit | |
g_ucParity++; // count highs | |
} | |
// last data bit | |
if(++g_ucRXDataBitCount > 7) { | |
g_ucPS2RXState = PS2_STATE_PARITY; | |
} | |
break; | |
} | |
case PS2_STATE_PARITY: { | |
if((g_ucParity & 0x01) == PS2ReadDat()) { | |
g_ucPS2RXState = PS2_STATE_IDLE; | |
} else { | |
g_ucPS2RXState = PS2_STATE_STOP; | |
} | |
break; | |
} // parity | |
case PS2_STATE_STOP: { | |
// check for stop bit (inverted) | |
if(PS2ReadDat()==0) { | |
g_ucPS2RXState = PS2_STATE_IDLE; | |
} else { | |
g_ucPS2RXState = PS2_STATE_DONE; | |
} | |
break; | |
} | |
} // select state | |
} | |
//------------------------------------------------------------------ | |
case HOST_TO_SLAVE: // we want to talk to the PS2 device | |
{ | |
switch(g_ucPS2TXState) { | |
case PS2_STATE_DATA: { | |
// write data out, LSB first | |
//g_ulTXCode >>= 1; | |
if ( g_ucTXDataBitCount > 0) { | |
if CHECK_BIT(g_ulTXCode, g_ucTXDataBitCount-1) { | |
digitalWrite(PS2_datapin, HIGH); // GPIOPinWrite(PS2_BASE, DAT_PIN, DAT_PIN); | |
} else { | |
digitalWrite(PS2_datapin, LOW); // GPIOPinWrite(PS2_BASE, DAT_PIN, 0); | |
} | |
} else { | |
// not the start bit | |
digitalWrite(PS2_datapin, LOW); // GPIOPinWrite(PS2_BASE, DAT_PIN, 0); | |
} | |
if(++g_ucTXDataBitCount == 9) { | |
g_ucPS2TXState = PS2_STATE_PARITY; | |
} | |
break; | |
} | |
case PS2_STATE_PARITY: { | |
if (!getParity(g_ulTXCode)) { | |
digitalWrite(PS2_datapin, HIGH); // GPIOPinWrite(PS2_BASE, DAT_PIN, DAT_PIN); | |
} else { | |
digitalWrite(PS2_datapin, LOW); // GPIOPinWrite(PS2_BASE, DAT_PIN, 0); | |
} | |
g_ucPS2TXState = PS2_STATE_STOP; | |
break; | |
} // parity | |
case PS2_STATE_STOP: { | |
pinMode(PS2_datapin, INPUT); | |
g_ucPS2TXState = PS2_STATE_DONE; | |
} // stop bit | |
case PS2_STATE_DONE: { | |
g_ucPS2TXState = PS2_STATE_IDLE; | |
g_ucPS2ExpectAck = 1; | |
g_ucPS2Mode = SLAVE_TO_HOST; | |
} | |
} // ps/2 tx state | |
} // host to slave | |
} // select transmission mode | |
}// interrupt handler | |
void setup() | |
{ | |
Serial.begin(9600); | |
attachInterrupt(PS2_clockpin, PS2_interrupt, FALLING); | |
PS2Send(0xF4); | |
g_ucPS2SlaveState = SLAVE_DATA_EXPECTED; | |
} | |
void loop() | |
{ | |
if (g_ucPS2RXState == PS2_STATE_DONE) { | |
switch (g_ucPS2SlaveState) { | |
case SLAVE_DATA_EXPECTED: { | |
switch (g_ucMovSeq) { | |
case 0: { | |
g_ucMovHeader = g_ulRXCode; | |
// check if this could be the first packet, whose 3rd bit is always 1 | |
if ( CHECK_BIT(g_ulRXCode,3) != 0) { g_ucMovSeq++; } | |
break; | |
} | |
case 1: { | |
g_ucDeltaX = g_ulRXCode; | |
g_ucMovSeq++; | |
break; | |
} | |
case 2: { | |
g_ucDeltaY = g_ulRXCode; | |
g_ucMovSeq = 0; | |
/* | |
UARTCharPut(UART0_BASE, 'm' ); | |
UARTCharPut(UART0_BASE, g_ucMovHeader ); | |
UARTCharPut(UART0_BASE, g_ucDeltaX ); | |
UARTCharPut(UART0_BASE, g_ucDeltaY ); | |
*/ | |
Serial.print("head "); | |
Serial.println(g_ucMovHeader); | |
Serial.print("x "); | |
Serial.println(g_ucDeltaX); | |
Serial.print("y "); | |
Serial.println(g_ucDeltaX); | |
break; | |
} | |
} | |
break; | |
} | |
case SLAVE_ACK_EXPECTED: { | |
g_ucMovSeq = 0; | |
if (g_ulRXCode == 0xAA) { | |
// UARTCharPut(UART0_BASE, 0xAA); | |
// turn off led | |
// GPIO_PORTF_DATA_R &= ~(GPIO_PIN_1); | |
PS2Send(0xF2); | |
g_ucPS2SlaveState = SLAVE_INITIALIZED; | |
} else { | |
// UARTCharPut(UART0_BASE, 0xFE); // reset has failed | |
} | |
break; | |
} | |
case SLAVE_INITIALIZED: { | |
g_ucMovSeq = 0; | |
if (g_ulRXCode == 0x00) { | |
PS2Send(0xF4); | |
g_ucPS2SlaveState = SLAVE_DATA_EXPECTED; | |
} else { | |
// UARTCharPut(UART0_BASE, 0xF4 ); | |
} | |
break; | |
} | |
} | |
g_ucPS2RXState = PS2_STATE_IDLE; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment