/*
Measurement.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 receiveInterval 300 //interval between each SendRFByte call
#define measureInterval 300 //interval between each measurement
#define ZERO_OFFSET 21 //the offset used when converting numeric value into ascii
/*
measureState:
1: measure voltage
2: measure current
3: measure frequency
*/
unsigned char measureState;
unsigned char prevMeasureState;

/*
systemState: 900ms as a complete cycle
0: first 300ms period
1: second 300ms period
2: third 300ms period
*/
unsigned char systemState; //which state the system is in
int RF_buffer[2]; //the receive buffer
unsigned char receiveFlag; //if receive a control signal
/*
RangeState:
1 -- Measure on the low end
2 -- Measure on the middle interval
3 -- Measure on the high end
*/
volatile unsigned char rangeState; //the current range the measurement is taking on. see top for details

volatile unsigned int time1; //counter for periodical RF communication
volatile unsigned int time2; //counter for periodical measurement
unsigned char Ain; //the input from the ADC
float VRefLow; //the calibrated low ADC reference voltage (around 1.1V)
float VRefMed; //the calibrated medium ADC reference voltage (around 2.56V)
float VRefHigh; //the calibrated high ADC reference voltage (around 5V)
float reference; //the current reference voltage used by ADC
float voltage; //the measured voltage
float voltageTemp; //the voltage measured in the current measurement
float current; //the measured resistance
float frequency; //the measured frequency
float measurementResult; //the measurement result
float reference; //the current reference voltage used by ADC
unsigned char integerPart; //the integer part of the measurement result + 48
unsigned char decimalPart; //the decimal part of the measurement result + 48
unsigned char unitPart; //the unit, ie kilo, mili and etc., of the measurement

volatile unsigned int T1capture; //the Timer1 count captured into ICR1 from current capture
volatile unsigned int lastT1capture;//the Timer1 count captured into ICR1 from last capture
volatile unsigned int period; //the count difference between the T1capture and lastT1capture
volatile char tempReg; //temporary store for TCCR1B

//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" //increament the r2
"out __SREG__, r19\n\t" //restore the SREG
"pop r19\n\t" //restore the r19
"reti");
}

//The ISR is in charge of counting the number of rising edges
//shown on the ICP (D.6)
ISR(TIMER1_CAPT_vect) {
T1capture = ICR1; //read in the input capture register
// compute time between captures
period = T1capture - lastT1capture;
lastT1capture = T1capture; //update the last capture time

//adjust the prescaler to adjust the count resolution for better accuracy
if (period < 800 && rangeState==1) {
tempReg = TCCR1B & 0xf8;
TCCR1B = tempReg + 1; //set the Timer1 prescaler to CLK/64
rangeState = 3; //update the range state
}

}

//The ISR is in charge of auto-adjusting the Timer1 prescaler to avoid overflow
//in frequency measurement
ISR(TIMER1_OVF_vect) {
tempReg = TCCR1B & 0xf8;
TCCR1B = tempReg + 3; //set the Timer1 prescaler to CLK/64
rangeState = 1; //update the range state
}

 

//Initialize the settings for voltage measurement
void init_V(void) {
DDRA = 0; //All pins on PORTA set to input with high impedance
rangeState = 3; //Set the measurement range to the highest range
ADCSRA = (1<<ADEN) + 7; //Turn on the ADC. Set the ADC prescaler to 128
TIMSK1 = 0; //Disable the Timer0 interrupt used for frequency measurement
}

//Measure the voltage
void measureV(void) {
while(ADCSRA & 1<<ADSC) {};//wait until the current ADC conversion is done
ADMUX = ((1<<ADLAR) | (1<<REFS0)); //Set the left-adjust. Set the input to be PINA.0. Set Vref=AVCC (5V)
reference = VRefHigh; //set V reference for calculation
rangeState = 3; //range state is in default 3
ADCSRA |= (1<<ADSC); //start conversion
while(ADCSRA & 1<<ADSC) {}; //wait until current conversion is done
Ain = ADCH; //read in the value
voltage = (float)Ain / 256.0 * reference; //calculate the voltage

if (voltage < VRefLow) { //if the measured voltage is in the low range
ADMUX = ((1<<ADLAR) | (1<<REFS1)); //Set the left-adjust. Set the input to be PINA.0. Set Vref=1.1V
reference = VRefLow; //update V reference
rangeState = 1; //set the range state to 1
_delay_ms(20); //delay to wait for ADC settle down due to change of Aref
ADCSRA |= (1<<ADSC); //start conversion
while(ADCSRA & 1<<ADSC) {}; //wait until current conversion is done
Ain = ADCH; //read in the value
voltage = (float)Ain / 256.0 * reference; //calculate the voltage
}
else if (voltage < VRefMed) { //if the measured voltage is in the middle range
ADMUX = ((1<<ADLAR) | (1<<REFS0) |(1<<REFS1)); //Set the left-adjust. Set the input to be PINA.0. Set Vref=2.56V
reference = VRefMed; //update the V reference
rangeState = 2; //set the range state to 2
_delay_ms(20); //delay to wait for ADC settle down due to change of Aref
ADCSRA |= (1<<ADSC); //start conversion
while(ADCSRA & 1<<ADSC) {}; //wait until current conversion is done
Ain = ADCH; //read in value
voltage = (float)Ain / 256.0 * reference; //calcualte the voltage
}
}

