/******************************************

 *    Wireless Keyboard - Hejnar, Leventhal

 *    Keyboard Side Code

*/

 

 

#include <Mega32.h>

 

//connect the DAT line to B.0 which is pin 1 on the chip    (Yellow)

//connect the CLK line to B.2 which is pin 3 on the chip    (Blue)

 

#define rDAT PIND.7

#define rCLK PIND.3

 

#define wDAT PORTD.7

#define wCLK PORTD.3

 

#define TRUE 1

#define FALSE 0

 

#define QUEUELEN 200

char receive;         //indicate that int2 interrupt should work as reciever (if 1) or tranmsitter (if 0)

char countIn;        //count how many Low CLK pulses we have seen as we receive data from keyboard

char countOut;     //count how many Low CLK pulses we have seen as we transmitt data to keyboard

unsigned char dataIn;         //will store the data bits coming in from the keyboard

unsigned char dataOut;      //will store the data bits we are sending out to the keyboard

char parityIn;        //will store the calculated parity of data coming in from the keyboard

char parityOut;     //will store the calculated parity of data we are sending out to the keyboard

char pError;           //will store if there was a parity error in data sent by keyboard

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 rxDone;         //indicate done receiving frame

char txDone;         //indicate done transmitting frame

char t2visits;         //times gone into timer2 interrupt without any resets of the counter

 

void initialize(void);             //initialize mcu

void initializeKyBd(void);//initialize keyboard

char queuePut(char d);       //put data into queue

char queueGet(void);          //get data from queue

void TXtoKyBd(char d);    //send command to keyboard

void RXfromKyBd(void);   //receive data from keyboard

 

//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

}

 

//on every falling clock edge generated by the keyboard, either receives or sends data

interrupt [EXT_INT1] void external_int1(void)

