/*
Base.c
Code by Xiaofeng Gu (xg46@cornell.edu), Jia Yang (jy363@cornell.edu)
ECE 4760 Final Project
Spring 2012
*/

#include<inttypes.h>
#include<avr/io.h>
#include<avr/interrupt.h>
#include<stdio.h>
#include<stdlib.h>
#include<util/delay.h>
#include<string.h>
#include<avr/pgmspace.h>
#include "lcd_lib.h"
#include "uart.h"

// set up serial for debugging
FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);

//The declaration of the functions implemented in RF.S
extern void PostXmitDelay(void);
extern void PostRCVDelay(void);
extern int ReceiveRFByte(void);
extern void SendRFByte(char data);

#define RFInterval 400 //interval between each ReceiveRFByte call
#define LCDInterval 400 //interval between each LCD update
#define ZERO_OFFSET 21 //the offset used when converting numeric value into ascii

unsigned int failReceiveCount; //number of receive call tried with nothing received
/*
measureState:
1: measure voltage
2: measure current
3: measure frequency
*/
unsigned char measureState; //current measurement mode
unsigned char prevMeasureState; //previous measurement mode
unsigned char measureChangeFlag;//if a measurement mode change happens
unsigned char receiveFlag; //if receives signal
/*
Transmit:
'V' voltage
'I' current
'F' frequency

Receive:
'0' - '9', '.', 'K'
*/

int RF_buffer[8]; //the information returned by the ReceiveRFByte
char RFSend; //the information to be sent

volatile unsigned int time1; //counter for periodical ReceiveRFByte call
volatile unsigned int time2; //counter for periodical LCD update

int8_t lcd_buffer[17]; //the LCD buffer
int8_t lcd_temp[17]; //the temporary LCD buffer
int8_t integer_buffer[5]; //the buffer to hold the integer part of the data
int8_t decimal_buffer[5]; //the buffer to hold the decimal part of the data

//LCD display template
const int8_t LCD_V[] PROGMEM = "V= \0";
const int8_t LCD_I[] PROGMEM = "I= \0";
const int8_t LCD_F[] PROGMEM = "F= \0";
const int8_t LCD_NC[] PROGMEM = "No Connection.\0";
const int8_t LCD_Blank[] PROGMEM = " \0";

//The Timer2 compare match with OCR2A. Keep track of the 1ms time base
ISR (TIMER2_COMPA_vect) {
if (time1 > 0) --time1;
if (time2 > 0) --time2;
}

//The ISR work with RF.S to keep track of RF timing based on
//the initial value of the Timer0
ISR(TIMER0_OVF_vect, ISR_NAKED) {
asm("pop r18\n\t" //pop out the last return address
"pop r18\n\t"
"reti"); //return using the second to last address
}

//The ISR work with RF.S to count the number of rising edges
ISR(ANALOG_COMP_vect, ISR_NAKED) {
asm("push r19\n\t" //store the r19 register
"in r19, __SREG__\n\t" //store the SREG in r19
"inc r2\n\t" //increamenet r2
"out __SREG__, r19\n\t" //restore the SREG
"pop r19\n\t" //restore the r19
"reti");
}

//The initialization code for the system
void init(void) {
//Set up the PORT
DDRB = 0x00; //All pins on PORTB set to input w/ high impedance
PORTB = 0x30; //Turn on PINB.4 and PINB.5 pull-up resistor

//Initialize the LCD
LCDinit(); //initialize the display
LCDcursorOFF();
LCDclr(); //clear the display

//init the UART -- uart_init() is in uart.c
uart_init();
stdout = stdin = stderr = &uart_str;
fprintf(stdout,"Starting...Base Unit\n\r");

//Initialize the system state variables
time1 = RFInterval; //schedule next RF receive call
time2 = LCDInterval; //schedule next LCD update
measureState = 1; //the default state is to measure voltage
prevMeasureState = measureState;
measureChangeFlag = 1; //set the flag to get the template displayed
failReceiveCount = 0; //clear the fail counter

//Set up the Timer2 to keep track of the 1ms time base
TCNT2 = 0;
OCR2A = 249;
TIMSK2 = (1 << OCIE2A) ; //enable the output compare match A interrupt
TCCR2B = 4; //set the prescaler=64
TCCR2A = (1 << WGM21); //set to the CTC mode

sei(); //turn on interrupts
}