//Initialize for current measurement
void init_I(void) {
DDRA = 0; //All pins on PORTA set to input with high impedance
rangeState = 3; //Set the measurement range to the highest range
ADCSRA = (1<<ADEN) + 7; //Turn on the ADC. Set the ADC prescaler to 128
TIMSK1 = 0; //Disable the Timer0 interrupt used for F calculation
}

void measureI(void) {
while(ADCSRA & 1<<ADSC) {};//wait until the current ADC conversion is done
ADMUX = ((1<<ADLAR) | (1<<REFS0)); //Set the left-adjust. Set the input to be PINA.0. Set Vref=AVCC (5V)
reference = VRefHigh; //set V reference for calculation
rangeState = 3; //range state is in default 3
ADCSRA |= (1<<ADSC); //start conversion
while(ADCSRA & 1<<ADSC) {}; //wait until current conversion is done
Ain = ADCH; //read in the value
voltageTemp = (float)Ain / 256.0 * reference; //calculate the voltage

if (voltageTemp < VRefLow) { //if the measured voltage is in the low range
ADMUX = ((1<<ADLAR) | (1<<REFS1)); //Set the left-adjust. Set the input to be PINA.0. Set Vref=1.1V
reference = VRefLow; //update V reference
rangeState = 1; //set the range state to 1
_delay_ms(20); //delay to wait for ADC settle down due to change of Aref
ADCSRA |= (1<<ADSC); //start conversion
while(ADCSRA & 1<<ADSC) {}; //wait until current conversion is done
Ain = ADCH; //read in the value
voltageTemp = (float)Ain / 256.0 * reference; //calculate the voltage
}
else if (voltageTemp < VRefMed) { //if the measured voltage is in the middle range
ADMUX = ((1<<ADLAR) | (1<<REFS0) |(1<<REFS1)); //Set the left-adjust. Set the input to be PINA.0. Set Vref=2.56V
reference = VRefMed; //update the V reference
rangeState = 2; //set the range state to 2
_delay_ms(20); //delay to wait for ADC settle down due to change of Aref
ADCSRA |= (1<<ADSC); //start conversion
while(ADCSRA & 1<<ADSC) {}; //wait until current conversion is done
Ain = ADCH; //read in value
voltageTemp = (float)Ain / 256.0 * reference; //calcualte the voltage
}
current = (voltageTemp - 2.5) / 5.1; //calculate the current
}

//Initialize for frequency measurement
void init_F(void) {
DDRD &= ~(1 << PIND6); //Turn PIND.6 into input with high impedance
rangeState = 3; //default range state is 3
TIMSK1 = (1<<ICIE1) + (1<<TOIE1); //enable the input capture interrupt and overflow interrupt
TCCR1B = (1<<ICES1) + 1; //set the input capture trigger to be rising edge. set prescaler to 1
TCNT1 = 0; //intialize Timer1
}

//Measure the frequency
void measureF(void) {
frequency = 16000000.0 / (float)period; //calculate the frequency
if (rangeState == 1) { //if the prescaler is 64
frequency = frequency / 64.0; //do the adjustment
}
}

//Initialization for the system
void init(void) {
//Initialize the port
DDRA = 0;
DDRB = 0;
DDRD = (1 << PIND4); //PIND4 is to drive the RF indicator LED

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

systemState = 1; //currently in the first phase of the cycle

//Initialize system variables
voltage = 0;
voltageTemp = 0;
current = 0;
frequency = 0;
time1 = measureInterval; //schedule next measurement call
measureState = 1; //default measurement is voltage
init_V(); //initialization for voltage measurement

VRefLow = 1.09; //the ARef tuned
VRefMed = 2.54; //the ARef tuned
VRefHigh = 4.83; //the ARef tuned
//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();
}

