/******************************************
* Wireless Keyboard - Hejnar, Leventhal
* Computer Side Code
* Ports:
* A - Input/Output to Computer
* A.7 - Output Clock
* A.5 - Input Clock
* A.6 - Output Data
* A.4 - Input Data
*
* C - Debugging LEDs
*/
#include <Mega32.h>
#define CLK_TIME 250
#define RESPOND_WAIT 700
//define ps/2 data line states
#define IDLE 0
#define INHIBIT 1
#define BUSY 2
#define REQUEST 3
//define some useful maps to bits
#define UART_IN UCSRA.7
#define CLK_OUT PORTA.7
#define DATA_OUT PORTA.6
#define CLK_IN PINA.5
#define DATA_IN PINA.4
//state of the data line
char transmitting, receiving, waiting;
//receive state
//0 waiting for AA
//1 - waiting for FF
//2 - waiting for actual data
char RFState;
//variables for writing to PS/2 port
char position, PS2byte, PS2parity;
char clockState;
//define clock states
#define HIGH 0
#define FALLING 1
#define LOW 2
#define RISING 3
//define queue values
#define QUEUELEN 200
#define TRUE 1
#define FALSE 0
char queue[QUEUELEN]; //queue of data sent by keyboard.
(first in, first out)
char queueFull; //indicates
if queue is full
char queueEmpty;//indicates if queue is empty
char queueIn; //indicates
where to put data into queue
char queueOut; //indicates
where to take data out of queue
char queuePut(char d); //put
data into queue
char queueGet(void); //get
data from queue
//0 = waiting
//1 = received AA last
//2 = received FF last next in is good
char preamble_state;
//break code was received
char break_code;
char parity(char x)//calculate the parity of a character
{
char temp, i;
temp=1;
for(i=0;i<8;i++)
{
temp=temp^(x&1);
x>>=1;
}
return temp;
}
//insert data into queue.
Return 1 if queue full, or 0 if inserted data sucessfully
char queuePut(char d)
{
if
(queueFull==TRUE)//check if queue is full
return(TRUE);
queue[queueIn]=d; //insert d into queue
queueIn++; //increment
where to stick in the next d value
queueEmpty=FALSE; //indicate queue isnt' empty anymore
if
(queueIn==QUEUELEN) //if reached the end
of the queue
queueIn=0; //wrap
around to the beginning
if
(queueIn==queueOut) //if queueIn
caught up to queueOut
queueFull=TRUE;
//indicate queue is full
return(0);
}
//get data out of queue.
Return 0 if queue empty or the actual data if not empty
char queueGet(void)
{
char d;
if
(queueEmpty==TRUE) //check if queue is empty
return(0);
d=queue[queueOut]; //get data out of queue
queueOut++; //increment
location where to get next d value
queueFull=FALSE; //indicate queue isn't full anymore
if
(queueOut==QUEUELEN) //if
reached the end of the queue
queueOut=0; //wrap
around to the beginning
if
(queueOut==queueIn) //if queueOut
caught up to queueIn
queueEmpty=TRUE; //indicate queue is empty
return(d);
//return the data from queue
}
//determine the state of the data line
char clockStateNow(void)
{
if(transmitting ||
receiving || waiting)//I'm doing something
return(BUSY);
if(CLK_IN==0)//computer is inhibiting commmunication
return(INHIBIT);
if(DATA_IN==0)//computer
wants to send something
return(REQUEST);
return(IDLE);//nothing happening
}
void initChip(void)
{
OCR1A = CLK_TIME; //compare match to drive the clock to PC
OCR1B =
RESPOND_WAIT;
TCCR1B = 0x01; //Run counter at full speed
TCCR1A = 0x00; //nothing needed here, no output
TIMSK = 0x18; //Set the compare A interrupt and compare B
interrupt
queueFull=FALSE;//queue is not full
queueEmpty=TRUE;//queue is empty
queueIn=0; //where to insert into
queue
queueOut=0; //where to take out of
queue
PORTA = 0xC0; //initialize outputs to high
DDRD = 0x00;
DDRB = 0x00;
DDRA = 0xC0; //set data direction (see
initial wiriing comments)
DDRC = 0xFF; //set LEDs to all output
PORTC=0x00; //siet all LED's to off
UCSRB = 0x18 ;
//enable only the receiver, no interrupts
UCSRC=0xB6; //odd
parity, 8-bit data segments
UBRRH =
(int)300>>8;
UBRRL = (int)300
& 0xFF ; //need to change this 416 for 2400, 207 for 4800
#asm
sei
#endasm
}
//this interrupt occurs four times per clock cycle on the
ps/2 line
interrupt [TIM1_COMPA] void t1_cmpA(void)
{
TCNT1=0;
if(transmitting)
{
if(clockState==HIGH)//write data out here
{
if(position==0)//start bit
{
DATA_OUT=0;
}
else
if(position<9)//data bits
{
DATA_OUT=PS2byte&0x01;
PS2byte =
PS2byte>>1;
}
else
if(position==9)//parity bit
{
DATA_OUT=PS2parity;
}
else
if(position==10)//stop bit
{
DATA_OUT=1;
}
else
if(position==11)//end transmission, set up delay
{
transmitting=0;
waiting=1;
TCNT1=CLK_TIME+1;
}
position++;
clockState=FALLING;
}
else
if(clockState==FALLING)
{
CLK_OUT=0;
clockState=LOW;
}
else
if(clockState==LOW)
{
clockState=RISING;
}
else
{
CLK_OUT=1;
clockState=HIGH;
}
}
if(receiving)
{
//this was not
working properly, removed to avoid bugs
}
PORTC=0x00;
}
//end delay step
interrupt [TIM1_COMPB] void t1_cmpB(void)
{
waiting=0;
TCNT1=0;
}
void main(void)
{
char inFromRF;
char myState;
initChip();//initialize
chip
RFState
= 0;
break_code=0;
while(1)
{
if(UART_IN)
{
//handle uart, make sure you receive the
preamble before registering a symbol
inFromRF=UDR;
if(!UCSRA.2)
{
if(RFState==0)
{
if(inFromRF==0xAA)
RFState=1;
}
else if(RFState==1)
{
if(inFromRF==0xFF)
{
RFState=2;
}
}
else if(RFState==2)
{
if(inFromRF!=0xAA)//ignore this, the
transmitter was just helping the AGC
{
if(inFromRF==0xF0)//keep break codes
and their key codes together
{
PORTC=0xFF;
break_code=1;
}
else
{
if(break_code==1)
{
PORTC=0x00;
break_code=0;
queuePut(0xF0);
}
queuePut(inFromRF);
}
}
RFState=0;
}
PORTC=0x00;
}
}
myState=clockStateNow();//check if the line is idle
//never
go to receive mode
/* if(myState==REQUEST)
{
receiving=1;
position=0;
}*/
if(
(myState==IDLE) && !queueEmpty )//is it idle and is there something to
send
{
//PORTC=0xFF;
PS2byte =
queueGet();
if(PS2byte!=0)
{
transmitting=1;
position=0;
clockState=HIGH;
PS2parity =
parity(PS2byte);
}
}
}
}