/*
When the switch is open, it should read a high. When it is closed, it reads a low.
switch 1(B.5) and switch 0(B.4):
HH: measure V
HL: measure I
LL: measure F
LH: Not defined. Switch to this state will have no impact
*/
void switchDetect(void) {
char temp1 = (PINB & 0x10); //the read from switch0
char temp2 = (PINB & 0x20); //the read from switch1
//fprintf(stdout, "sw0=%d sw1=%d\n\t", temp1, temp2);
if (temp1 && temp2) { //if it is HH
measureState = 1; //measure voltage
}
else if (temp2 && (!temp1)) { //if it is HL
measureState = 2; //measure current
}
else if ((!temp2) && (!temp1)) {//if it is LL
measureState = 3; //measure frequency
}
}

//Task1 is in charge of looking for measurement data, and process the data
//if receive any valid data.
void task1(void) {
char temp1;
int temp3, i;
char foundPeriod = 0; //indicate a valid data package is found
receiveFlag = 0; //clear the flag

while (time1 > 50 && !foundPeriod) {
RF_buffer[0] = ReceiveRFByte(); //look for incoming signal
//fprintf(stdout, "rcv:%d\n\t",RF_buffer[0]);
if ((RF_buffer[0]>>8)) { //if the receive bit is set in the returned data
RF_buffer[1] = ReceiveRFByte(); //receive the second data
RF_buffer[2] = ReceiveRFByte(); //receive the third data
RF_buffer[3] = ReceiveRFByte(); //receive the fourth data
RF_buffer[4] = ReceiveRFByte(); //receive the 5th data
RF_buffer[5] = ReceiveRFByte(); //receive the 6th data
RF_buffer[6] = ReceiveRFByte(); //receive the 7th data
RF_buffer[7] = ReceiveRFByte(); //receive the 8th data
receiveFlag = 1; //set the receive flag
}
if (receiveFlag == 1) {
//find the index of the first appearance of the '.' in the right position range
for (i=1; i<6 && !foundPeriod; i++) {
temp1 = ((RF_buffer[i] & 0x00ff) == 0x2E);
if (temp1) { //it is a comma
foundPeriod = 1; //we find the valid data
}
}
i--; //decrement by 1 to offset the increment at the end of the loop
//all the data have the receive bit set
temp1 = (RF_buffer[i-1]>>8) && (RF_buffer[i]>>8) && (RF_buffer[i+1]>>8) && (RF_buffer[i+2]>>8);

if (temp1 && foundPeriod) { //the receive data is valid
failReceiveCount = 0; //clear the failed connection counter
temp3 = (RF_buffer[i-1] & 0x00ff) - ZERO_OFFSET; //convert the integer-part data into integer
sprintf(integer_buffer, "%d", temp3); //convert it into string
temp3 = (RF_buffer[i+1] & 0x00ff) - ZERO_OFFSET; //convert the decimal-part data into integer
if (temp3 > 10) {
sprintf(decimal_buffer, "%d", temp3); //convert it into string
}
else { //if the decimal is less than 10,
sprintf(decimal_buffer, "0%d", temp3); //append a zero at the beginning
}
strcpy(lcd_temp, integer_buffer); //copy the integer part into the temp buffer
temp3 = strlen(integer_buffer);
lcd_temp[temp3] = '.'; //put the '.' into the temp buffer
strcpy(lcd_temp+temp3+1, decimal_buffer); //copy the decimal part into the temp buffer
temp3 += 1 + strlen(decimal_buffer);
temp1 = (char)(RF_buffer[i+2] & 0x00ff); //convert the number order (K, m) into ascii

if (temp1 != 'N') { //if there is an order
lcd_temp[temp3] = temp1; //put that order at the end of string
lcd_temp[temp3+1] = ' '; //also append several blanks to clear the
lcd_temp[temp3+2] = ' '; //the previous characters left
lcd_temp[temp3+3] = ' ';
lcd_temp[temp3+4] = ' ';
lcd_temp[temp3+5] = ' ';
lcd_temp[temp3+6] = 0;
}
else { //if there is not an order
lcd_temp[temp3] = ' '; //put blanks at the end of string
lcd_temp[temp3+1] = ' ';
lcd_temp[temp3+2] = ' ';
lcd_temp[temp3+3] = ' ';
lcd_temp[temp3+4] = ' ';
lcd_temp[temp3+5] = 0;
}
strcpy(lcd_buffer, lcd_temp); //copy the content from temp buffer into buffer
}
}
else { //if data is received
if (failReceiveCount < 20) { //top counter at 20 to avoid overflow
failReceiveCount++; //increament counter if less than 20
}
}

}
}

