//ECE 476 Final Project SP06 //Abigail Krich //Self Powered Solar Data Logger #include #include #include #include #include "dflash.h" // program writes to STK500 on board flash // LCD includes and port definition #asm .equ __lcd_port=0x15 // PORT c #endasm #include // LCD driver routines #define LCDwidth 16 // LCD line length = 16 unsigned char lcd_buffer[20]; // Buffer that gets written to LCD // definitions // Debounce states #define Pushed 0 #define NoPush 1 #define MaybeNoPush 2 #define MaybePush 3 #define setYr 0 #define setMo 1 #define setDay 2 #define setHr 3 #define setMin 4 //Display states #define disp_welcome 0 #define disp_logOrDump 1 #define disp_plsSetTime 2 #define disp_setTime 3 #define disp_andSetInterval 4 #define disp_setInterval 5 #define disp_eraseMem 6 #define disp_yStarts 7 #define disp_logging 8 #define disp_logInfo 9 #define disp_stop 10 #define disp_retrieving 12 #define disp_memFull 13 // Task times, all times in ms #define tb0 30 // 30 ms for each button polling routine #define tb1 30 #define tb2 30 #define tb3 30 #define tb4 30 #define tb5 30 #define tb7 30 #define mt 60000 // one minute for minute timer #define lt 5000 // five seconds for log timer #define lcdt 120000 // two minutes for LCD idle timer #define lcdp 30 // poll for any button push every 30 ms #define lcdupd 250 // update LCD every quarter second #define adct 1000 // one second for ADC timer #define Vref 4.93 // measured Vref // functions void initialize(void); // initializes program void normalDisplay(void); // calls the LCD display void pollButton0(void); // set time void pollButton1(void); // set interval void pollButton2(void); // Up, Yes, Start void incrementIt(void); // Routine when button 2 is pushed void pollButton3(void); // Down, No, Stop void decrementIt(void); // Routine when button 3 is pushed void pollButton4(void); // Next void pollButton5(void); // Log Info Display void calcLogInfo(void); // Calculates info for log info display void pollButton7(void); // Upload data void timeUpdate(void); // Update system clock void callADC(void); // Call the ADC to take a reading void getInsolation(void); // Read the insolation value and keep running average void turnLCDonoff(void); // Turn the LCD on/off when system goes to sleep/wakes void storeData(void); // Store data to flash memory void retrieveData(void); // retrieve data from flash memory void puts_int(void); // non-blocking print // global variables // task timers unsigned char timeb0, timeb1, timeb2, timeb3, timeb4, timeb5, timeb7; unsigned int minTime, logTime, lcdTime, lcdPoll, lcdUpdate, ADCtimer, wait; // timers saying how long we've logged and how long we have left to log unsigned char loggedMin, loggedHr, leftLogMin, leftLogHr; unsigned int loggedDay, leftLogDay; // flags unsigned char settingTime, settingInterval, settingMemory, checkingIfReady, r; unsigned char hasTimeBeenSet, hasIntervalBeenSet, hasMemoryBeenChecked, logOrDumpDecisionMade, eraseCheck, readyToStart; unsigned char upHold, downHold, confirmStop, buttonsActive; unsigned char memoryFull, retrievalDone, firstTimeFlag, batDelay; // state machines unsigned char PushState0, PushState1, PushState2, PushState3, PushState4, PushState5, PushState7; unsigned char whichTime,displayState; // other unsigned char yr, hr, min, mo, day; unsigned char interval, logCount; unsigned int insol, insolR, insolRaw, avgInsol, loggedInsol, batRaw, batV; unsigned char yrR, moR, dayR, hrR, minR; unsigned char tmp[2]; //TX empty ISR variables unsigned char t_index; //current string index unsigned char t_buffer[100]; //output string unsigned char t_ready; //flag for transmit done unsigned char t_char; //current character // flash indexes unsigned int page_i; unsigned int flashAddress_i; eeprom unsigned int flashAddress = 0; eeprom unsigned int page = 0; eeprom unsigned int minutesLogged = 0; eeprom unsigned int globalLogCount = 0; eeprom unsigned int logsLeft = 33792; //********************************************************** //timer 0 comp/match ISR, called once per ms, decrements task timers interrupt [TIM0_COMP] void timer0_compmatch(void) { //Decrement the times if they are not already zero if (timeb0>0) --timeb0; if (timeb1>0) --timeb1; if (timeb2>0) --timeb2; if (timeb3>0) --timeb3; if (timeb4>0) --timeb4; if (timeb5>0) --timeb5; if (timeb7>0) --timeb7; if (minTime>0) --minTime; if (logTime>0) --logTime; if (lcdTime>0) --lcdTime; if (lcdPoll>0) --lcdPoll; if (lcdUpdate>0) --lcdUpdate; if (ADCtimer>0) --ADCtimer; if (wait>0) --wait; } //********************************************************** //ADC complete ISR - called after ADC finishes each conversion interrupt [17] void getADC(void) { if (ADMUX == 0b00100000) // when we've been reading the photodiode { insolRaw = ADCH; // raw A to D reading from sensor insol = insolRaw*4; // convert to W/m2 //reset ADMUX to read the other channel (battery) once every 5 seconds batDelay++; if (batDelay == 5) { //channel one for battery, left adj, external aref (CONNECT AREF JUMPER) ADMUX = 0b00100001; batDelay = 0; } } else if (ADMUX == 0b00100001) // when we've been reading the battery voltage { batRaw = ADCH; //take raw reading batV = batRaw*0.0783; //calibrate to read in volts if (batV <=12 && insol > 25) // if the battery is less than or equal to 12V and there is light { PORTD.4 = 1; // reconnect the PV panel } // (otherwise leave it disconnected so we don't overcharge or drain battery) //reset ADMUX to read photodiode next time //channel zero for photodiode, left adj, external aref (CONNECT AREF JUMPER) ADMUX = 0b00100000; } } //********************************************************** void main(void) { initialize(); while(1) { // buttonsActive == 1 only when the LCD is on if (timeb0 == 0) { timeb0 = tb0; // reset task timer if (buttonsActive && logOrDumpDecisionMade && confirmStop == 0) { pollButton0(); // set time } } if (timeb1 == 0) { timeb1 = tb1; // reset task timer if (buttonsActive && logOrDumpDecisionMade && hasTimeBeenSet && confirmStop == 0) { pollButton1(); // set interval } } if (timeb2 == 0) { timeb2 = tb2; // reset task timer if (buttonsActive == 1) { pollButton2(); // up or yes } } if (timeb3 == 0) { timeb3 = tb3; // reset task timer if (buttonsActive == 1 && logOrDumpDecisionMade == 1) { pollButton3(); // down or no } } if (timeb4 == 0) { timeb4 = tb4; // reset task timer if (buttonsActive == 1 && logOrDumpDecisionMade == 1) { pollButton4(); // next } } if (timeb5 == 0) { timeb5 = tb5; // reset task timer if (buttonsActive && readyToStart && settingTime == 0 && settingInterval == 0 && confirmStop == 0) pollButton5(); // show log info display } } if (timeb7 == 0) { timeb7 = tb7; // reset task timer if ((confirmStop == 2 || displayState == disp_logOrDump) && (buttonsActive == 1)) { pollButton7(); // retrieve data } } if (minTime == 0) { minTime=mt; //reset task timer timeUpdate(); // keep clock updating } if (logTime == 0) { logTime = lt; // reset task timer if (readyToStart == 1 && memoryFull == 0) getInsolation(); // log away! } if (lcdPoll == 0) { lcdPoll = lcdp; // reset task timer turnLCDonoff(); // turn LCD on and off } if (lcdUpdate == 0) { lcdUpdate = lcdupd; // reset task timer if (PORTD.5) { normalDisplay(); // update the LCD } } if (ADCtimer == 0) { ADCtimer = adct; // reset task timer if (readyToStart == 1) callADC(); // ADC go! } } // end while } // end main //********************************************************** void initialize(void) { // Initialize ports DDRA = 0x00; // ADC for light sensor, battery voltage on Port A // photodiode to portA.0 (channel 0) // battery voltage to portA.1 (channel 1) // Buttons 4:7 on Port A.4:7 DDRB.0 = 0x00; // Buttons 0:3 on Port B.0:3 DDRB.1 = 0x00; DDRB.2 = 0x00; DDRB.3 = 0x00; // PortB.4:7 used by DFLASH DDRC = 0xFF; // LCD display on Port C DDRD = 0xFF; // jumper from pins D.0 and D.1 to the RS232 jumper // Charging switch On/Off on Port D.4 // LCD On/Off on Port D.5 PORTD.5 = 1; //intialize LCD to on, this pin powers the LCD // Initialize Task Timers timeb0 = tb0; timeb1 = tb1; timeb2 = tb2; timeb3 = tb3; timeb4 = tb4; timeb5 = tb5; minTime = mt; logTime = lt; lcdTime = lcdt; // Initialize log timers loggedMin = 0; loggedHr = 0; leftLogMin = 0; leftLogHr = 0; loggedDay = 0; leftLogDay = 0; r = 0; //initialize flags and such settingTime = 0; settingInterval = 0; settingMemory = 0; checkingIfReady = 0; hasTimeBeenSet = 0; hasIntervalBeenSet = 0; interval = 1; hasMemoryBeenChecked = 0; logOrDumpDecisionMade = 0; readyToStart = 0; confirmStop = 0; upHold = 0; downHold = 0; logCount = 0; memoryFull = 0; retrievalDone = 0; batDelay = 0; buttonsActive = 1; //intitialize state machines PushState0 = NoPush; PushState1 = NoPush; PushState2 = NoPush; PushState3 = NoPush; PushState4 = NoPush; PushState5 = NoPush; whichTime = setYr; //Initialize times yr = 0; mo = 1; day = 1; hr = 0; min = 0; yrR = 99; moR = 99; dayR = 99; hrR = 99; minR = 99; //Initialize insolation insol = 0; avgInsol = 0; insolR = 0; loggedInsol = 0; //set up timer 0 OCR0 = 249; //interrupt runs once per mSec TIMSK = 2; //turn on timer 0 cmp-match ISR TCCR0 = 0b00001011; //prescalar to 64 and Clr-on-match //Enable ADC(bit7), ADC start conversion not set (bit 6), auto trigger off (bit5), // ADC interrupt flag cleared (bit4), disable ADC interrupt flag (bit3), // set prescaler to 1/128*16MHz=125,000 (bits 2:0 = 111) ADCSR = 0b10000111; ADMUX = 0b00100001; // initialize ADC to read channel one for battery, left adj, external aref //initialize flash storage DFlashInit(); //enable serial transmit but not receive UCSRB = 0b00001000 ; //set baud rate to 9600 UBRRL = 103 ; //using a 16 MHz crystal (9600 baud) //initialize pointers to flash storage page_i = 0; flashAddress_i = 0; firstTimeFlag = 0; // Initialize LCD lcd_init(LCDwidth); //initialize the display for 16 characters wide lcd_clear(); //clear the display // Turn on interrupts #asm sei #endasm normalDisplay(); //begin displaying to LCD } //********************************************************** // Normal Display Mode - flags and state machine determine which messages are displayed void normalDisplay(void) { switch (displayState) { case disp_welcome: // Initial welcome message displayed 1 second wait = 1000; lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Hello! Let"); lcd_gotoxy(0,1); lcd_putsf("The Sun Shine!"); while (wait > 0) { } displayState = disp_logOrDump; // Automatically switches to next state displayState = disp_logOrDump; break; case disp_logOrDump: // Displays option to upload or enter setup lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Press Upload or"); lcd_gotoxy(0,1); lcd_putsf("Y to Setup"); break; case disp_plsSetTime: // Ask user to set time lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Please Set Time"); break; case disp_setTime: //display setting time messages switch (whichTime) //whichTime tells which time we're currently setting { case setYr: //Set Year lcd_clear(); //clear the display lcd_gotoxy(0,0); //go to first space lcd_putsf("Please Set Year:"); break; case setMo: //Set Month lcd_clear(); //clear the display lcd_gotoxy(0,0); //go to first space lcd_putsf("Please Set Month:"); break; case setDay: //Set Day lcd_clear(); //clear the display lcd_gotoxy(0,0); //go to first space lcd_putsf("Please Set Day:"); break; case setHr: //Set Hour lcd_clear(); //clear the display lcd_gotoxy(0,0); //go to first space lcd_putsf("Please Set Hour:"); break; case setMin: //Set Min lcd_clear(); //clear the display lcd_gotoxy(0,0); //go to first space lcd_putsf("Please Set Min:"); break; } // end switch // print the full date and time on the second line lcd_gotoxy(0,1); sprintf(lcd_buffer,"%02i:%02i %02i/%02i/%02i",hr,min,mo,day,yr); lcd_puts(lcd_buffer); break; case disp_andSetInterval: //Ask user to set the logging interval lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("And Set Interval"); break; case disp_setInterval: lcd_clear(); //clear the display lcd_gotoxy(0,0); //go to first space lcd_putsf("Interval:"); lcd_gotoxy(10,0); //go to eleventh space sprintf(lcd_buffer,"%02i",interval); lcd_puts(lcd_buffer); //print value in buffer break; case disp_eraseMem: //Display messages to let user erase the memory if (eraseCheck == 0) { lcd_clear(); lcd_gotoxy(0,0); settingMemory = 1; lcd_putsf("Clear Memory Y/N"); } else if (eraseCheck == 1) { lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Confirm Y/N"); } break; case disp_yStarts: //Display message to let user start logging checkingIfReady = 1; lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Y Starts Logging"); break; case disp_logging: //Info displayed while actively logging //print insolation value and battery voltage on line 0 lcd_clear(); lcd_gotoxy(0,0); sprintf(lcd_buffer,"W/M2: %04i %02iV",insol,batV); lcd_puts(lcd_buffer); //print time and date on line 1 lcd_gotoxy(0,1); sprintf(lcd_buffer,"%02i:%02i %02i/%02i/%02i",hr,min,mo,day,yr); lcd_puts(lcd_buffer); break; case disp_logInfo: lcd_clear(); //Displays info about how long logger has been logging lcd_gotoxy(0,0); sprintf(lcd_buffer,"Logd:%02im%02ih%04id",loggedMin,loggedHr,loggedDay); lcd_puts(lcd_buffer); //Displays info about how long logger has left to log before running out of memory lcd_gotoxy(0,1); sprintf(lcd_buffer,"Left:%02im%02ih%04id",leftLogMin,leftLogHr,leftLogDay); lcd_puts(lcd_buffer); break; case disp_stop: //Displays message allowing user to stop logger if (confirmStop == 1) { lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Confirm Stop"); } else if (confirmStop == 2) { wait = 500; lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("System Stopped"); lcd_gotoxy(0,1); lcd_putsf("Restart | Upload"); checkingIfReady = 1; } break; case disp_retrieving: //Displays info to let user know data is uploading if (retrievalDone == 0) { lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Uploading Data"); lcd_gotoxy(0,1); sprintf(lcd_buffer,"%02i:%02i %02i/%02i %04i",hrR,minR,moR,dayR,insolR); lcd_puts(lcd_buffer); } if (retrievalDone == 1) { lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Done Uploading"); lcd_gotoxy(0,1); lcd_putsf("Please Restart"); while (1); // infinite loop. program stops/stalls here until reset } break; case disp_memFull: //Informs user that memory is full lcd_clear(); lcd_gotoxy(0,0); sprintf(lcd_buffer,"W/M2: %04i %02iV",insol,batV); lcd_puts(lcd_buffer); lcd_gotoxy(0,1); lcd_putsf("Memory Full"); break; } //switch } // end task //********************************************************** // Poll and debounce button 0 - Time Set void pollButton0(void) { switch (PushState0) { case NoPush: //if button 0 hasn't been pushed already if (PINB.0 == 0) //if button 0 is pushed during poll PushState0 = MaybePush; else PushState0 = NoPush; break; case MaybePush: if (PINB.0 == 0) { PushState0 = Pushed; if (settingTime == 0 && settingInterval == 0) //if not in the middle of setting time or interval { settingTime = 1; //flag says we're setting the time whichTime = setYr; //start by setting the year displayState = disp_setTime; //Switch display states upHold = 0; //reset counters that detect button holds downHold = 0; } else if (settingTime && hasTimeBeenSet == 1) //if in the middle of setting time and finished setting it { settingTime = 0; //exit time setting state if (hasIntervalBeenSet == 0) //if interval hasn't been set, set interval { displayState = disp_andSetInterval; } else if (hasMemoryBeenChecked == 0) //else if the memory hasn't been checked, check it { displayState = disp_eraseMem; } else if (readyToStart == 0) //if we're not logging, display message that we're ready { displayState = disp_yStarts; } else { displayState = disp_logging; //otherwise continue logging } } } else PushState0 = NoPush; break; case Pushed: if (PINB.0 == 0) PushState0 = Pushed; else PushState0 = MaybeNoPush; break; case MaybeNoPush: if (PINB.0 == 0) PushState0 = Pushed; else PushState0 = NoPush; break; } } //********************************************************** // Poll and debounce button 1 - Interval Set void pollButton1(void) { //same debounce routine as button 0 switch (PushState1) { case NoPush: if (PINB.1 == 0) PushState1 = MaybePush; else PushState1 = NoPush; break; case MaybePush: if (PINB.1 == 0) { PushState1 = Pushed; if (settingTime == 0 && settingInterval == 0) // if not in the middle of setting time or interval { settingInterval = 1; // flag says we're setting the interval upHold = 0; downHold = 0; displayState = disp_setInterval; } else if (settingInterval == 1) // if in the middle of setting interval { settingInterval = 0; // finish setting the interval hasIntervalBeenSet = 1; if (hasMemoryBeenChecked == 0) // if the memory hasn't been checked, check it { settingMemory = 1; displayState = disp_eraseMem; } else if (readyToStart == 0) // if we haven't started, tell user we're ready { displayState = disp_yStarts; } else { displayState = disp_logging; // otherwise return to logging } } } else PushState1 = NoPush; break; case Pushed: if (PINB.1 == 0) PushState1 = Pushed; else PushState1 = MaybeNoPush; break; case MaybeNoPush: if (PINB.1 == 0) PushState1 = Pushed; else PushState1 = NoPush; break; } // end switch } // end task //********************************************************** // Poll and debounce button 2 - Increment/Up button. void pollButton2(void) { //same debounce routine as button 0 //increment counter each time through that button is pushed, reset whenever button released switch (PushState2) { case NoPush: if (PINB.2 == 0) PushState2 = MaybePush; else PushState2 = NoPush; break; case MaybePush: if (PINB.2 == 0) { PushState2 = Pushed; upHold++; incrementIt(); //call this when we first realize the button is pushed } else { PushState2 = NoPush; upHold = 0; } break; case Pushed: if (PINB.2 == 0) { PushState2 = Pushed; upHold++; } else PushState2 = MaybeNoPush; break; case MaybeNoPush: if (PINB.2 == 0) { PushState2 = Pushed; upHold++; } else { PushState2 = NoPush; upHold = 0; } break; } if (upHold == 10) //and call again if the button has been held for 300ms { incrementIt(); } } //********************************************************** // Called from button 2 being pushed or held. void incrementIt(void) { upHold = 0; if (settingTime == 1) //if in the middle of setting time, increment the time { if (whichTime == setYr) { if (yr == 99) yr = 0; else yr++; } if (whichTime == setMo) { if (mo == 12) mo = 0; else mo++; } if (whichTime == setDay) { if (mo == 1 || mo == 3 || mo == 5 || mo == 7 || mo == 8 || mo == 10 || mo == 12) { if (day == 31) day = 1; else day++; } if (mo == 4 || mo == 6 || mo == 9 || mo == 11) { if (day == 30) day = 1; else day++; } if (mo == 2) { if (yr%4 == 0) // check for leap years { if (day == 29) day = 1; else day++; } else { if (day == 28) day = 1; else day++; } } } if (whichTime == setHr) { if (hr == 23) hr = 0; else hr++; } if (whichTime == setMin) { if (min == 59) min = 0; else min++; } } // end if settingTime==1 // otherwise if we're setting the interval, increase it else if (settingInterval == 1) { if (interval == 60) interval = 1; else interval++; } // end if setting interval // go through check to see if we should erase the memory else if (settingMemory == 1 && eraseCheck == 0) { eraseCheck = 1; } // confirm before actually erase memory else if (settingMemory == 1 && eraseCheck == 1) { eraseCheck = 2; settingMemory = 0; hasMemoryBeenChecked = 1; //initialize flash pointer flashAddress = 0; minutesLogged = 0; displayState = disp_yStarts; } // check to see if we're ready to start logging else if (checkingIfReady == 1) { checkingIfReady = 0; readyToStart = 1; confirmStop = 0; displayState = disp_logging; ADCSR.3 = 1; // enable ADC interrupt ADCSR.6=1; // start first ADC conversion } // check to see if we're going to cancel call to stop logging else if (readyToStart == 1 && confirmStop == 1) { confirmStop = 0; displayState = disp_logging; } // check to see if we're going to start by entering setup else if (displayState == disp_logOrDump) { displayState = disp_plsSetTime; logOrDumpDecisionMade = 1; } } // end task //********************************************************** // Poll and debounce button 3 - Decrement/Down button //decrement counter each time through that button is pushed, reset whenever button released void pollButton3(void) { //same debounce routine as button 0 switch (PushState3) { case NoPush: if (PINB.3 == 0) PushState3 = MaybePush; else PushState3 = NoPush; break; case MaybePush: if (PINB.3 == 0) { PushState3 = Pushed; downHold++; decrementIt(); //call when we realize button is pushed } else { PushState3 = NoPush; downHold = 0; } break; case Pushed: if (PINB.3 == 0) { PushState3 = Pushed; downHold++; } else PushState3 = MaybeNoPush; break; case MaybeNoPush: if (PINB.3 == 0) { PushState3 = Pushed; downHold++; } else { PushState3 = NoPush; downHold = 0; } break; } if (downHold == 10) { decrementIt(); //call again when button is held 300ms } } //********************************************************** // called from button 3 being pushed or held void decrementIt(void) { downHold = 0; if (settingTime == 1) //if in the middle of setting time, decrease time { if (whichTime == setYr) { if (yr == 0) yr = 99; else yr--; } if (whichTime == setMo) { if (mo == 1) mo = 12; else mo--; } if (whichTime == setDay) { if (mo == 1 || mo == 3 || mo == 5 || mo == 7 || mo == 8 || mo == 10 || mo == 12) { if (day == 1) day = 31; else day--; } if (mo == 4 || mo == 6 || mo == 9 || mo == 11) { if (day == 10) day = 30; else day--; } if (mo == 2) { if (yr%4 == 0) // check for leap year { if (day == 1) day = 29; else day--; } else { if (day == 1) day = 28; else day--; } } } if (whichTime == setHr) { if (hr == 0) hr = 23; else hr--; } if (whichTime == setMin) { if (min == 0) min = 59; else min--; } } // end if settingTime // if we're setting the interval, decrease the interval else if (settingInterval == 1) { if (interval == 1) interval = 60; else interval--; } // end if setting interval // go through check to see if we should erase the memory else if (settingMemory == 1) { eraseCheck = 0; settingMemory = 0; hasMemoryBeenChecked = 1; logsLeft = 33792; displayState = disp_yStarts; } // go through to see if we should stop logging else if (readyToStart == 1 && confirmStop == 0) //if we're running { confirmStop = 1; displayState = disp_stop; } // confirm before stop logging else if (readyToStart == 1 && confirmStop == 1) //if we're running { confirmStop = 2; readyToStart = 0; } } // end task //********************************************************** // Poll and debounce button 4 - "Next" button void pollButton4(void) { //same debounce routine as button 0 switch (PushState4) { case NoPush: if (PINA.4 == 0) PushState4 = MaybePush; else PushState4 = NoPush; break; case MaybePush: if (PINA.4 == 0) { PushState4 = Pushed; if (settingTime == 1) // if in the middle of setting time { switch (whichTime) { case setYr: whichTime = setMo; break; case setMo: whichTime = setDay; break; case setDay: whichTime = setHr; break; case setHr: whichTime = setMin; hasTimeBeenSet = 1; // we've now successfully set the time and can exit time set routine break; case setMin: whichTime = setYr; break; } // end switch } // end if settingTime } // end if PINC.4==0 else PushState4 = NoPush; break; case Pushed: if (PINA.4 == 0) PushState4 = Pushed; else PushState4 = MaybeNoPush; break; case MaybeNoPush: if (PINA.4 == 0) PushState4 = Pushed; else PushState4 = NoPush; break; } } //********************************************************** // Poll and debounce button 5 - LogInfo Display, tells how long we've logged and how long we can go void pollButton5(void) { //same debounce routine as button 0 switch (PushState5) { case NoPush: if (PINA.5 == 0) PushState5 = MaybePush; else PushState5 = NoPush; break; case MaybePush: if (PINA.5 == 0) { PushState5 = Pushed; calcLogInfo(); // calculate the info on how long we've logged displayState = disp_logInfo; // display the info on how long we've logged } else PushState5 = NoPush; break; case Pushed: if (PINA.5 == 0) PushState5 = Pushed; else PushState5 = MaybeNoPush; break; case MaybeNoPush: if (PINA.5 == 0) PushState5 = Pushed; else PushState5 = NoPush; displayState = disp_logging; break; } } //********************************************************** // Calculate the LogInfo Display void calcLogInfo(void) { // calculate how long logger has been logging loggedMin = (char)(minutesLogged%60); loggedHr = (char)((minutesLogged/60)%24); loggedDay = (unsigned int)(minutesLogged/(60*24)); // calculate how much longer logger can go before running out of memory leftLogMin = (char)(((unsigned int)interval*logsLeft)%60); leftLogHr = (char)((((unsigned int)interval*logsLeft)/60)%24); leftLogDay = (unsigned int)(((unsigned int)interval*logsLeft)/(60*24)); } //********************************************************** // Poll and debounce button 7 - Retrieve Data - (only gets called when confirmStop equals 2 and LCD is on) void pollButton7(void) { //same debounce routine as button 0 switch (PushState7) { case NoPush: if (PINA.7 == 0) PushState7 = MaybePush; else PushState7 = NoPush; break; case MaybePush: if (PINA.7 == 0) { PushState7 = Pushed; if (firstTimeFlag == 0) // if this is the first time button 7 is being pushed { // prevents multiple successive presses from interrupting eachother firstTimeFlag = 1; displayState = disp_retrieving; wait = 250; // wait to make sure we're ready WriteBufferToPageDF(page,0); // write anything in the buffer to the flash while (wait > 0); wait = 250; // wait while buffer is written putsf("\r\nStarting...\r\nSolar Data Logger by Abigail Krich\r\nCornell University ECE 476 Final Project\r\nHr:Min Mo/Day/Yr Insolation(W/m2)\r\n"); while (wait > 0); // wait for header rows to print retrieveData(); // now retrieve the data from flash } } else PushState7 = NoPush; break; case Pushed: if (PINA.7 == 0) PushState7 = Pushed; else PushState7 = MaybeNoPush; break; case MaybeNoPush: if (PINA.7 == 0) PushState7 = Pushed; else PushState7 = NoPush; break; } } //********************************************************** //timeUpdate increment the system time once per minute void timeUpdate(void) { ++min; //increment the min counter if (min == 60) //if min counter at 60min { min = 0; //reset min to 0 and hr++; //increment hr } if (hr==24) //if hr count is at 24 { hr = 0; //reset hr to 0 and day++; //increment day } //check wich month to know when to wrap if ((mo == 1 || mo == 3 || mo == 5 || mo == 7 || mo == 8 || mo == 10 || mo == 12) && day ==32) { day = 1; mo++; } else if ((mo == 4 || mo == 6 || mo == 9 || mo == 11) && day ==31) { day = 1; mo++; } else if (mo == 2) { if (yr%4 == 0) // check for leap year { if (day == 30) { day = 1; mo++; } } else { if (day == 29) { day = 1; mo++; } } } if (mo == 13) //if day count is at 366 { mo = 1; yr++; // increment the eyar } if (yr == 100) // wrap the year yr = 0; } // end task //********************************************************** //enables ADC interrupt to get readings void callADC(void) { if (ADMUX == 0b00100001) //if we're ready to read the battery { PORTD.4 = 0; // disconnect PV panel first to get accurate voltage reading } ADCSR.3 = 1; // enable ADC interrupt ADCSR.6=1; // start a conversion } // end task //********************************************************** //keeps running average of instantaneous insolation readings, //samples instantaneous readings at frequency defined by lt, //calls to store when interval time has elapsed void getInsolation(void) { logCount++; // add one to the log count (# of logs taken since data stored) //this keeps an average of the insolation over the interval avgInsol = (int)(((long)avgInsol*((long)logCount-1) + (long)insol)/(long)logCount); // if we've reached the end of the logging interval if (logCount == (unsigned char)((float)(60000/lt)*interval)) { loggedInsol = avgInsol; storeData(); // store the data to memory logCount = 0; // reset and increment/decrement the log counters globalLogCount++; logsLeft--; // how many minutes we've logged for, must be calculated here in case logging interval is changed later minutesLogged = minutesLogged + interval; } } // end task //********************************************************** // turns LCD off after system has been idle for 2 minutes, turn back on when button 6 is pushed void turnLCDonoff(void) { if (PORTD.5) // if the LCD is on { if ((~(PINB | 0xF0) != 0) || (~(PINA | 0x0F) != 0)) // and if any button is being pushed { lcdTime = lcdt; // reset timer PORTD.5 = 1; // keep LCD on buttonsActive = 1; // keep buttons active } } else if (PORTD.5 == 0 && PINA.6 == 0) // if button 6 is being pushed and the LCD is off { lcdTime = lcdt; // reset timer PORTD.5 = 1; // turn LCD on buttonsActive = 1; // make buttons active again } if (lcdTime == 0) // but if it's been 2 min since a button was pushed { PORTD.5 = 0; // Turn LCD power off buttonsActive = 0; //Stop polling buttons when LCD is off } } //********************************************************** // stores data to flash void storeData(void) { //264 bytes per page (same size as buffer) //we use 8 bytes per store => 33 stores per page //1024 pages => total of 33792 stores //if we log once per minute, this lasts 23.4 days //if we log once per hour, this lasts 3.8 years //store time WriteBufferDF((flashAddress<<3), yr, 0); // store values to the buffer WriteBufferDF((flashAddress<<3)+1, mo, 0); WriteBufferDF((flashAddress<<3)+2, day, 0); WriteBufferDF((flashAddress<<3)+3, hr, 0); WriteBufferDF((flashAddress<<3)+4, min, 0); //store insolation value memcpy(tmp,&loggedInsol, 2); WriteBufferDF((flashAddress<<3)+5, tmp[0], 0); WriteBufferDF((flashAddress<<3)+6, tmp[1], 0); flashAddress++; if (flashAddress == 33) { wait = 250; WriteBufferToPageDF(page,0); // write the data in the buffer to the page while (wait > 0); // wait while buffer writes flashAddress = 0; page++; // go to next page if (page ==1024) { memoryFull = 1; displayState = disp_memFull; // check and display if memory is full } } } //********************************************************** // retrievs data from flash to PC using Hyperterm, called once per logged interval // Use Hyperterm, set to 9600 baud, no parity, one stop bit, and no flow control // The STK500 board requires a jumper from port pins D0 and D1 to the RS232-spare header // UCSRB Bit 5 enables the buffer empty interrupt, bit 4 enables the receiver, and bit 3 the transmitter. void retrieveData(void) { // if we've reached the end of the stored data, print footer and stop retrieval if (page_i == page && flashAddress_i == flashAddress) { putsf("\r\nEnd of Data\r\n '\0'"); retrievalDone = 1; } // otherwise continue with retrieval by reading the data from the flash else { yrR = ReadFlashDF(page_i, (flashAddress_i<<3)); moR = ReadFlashDF(page_i, (flashAddress_i<<3)+1); dayR = ReadFlashDF(page_i, (flashAddress_i<<3)+2); hrR = ReadFlashDF(page_i, (flashAddress_i<<3)+3); minR = ReadFlashDF(page_i, (flashAddress_i<<3)+4); tmp[0] = ReadFlashDF(page_i, (flashAddress_i<<3)+5); tmp[1] = ReadFlashDF(page_i, (flashAddress_i<<3)+6); memcpy(&insolR, tmp,2); // print the read data using the non-blocking print routine sprintf(t_buffer,"%02i:%02i %02i/%02i/%02i %04i\r\n",hrR,minR,moR,dayR,yrR,insolR); puts_int(); //increase the pointer flashAddress_i++; if (flashAddress_i == 33) { flashAddress_i = 0; page_i++; if (page_i == 1024) // if we've reached the end of the data { putsf("\r\nEnd of Data\r\n '\0'"); // finish up retrievalDone = 1; } // end if page } // end if flashAddress } // end else } //********************************************************** // -- nonblocking print adapted from code by Bruce Land: initializes ISR-driven // transmit. This routine merely sets up the ISR, then //send one character, The ISR does all the work. void puts_int(void) { t_ready=0; t_index=0; if (t_buffer[0]>0) //if we've got something in the buffer { putchar(t_buffer[0]); //print the first character UCSRB.5=1; //turn interrupt on } } //********************************************************** //UART xmit-empty ISR interrupt [USART_DRE] void uart_send(void) { t_char = t_buffer[++t_index]; if (t_char == 0) { UCSRB.5=0; //kill isr t_ready=1; //transmit done retrieveData(); } else UDR = t_char ; //send the char }