//LAB 7 CODE - Transmitter
//Michael Jardin
//Nick Liu
//Monday, 7:30-10:30 Lab


#include <Mega163.h>

//timeout values for each task
#define t1             20
#define t2             50
#define t3             30

#define left           1               //value to determine left engine
#define right          2               //value to determine right engine
#define voltSegments   17              //number of different voltages for motors
#define off            0x08            //transmission byte indicating hovercraft should be off
#define idle           0x88            //transmission byte indicating hovercraft is idle

//define pushbutton debounce states
#define nopush         0
#define maybepushed    1
#define pushed         2
#define maybenopush    3

//the subroutines
void task1(void);                     //runs receive interrupt at t1 interval
void task2(void);                     //reads in analog input and turns into transmission byte
void task3(void);                     //power on/off button debounce state machine
void initialize(void);                //sets up the mcu
void gets_int(void);                  //nonblocking transmit interrupt scheme
void map(char direction);             //maps values to format used for transmission byte

//define variables
unsigned char reload;                 //timer 0 reload to set 1 mSec
unsigned char time1, time2, time3;    //keeps track of current time of each task
unsigned char r_ready;                //flag for receive done interrupt
unsigned char r_char;                 //current character
int Ain;                               //raw A to D number
unsigned char first;                  //flag used to read in right and left input
unsigned char i;                      //counter
float voltage;                         //analog voltage read in
unsigned char value;                  //temporary value used to construct transmission byte
unsigned char state, pushflag;        //current state of debounce state machine

//Lookup table used to determine what value to set analog voltage
flash float voltsIn[voltSegments]=
    {-0.02, 0.31, 0.62, 0.93, 1.25,
    1.56, 1.86, 2.20, 2.50, 2.81, 3.11,
    3.42, 3.73, 4.05, 4.36, 4.68, 5.05};

//lookup table to determine direction and magnitude of each engine
flash unsigned char packet[16]=
    {15, 14, 13, 12, 11, 10, 9, 8, 8, 1, 2, 3, 4, 5, 6, 7};

//**********************************************************
//timer 0 overflow ISR
interrupt [TIM0_OVF] void timer0_overflow(void)
{
    TCNT0=reload; //reload to force 1 mSec overflow

    if (time1>0) --time1;                 //Decrement the two times if they are not already zero
    if (time2>0) --time2;
    if (time3>0) --time3;
}


interrupt [UART_DRE] void udr_empty(void)
{
    UDR = r_char;                         //transmits a char
    UCSRB.5=0;                            //stop trans ISR
    r_ready=1;                            //ready to start the interrupt again
}


//**********************************************************
//Entry point and task scheduler loop

void main(void)
{
initialize();

while(1){                              //main task scheduler loop -- never exits!
    if (time1==0)
        task1();
    if (r_char!=off)                   //if the hovercraft is on, only then read in engine voltages
        if (time2==0)
            task2();
    if (time3==0)
        task3();
    }
}

//***********************************************************
void map(char direction)
{
    i = 0;
    while (!((voltage >= voltsIn[i]) && (voltage < voltsIn[i+1]) && (i < voltSegments)))
        i++;

    if (i == voltSegments)               //if voltage is not inside the range
        r_char=idle;                     //stop the motor
    else {
        value = packet[i];               //set value to specific magnitude

    if (direction == left) {
        value <<= 4;                     //shift bits 4 spaces to the left
        value += 0x0f;                   //make 1st 4 bits = 1111
        r_char = r_char & 0x0f;          //clear out left four bits
        r_char += 0xf0;                  //make last 4 bits = 1111
        r_char = r_char & value;         //replace left 4 bits with value
        }
    else {
        value += 0xf0;                   //make last 4 bits 1111
        r_char = r_char & 0xf0;          //clear out right four bits
        r_char += 0x0f;                  //make 1st 4 bits = 1111
        r_char = r_char & value;         //replace right 4 bits with value
        }
    }

}

//**********************************************************
void task1(void)                        //used to keep 20 msec base for TX interrupt
{
    time1 = t1;

    if (r_ready)
        gets_int();                     //ready to transmit data
}

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

void task2(void)                        //A/D converter
{
    time2 = t2;

    if(ADCSR.6 == 0 && first){
        Ain = ADCH;
        Ain >>= 2;                       //obtain only 6 bit accuracy
        voltage =(float)Ain;
        voltage=(voltage/64) * 5.0;
        map(left);                       //set up the correct opcode
        ADMUX = 0b00100001;              //need to adjust the first three values for Aref, READ channel 1
        ADCSR.6 = 1;                     //init A to D Conversion
        first = 0;
        }

    if(ADCSR.6 == 0 && first == 0){
        Ain = ADCH;
        Ain >>= 2;
        voltage =(float)Ain;
        voltage=(voltage/64) * 5.0;
        map(right);                     //set up the correct opcode
        ADMUX = 0b00100000;             //need to adjust the first three values for Aref, READ channel 0
        ADCSR.6 = 1;                    //init A to D Conversion
        first = 1;
        }
}

//**********************************************************
void task3(void)                        //debounce for power on/off button
{
    time3=t3;

    switch(state) {
        case nopush:
            if (PINC == 0x01)             //if a button push is detected
                state=maybepushed;        //move to the next state
            else
                state=nopush;
            break;

        case maybepushed:
            if (PINC == 0x01)             //if that same button is still pushed
                state=pushed;             //move to the pushed state
            else
                state=nopush;             //else there is no button push
            break;

        case pushed:
            if (PINC == 0x01){            //if again that button is pushed
                state = pushed;
                pushflag=1;
                }
            else if (pushflag){
                if (r_char==off)          //turn on/off the hovercraft
                    r_char=idle;
                else r_char=off;
                    pushflag=0;
                }
            else
                state=maybenopush;        //else maybe the button wasn't pushed
                    break;

        case maybenopush:
            if (PINC == 0x01)             //if that button is pushed
                state=pushed;             //go back to the pushed state
            else
                state=nopush;             //else there was no button push
            break;
        }
}

//**********************************************************
// -- non-blocking keyboard check initializes ISR-driven
// receive. This routine merely sets up the ISR, which then
//does all the work of getting a command.

void gets_int(void)
{
        r_ready=0;
        UCSRB.5=1;
}


//**********************************************************
//Set it all up

void initialize(void)
{
    //Set up UART communication
    UCSRB =0x08;
    UBRR = 255 ;

    //set up the ports
    DDRD=0xff;                         // PORT D is an output
    DDRC=0x00;                         // PORT C is an input (left control)
    DDRA=0x00;                         // PORT A is an input (right control)
    PORTC=0;
    PORTA=0;

    //set up timer 0
    reload=256-62;                     //value for 1 Msec
    TCNT0=reload;                      //preload timer 1 so that is interrupts after 1 mSec.
    TCCR0=3;                           //prescalar to 64
    TIMSK=1;                           //turn on timer 0 overflow ISR

    //init the task timers
    time1=t1;
    time2=t2;

    //set up A/D converter
    ADMUX = 0b00100000;                 //READ channel 0
    ADCSR = 0b11000110;                 //start conversion

    r_ready=1;                          //ready to start transmitting
    r_char = off;                       //hovercraft is powered off
    state = nopush;

    //crank up the ISRs
    #asm
        sei
    #endasm

}