{

                //when receiving data

                if(receive==TRUE)

                {

                                countIn++; //count how many clock pulses we have seen

                                //if countIn=1, then seeing the start bit

                                //if countIn=2 to 9, then seeing data bits 0 to 7

                                //if countIn=10, then seeing the parity bit

                                //if countIn=11, then seeing the stop bit

               

                                if (countIn==1)//seeing start bit

                                {

                               

                                     dataIn=0;

                                     rxDone=FALSE; //receiving not done (just starting)                                      

                                                //set up timer0 to check for no clock transition in 200uSec

                                                TCNT0=0;             //reset timer0

                                                TCCR0=0x0B;       //put timer0 into compare match mode, prescaler of 64;

                                                TIMSK=(TIMSK&0xFD)|0x02; //make bit1=1 to enable compare match interrupt;

                                                TIFR=TIFR&0xFD; //make bit1=0 to clear the timer0 comp match flag (just in case its set)

                                }

                               

                                else if ((countIn>1) && (countIn<10)) //data bits being sent

                                {  

                                                TCNT0=0;             //reset timer0 since saw a clock signal

                                                TIFR=TIFR&0xFD;//make bit1=0 to clear the timer0 comp match flag (just in case its set)

                                               

                                                dataIn=dataIn>>1;//shift data right by 1

                                                if (rDAT==1)         //if DAT line is 1

                                                {

                                                                dataIn=dataIn|0x80;             //shift in a received bit of 1. (0 shifted in automatically)

                                                                parityIn++;                            //update the parity  

                                                } 

                                }

                                else if (countIn==10) //parity bit being sent

                                {      

                                                TCNT0=0;             //reset timer0 since saw a clock signal

                                                TIFR=TIFR&0xFD; //set bit1=0 to clear the timer0 comp match flag (just in case its set)

                                               

                                                //partiy should be odd

                                                if ((parityIn+rDAT)&0x01)  //if (calculated parity+ rDAT) is odd,

                                                                pError=FALSE;                     //then no parity error

                                                else

                                                                pError=TRUE; //otherewise there was a parity error  

                                }

                               

                                else if (countIn==11)           //stop bit being sent

                                {

                                                TCCR0=0x00;        //stop timer0 since seen last clock

                                                TCNT0=0;             //reset timer0

                                                TIMSK=TIMSK&0xFD;     //set bit1=0 to disable timer0 interrupt

                                                TIFR=TIFR&0xFD;              //set bit1=0 to clear the timer0 comp match flag just in case

                                                //don't pass along responses to RF that are meant only for the microcontroller or if there was a parity error

                                                // parityError   BAT successful      Ack           BAT error

                                                if(!pError && (dataIn!=0xAA) && (dataIn!=0xFA) && (dataIn!=0xFC))

                                                {

                                                                                //AGC set up and syncronization

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xAA);

                                                                                queuePut(0xFF);

                                                                                queuePut(dataIn); //insert data into queue

                                                }

 

                                                rxDone=TRUE;     //indicate that we have received a frame

                                                countIn=0;            //reset count since have seen the end of frame

                                                parityIn=0;            //clear parity for next time       

                                }   

                }

                //when sending data, writing stuff onto the DAT line when clock is low so

                //that the data is valid when clock goes high and the keyboard reads it

                else

                {

                                countOut++;         //coutn how many clock pulses we have seen

                                //if countOut=1, then seeing ourselves pulling line low to inhibit communication

                                //if countOut=2 to 9 then we will be sending data bits 0 to 7

                                //if countOut=10, then we will be sending out the parity bit

                                //if countOut=11, then we will be sending out the stop bit

                                //if countOut=12, then seeing the "ack" bit     

               

                                if (countOut==1) //seeing ourslves pulling the line low to inhibit communication

                                {

                                                //set up timer so that we keep CLK low for at least 100uSec

                                                TCCR1A=0x00;     //normal waveform generation

                                                TCCR1B=0x01;     //normal waveform generation and full clock speed (16MHz)

                                                TCNT1=0;             //reset timer1 to 0

                                                OCR1A=2240;       //140uSec*16MHz=2240cycles

                                                TIMSK=(TIMSK&0xEF)|0x10;           //set bit4=1 to 1 to enable Output Compare A match interrupt 

                                               

                                                //set up timer0 to check for no clock transition in 200uSec

                                                TCNT0=0;             //reset timer0

                                                TCCR0=0x0B;       //put timer0 into compare match mode, prescaler of 64;

                                                TIMSK=(TIMSK&0xFD)|0x02; //set bit1=1 to enable compare match interrupt;

                                                TIFR=TIFR&0xFD; //set bit1=0 to clear the timer0 comp match flag (just in case its set)

              

                                                parityOut=0;         //reset parity

                                                txDone=FALSE;   //transmitting not done(just starting)

                                               

                                }

                               

                                else if (countOut>1 &&countOut<10) //data bits being sent

                                {      

                                                TCNT0=0; //reset timer0 since saw a clock signal

                                                TIFR=TIFR&0xFD; //set bit1=0 to clear the timer0 comp match flag just in case

                                               

                                                if (dataOut&0x01)//if lowest bit is a 1

                                                {

                                                                wDAT=1;                              //write out a 1 to the keyboard

                                                                parityOut++;         //calculate the parity

                                                }

                                                else

                                                                wDAT=0;              //write out a 0 to the keybard

                                                dataOut=dataOut>>1;//shift data right by 1 to get next bit next time

                                }

                               

                                else if (countOut==10)//send parity bit

                                {                                     

                                                TCNT0=0; //reset timer0 since saw a clock signal

                                                TIFR=TIFR&0xFD; //set bit1=0 to clear the timer0 comp match flag (just in case its set)

                                               

                                                if(parityOut&0x01) //if parity is odd

                                                                wDAT=0;              //write out a 0 to the keyboard

                                                else

                                                                wDAT=1;              //write out a 1 to the keyboard to make it odd parity

                                }

                               

                                else if (countOut==11)//sending out the stop bit

                                {                                              

                                                TCNT0=0; //reset timer0 since saw a clock signal

                                                TIFR=TIFR&0xFD; //set bit1=0 to clear the timer0 comp match flag (just in case its set)

                                               

                                                wDAT=1;              //write the stop bit (when change DDR, this will also activate the pullups)

                                                DDRD=0x00; //make DAT line input, CLK input already.  Do this so kybd can send ack bit

                                }

                               

                                else if (countOut==12)//getting the ack bit of 0 on DAT

                                {

                                TCCR0=0x00;        //turn off timer0 since seen last clock

                                TIMSK=TIMSK&0xFD;     //set bit1=0 to turn of timer0 comp match interrupt

                                TIFR=TIFR&0xFD;              //set bit1=0 to clear timer0 comp match flag (just in case its set)       

                               

                                                txDone=TRUE;//transmitting done

                                                countOut=0;         //reset countOut for next time

                                                parityOut=0;//reset parity for next time

                                }

                }

 GIFR=0;               

}

 

//used to make CLK low for at least 100uSec so can send stuff to keyboard

interrupt [TIM1_COMPA] timer1_compA(void)

{

                TIMSK=TIMSK&0xEF;                      //set bit4=0 to turn off interrupt

                TCCR1B=0x00;     //turn off timer (saves power)

                DDRD=0x80;                         //make CLK an input, keep DAT as output

                wCLK=1;                                               //write 1 to B.2 so pullup turn on

                wDAT=0;                                              //write 0 to DAT, this is the start bit...kybd will start generating a clock any moment now

}  

 

//if this executes, then it has been at least 200uSec between clk pulses while receiving or transmitting data to keyboard

interrupt [TIM0_COMP] timer0_comp(void)

{

                TIMSK=TIMSK&0xFD;     //set bit1=0 to turn off timer0 comp match interrupt

                TCCR0=0x00;        //turn off timer0

                TIFR=TIFR&0xFD;              //set bit1=0 to clear timer0 comp match flag (just in case its set)

               

                countIn=0;            //reset counters

                countOut=0;

               

                parityIn=0;            //reset parity calculations

                parityOut=0;   

               

                txDone=TRUE;     //there was some error, but pretend you are done

                rxDone=TRUE;                                                   

               

                receive=TRUE;     //put system into receive mode (just a default option)

                wDAT=1;              //turn on pull ups

                wCLK=1;                          

                DDRD=0x00;         //set DAT and CLK to input

}

 