//First take the measurement based on the current measurement mode.
//Then transmit the measurement result in the rest time of the period.
void task1(void) {
//Take measurements according to system state
if (measureState == 1) {
measureV();
measurementResult = voltage;
}
else if (measureState == 2) {
measureI();
measurementResult = current;
}
else if (measureState == 3){
measureF();
measurementResult = frequency;
}

//fprintf(stdout, "measureState=%d,%f\n\t",measureState,measurementResult);

//Convert the result into the integer, decimal and unit parts
if (measurementResult > 100) {
//fprintf(stdout, "debug=%d\t\n",((long)measurementResult) / 1000);
integerPart = ((long)measurementResult) / 1000 + ZERO_OFFSET;
decimalPart = ((long)measurementResult) % 1000 / 10 + ZERO_OFFSET;
unitPart = 'K';
}
else if (measurementResult < 0.2) {
integerPart = (int)(measurementResult * 1000) + ZERO_OFFSET;
decimalPart = ((int)(measurementResult * 100000)) % 100 + ZERO_OFFSET;
unitPart = 'm';
}
else {
integerPart = (int)measurementResult + ZERO_OFFSET;
decimalPart = ((int)(measurementResult * 100)) % 100 + ZERO_OFFSET;
unitPart = 'N';
}
//Transmit the measurement result until the current period ends.
while (time1 > 0) {
//fprintf(stdout, "int:%d dec:%d unit:%d\n\t",integerPart, decimalPart, unitPart);
SendRFByte(integerPart); //send the integer part
SendRFByte('.'); //send the '.'
SendRFByte(decimalPart); //send the decimal part
SendRFByte(unitPart); //send the order
PostRCVDelay(); //delay for 21.56 baud = 18ms

}
}

//Look for potential incoming mode selection signal and adjust
//the measurement mode accordingly
void task2(void) {
char temp;
receiveFlag = 0; //clear the flag
while (time1 > 0 && !receiveFlag){ //when the 100ms is not up and not receive a valid command yet
PostXmitDelay(); //wait for the antenna to settle down
RF_buffer[0] = ReceiveRFByte(); //receive the first signal
RF_buffer[1] = ReceiveRFByte(); //receive the second signal
PostRCVDelay(); //wait for the measurement unit to settle down
if (RF_buffer[0]>>8) { //if the first try receives signal
temp = (char)(RF_buffer[0] & 0x00ff); //get the data
if (temp == 'V') { //if the received data is V
SendRFByte(temp); //echo back the signal
PostRCVDelay(); //intentiional delay
SendRFByte(temp); //echo back again
receiveFlag = 1; //set the receive flag
measureState = 1; //update the measure state
init_V(); //voltage measurement initialization
}
else if (temp == 'I') { //if the signal is I
SendRFByte(temp); //echo back twice
PostRCVDelay();
SendRFByte(temp);
receiveFlag = 1; //set the flag
measureState = 2; //update the measurement state
init_I(); //current initialization
}
else if (temp == 'F') { //if the signal is F
SendRFByte(temp); //echo back the signal twice
PostRCVDelay();
SendRFByte(temp);
receiveFlag = 1; //set the receive flag
measureState = 3; //update the measurement state
init_F(); //frequency initialization
}
}
if ((RF_buffer[1]>>8) && !receiveFlag) { //if the second try receives the signal and the first try doesnot
temp = (char)(RF_buffer[1] & 0x00ff); //get the data
if (temp == 'V') { //same thing as above
SendRFByte(temp);
PostRCVDelay();
SendRFByte(temp);
receiveFlag = 1;
measureState = 1;
init_V();
}
else if (temp == 'I') {
SendRFByte(temp);
PostRCVDelay();
SendRFByte(temp);
receiveFlag = 1;
measureState = 2;
init_I();
}
else if (temp == 'F') {
SendRFByte(temp);
PostRCVDelay();
SendRFByte(temp);
receiveFlag = 1;
measureState = 3;
init_F();
}
}
}
}

int main(void) {
init(); //initialize the system
while(1) {
if (time1 == 0) {
systemState = (systemState + 1) % 3; //calculate the new system state
if (systemState < 2) { //if the first two states
time1 = measureInterval; //schedule next measurement call
task1(); //call the measurement task
}
else {
time1 = receiveInterval; //schedule next receive call
task2(); //call the receive task
}
}
}
return 1;
}