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

 *    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);

        } 

      }

   }  

}