//used to make sure we send something approximately every 25msec

interrupt [TIM2_OVF] timer2_overflow(void)

{

                t2visits++;

                //after visit this interrupt without reseting 6 times, then about 25msec

                //elapsed since last time we sent something

                if (t2visits==15)

                {

                                //send some stuff for AGC

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xAA);

                                queuePut(0xFF);

                                queuePut(0xAA);

                               

                                t2visits=0;//reset number of visits

                }

 

}             

               

               

//initialize the mcu

void initialize(void)

{

 

                DDRD=0x00;         //make PORTD pin D.3 and D.7 input for CLK and DAT

                PORTD=0x88;       //write 1 to B.7 and B.3 so pullups on for wCLK and wDAT

                MCUCR=0x08;      //int1 triggered on falling edge

                GICR=0x80;           //int1 enable

 

                //Timer0 set up.  The rest will be done inside the external interupt

                OCR0=50; //Timer0 Outoput compare when 50 (decimal)....1/(16Mhz/64)*50=200uSec.

               

                //Timer2 set up.

                TCCR2=0x06;        //prescal by 256

                TIMSK=(TIMSK&0xBF)|0x40; //set bit6=1 to enable overflow interrupt

 

                //UART

                UCSRB=0x08; //enable transmitter only

                UCSRC=0xB6; //Asynchronous mode, 8bit words, 1 stop bit ,odd parity

                UBRRH = (int)300>>8;

                UBRRL = (int)300 & 0xFF ; //need to change this 416 for 2400, 207 for 4800

               

                DDRC=0xff;           //for testing, set portc to output

                PORTC=0xff;         //for testing,set LEDs off 

                DDRA=0xff;          //for testing set porta.0 to output

                PORTA=0x00;       //for testing,set to 0

 

               

                receive=TRUE;     //put int1 interupt into recieve mode               

                countIn=0;            //no low clock pulses seen yet

                countOut=0;                        

                parityIn=0;            //calculated parity at 0

                parityOut=0;

 

                //set up queue

                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

 

                rxDone=FALSE;   //haven't finished receiving

                txDone=FALSE;   //haven't finished transmitting

               

                #asm

                                sei

                #endasm    

}

 

void initializeKyBd(void)

{

                //------------------------  

                dataIn=0;    

                countIn=0;

                RXfromKyBd();    //put into receive mode    

                while(!rxDone)                      //wait until get 0xAA (por sucessfull) from kybd (get it only when turn

                                ;                               //power on and off. Not when you reset microcontroller

                               

                //------------------------ 

                TXtoKyBd(0xFF); //put int tranmsit mode, tell kybd to reset itself

                while(!txDone)      //wait until transmitting done

                                ;

                //------------------------

                RXfromKyBd();    //put into receive mode

                while(!rxDone)      //wait until get 0xFA (ack) from kybd that it got the command

                                ;

                //------------------------            

                RXfromKyBd();    //put into receive mode

                while(!rxDone)      //wait until get 0xAA (reset sucesfull) from kybd

                                ;

               

                //------------------------

                TXtoKyBd(0xF4);                //put into transmit mode, tell kybd to enable all keys (cmd might not be needed)

                while(!txDone)      //wait until transmitting done

                                ;

                //------------------------

                RXfromKyBd();    //put into receive mode

                while(!rxDone)      //wait until get 0xFA (ack) from kybd that it got the command

                                ;

                //------------------------

                //kybd will now remain in receive mode unless we change it  

               

 

}

 

//set interrupt to be in receive mode

void RXfromKyBd(void)

{

                receive=TRUE;     //set int2 to receive mode

                rxDone=FALSE;   //reset

                wDAT=1;                              //pull ups on

                wCLK=1;                               //pull ups on

                DDRD=0x00;         //set DAT and CLK to input

                countIn=0;

}

 

//sets interrupt to be in transmit mode

void TXtoKyBd(char d)

{   

                receive=FALSE;   //set int2 into transmit mode

                txDone=FALSE;   //reset

                dataOut=d;            //the data to transmit

                DDRD=0x88;         //set DAT and CLK to output           

                wDAT=1;                              //keey DAT high

                wCLK=0;                               //pull CLK low to signal that you want to communicate.  Int2 will trigger right away

}

 

 

void main(void)

{             

                initialize();              //initialize mcu

                initializeKyBd(); //initialize keyboard

               

                while(1)

                {             

                                if((!queueEmpty) && (UCSRA&0x20)) //if queue not empty and UART is available

                                {             

                                                t2visits=0;             //reset timer2 count value

                                                TCNT2=0;             //reset timer2

                                                TIFR=TIFR&0xBF;              //set bit6=0 to reset overflow flag (just in case its set)

                                                UDR=queueGet(); //send data out

                               

                                }

                }

}