//Task2 is in charge of detect switch input and transmit the
//control signal if necessary
void task2(void) {
char temp1, temp2, echoFlag;
switchDetect(); //detect the switch status
if (prevMeasureState != measureState) {
prevMeasureState = measureState;//update previous measurement state
measureChangeFlag = 1; //set the change flag
if (measureState == 1){ //select the control signal to send
RFSend = 'V';
}
else if (measureState == 2) {
RFSend = 'I';
}
else if (measureState == 3){
RFSend = 'F';
}
TIMSK2 = 0; //disable the Timer2 interrupt service routine
echoFlag = 0; //clear the echo flag

while (!echoFlag) { //while echo does not match the control signal
PostRCVDelay(); //wait for the antenna at the measurement unit to settle down
SendRFByte(RFSend); //send the mode selection
PostRCVDelay(); //intentionally to create the delay between two identical transmissions
SendRFByte(RFSend); //send the mode selection again

PostXmitDelay(); //wait for the antenna at the base unit to settle down
RF_buffer[0] = ReceiveRFByte(); //look for the echo back signal
RF_buffer[1] = ReceiveRFByte(); //look for the second echo back signal
temp1 = ((RF_buffer[0] & 0x00ff) == RFSend); //if the first echoback=control signal
temp2 = ((RF_buffer[1] & 0x00ff) == RFSend); //if the second echoback=control signal
if ((RF_buffer[0] >> 8) && temp1) { //if the first echo matches
echoFlag = 1; //set the echo back flag
}
else if ((RF_buffer[1] >> 8) && temp2) { //if the second echo matches
echoFlag = 1; //set the echo flag
}
}
TIMSK2 = (1 << OCIE2A); //turn on the Timer2 interrupt
}
}

//Update the LCD periodically
void updateLCD(void) {
if (measureChangeFlag) { //if there is a change on measurment mode since last LCD update
measureChangeFlag = 0; //clear the flag
//update the display template
if (measureState == 1) {
CopyStringtoLCD(LCD_V, 0, 0);
}
else if (measureState == 2) {
CopyStringtoLCD(LCD_I, 0, 0);
}
else {
CopyStringtoLCD(LCD_F, 0, 0);
}
}
LCDGotoXY(2, 0); //update the measurement result
LCDstring(lcd_buffer, strlen(lcd_buffer));
if (failReceiveCount > 19) { //if too many fail connections
CopyStringtoLCD(LCD_NC,0,1); //display No Connectin message
}
else {
CopyStringtoLCD(LCD_Blank, 0, 1);//clear the No Connection message
}
}

int main(void) {

init(); //initialize the system
updateLCD(); //update the LCD
while(1) {
if (time1 == 0) {
time1 = RFInterval; //reset the time1. schedule next call to task1 and task2
task1(); //look for the incoming signal and update the lcd_buffer accordingly
task2(); //detect the button push
}
if (time2 == 0) {
time2 = LCDInterval;//reset time2. schedule next call to LCD update
updateLCD(); //update LCD
}
}
return 1;
}

/*
ASCII table
'.' 0d46 0x2E
'0' 0d48 0x30
'V' 0d86 0x56
'I' 0d73 0x49
'F' 0d70 0x46
'N' 0d78 0x4E
'K' 0d75 0x4B
'm' 0d109 0x6D
*/