// File Name : ee281proj.c
//
// Title : LCD Desk Clock
// Revision : 1.0
// Notes :
// Target MCU : Atmel AVR series
// Editor Tabs : 4
//
// Revision History:
// When Who Description of change
// ----------- ----------- -----------------------
// 14-Oct-2002 pstang Created the program
// 10-Nov-2002 gangxie@stanford.edu
//*****************************************************************************
//----- Include Files ---------------------------------------------------------
#include // include I/O definitions (port names, pin names, etc)
#include // include "signal" names (interrupt names)
#include // include interrupt support
#include
#include "global.h" // include our global settings
#include "timer.h" // include timer function library
#include "lcd.h"
//----- Functions -------------------------------------------------------------
void InitSysState(void);
void InitIOPins(void);
void BottonOneClick(void);
void BottonTwoClick(void);
void BottonIncClick(void);
void BottonDecClick(void);
void timer0OutForButtonClick(void);
void UpdateFieldEdited (void);
void ChangeFieldValue(u08 isInc);
u08 AdjustOnoff(u08 val);
void Timer1CmpA_Isr(void);
void StopPumping(void);
void TurnToNextPage(void);
void PageTimeout(void);
void ChangeSpeakerFreq(void);
u08 NeedSpeakerWork(void);
//----- Global Variables ------------------------------------------------------
//The number of editable fields in each LCD page
static char __attribute__ ((progmem)) NumOfFieldsInPage[] = {7, 4, 2, 2, 3, 6};
RtcTimeType gRtcTime;
RtcTimeType gAlarmTime;
PumpInfoType gPumpInfo;
SysStateType gSysState;
//----- Begin Code ------------------------------------------------------------
int main(void)
{
u08 temp;
InitIOPins(); //Iitialized first for security
timerInit(); //Initialize the timer systems
InitializeRtc(); //Initialize clock
InitializeAlarm(); //Initialize alarm
InitializePump(); //Initialize pump
InitSysState();
lcdInit();
lcdInitDisp();
SetupTimer0(); //timer0 for switches/buttons
SetupTimer1(); //Initialize Timer1 for speaker
while (1)
{
temp = inp(SW_PORTI); //read SW
if ((~temp)&SW_BTNONE)
BottonOneClick();
if ((~temp)&SW_BTNTWO)
BottonTwoClick();
if ((~temp)&SW_BTNINC)
BottonIncClick();
if ((~temp)&SW_BTNDEC)
BottonDecClick();
UpdateFieldEdited(); //LCD
PageTimeout();
lcdUpdateDisp();
ChangeSpeakerFreq(); //Speaker
}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void InitSysState(void)
{
gSysState.pumpActivated = 0;
gSysState.pumpSoundOn = 0;
gSysState.alarmOn = 0;
gSysState.alarmCount = 0;
gSysState.displayPage = kPage0_Norm;
gSysState.displayState = kState_View;
gSysState.displayTimeout= 0;
gSysState.displayPrevPage=kPage0_Norm;
gSysState.bntOneCycCnt = 0;
gSysState.bntTwoCycCnt = 0;
gSysState.bntIncCycCnt = 0;
gSysState.bntDecCycCnt = 0;
gSysState.update_rtc_year = 0;
gSysState.update_rtc_month = 0;
gSysState.update_rtc_day = 0;
gSysState.update_rtc_sunsat = 0;
gSysState.update_rtc_hour = 0;
gSysState.update_rtc_minute = 0;
gSysState.update_rtc_second = 0;
gSysState.update_info = 0;
gSysState.update_info_num = 0;
gSysState.update_alm_hour = 0;
gSysState.update_alm_minute = 0;
gSysState.update_alm_second = 0;
gSysState.update_alm_sound = 0;
gSysState.update_pa_active = 0;
gSysState.update_pa_sound = 0;
gSysState.update_pd_minute = 0;
gSysState.update_pd_second = 0;
gSysState.update_pf_day = 0;
gSysState.update_pf_hour = 0;
gSysState.update_pf_minute = 0;
gSysState.update_pn_year = 0;
gSysState.update_pn_month = 0;
gSysState.update_pn_day = 0;
gSysState.update_pn_hour = 0;
gSysState.update_pn_minute = 0;
gSysState.update_pn_second = 0;
gSysState.fieldAdjusted = 0;
gSysState.spkPosVolt = 1;
gSysState.spkNoteNum = 0;
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void InitIOPins(void)
{
//initialize SW
outp(0x00, SW_PORTR); //input
outp(0xff, SW_PORTI);
//initialize SPK
outp(0xff, SPK_PORTR); //output
outp(0x00, SPK_PORTO); //this line is very important for the pump
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void StopPumping(void)
{
cbi(SPK_PORTO, PUMP_POSPIN); //clear 0=>stop bumping
CalcNextPumpTime();
gPumpInfo.duration.year = 0;
gSysState.update_info = 1;
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void TurnToNextPage(void)
{
gSysState.displayPage ++;
if (gSysState.displayPage > kPage5_PumpNext)
gSysState.displayPage = kPage0_Norm;
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void BottonOneClick(void)
{
if (gSysState.alarmCount > 0)
{
gSysState.alarmCount = 0;
gSysState.bntOneCycCnt = kIgnoreClickDuration;
return;
}
if (gPumpInfo.duration.year > 0)
{
StopPumping();
gSysState.bntOneCycCnt = kIgnoreClickDuration;
return;
}
gSysState.displayTimeout = kPageViewTimeout;
if (gSysState.bntOneCycCnt == 0)
{
if (gSysState.displayState == kState_View)
{
gSysState.displayPrevPage = gSysState.displayPage;
TurnToNextPage();
}
else
gSysState.fieldAdjusted ++;
gSysState.bntOneCycCnt = kIgnoreClickDuration;
}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void BottonTwoClick(void)
{
if (gSysState.alarmCount > 0)
{
gSysState.alarmCount = 0;
gSysState.bntTwoCycCnt = kIgnoreClickDuration;
return;
}
if (gPumpInfo.duration.year > 0)
{
StopPumping();
gSysState.bntTwoCycCnt = kIgnoreClickDuration;
return;
}
gSysState.displayTimeout = kPageViewTimeout;
if (gSysState.bntTwoCycCnt == 0)
{
if (gSysState.displayState == kState_View) //view->edit
{
gSysState.displayState = kState_Edit;
gSysState.fieldAdjusted = 0;
}
else //edit->view
{
gSysState.displayState = kState_View;
lcdStopBlink();
CalcNextPumpTime();
}
gSysState.bntTwoCycCnt = kIgnoreClickDuration;
}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void BottonIncClick(void)
{
if (gSysState.alarmCount > 0)
{
gSysState.alarmCount = 0;
gSysState.bntIncCycCnt = kIgnoreClickDuration;
return;
}
if (gPumpInfo.duration.year > 0)
{
StopPumping();
gSysState.bntIncCycCnt = kIgnoreClickDuration;
return;
}
gSysState.displayTimeout = kPageViewTimeout;
if (gSysState.bntIncCycCnt == 0)
{
if (gSysState.displayState==kState_Edit)
ChangeFieldValue(1);
gSysState.bntIncCycCnt = kIgnoreClickDuration;
}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void BottonDecClick(void)
{
if (gSysState.alarmCount > 0)
{
gSysState.alarmCount = 0;
gSysState.bntDecCycCnt = kIgnoreClickDuration;
return;
}
if (gPumpInfo.duration.year > 0)
{
StopPumping();
gSysState.bntDecCycCnt = kIgnoreClickDuration;
return;
}
gSysState.displayTimeout = kPageViewTimeout;
if (gSysState.bntDecCycCnt == 0)
{
if (gSysState.displayState==kState_Edit)
ChangeFieldValue(0);
gSysState.bntDecCycCnt = kIgnoreClickDuration;
}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void ChangeFieldValue(u08 isInc)
{
u08 aid, tmp;
//on/off/active/inactive
switch (gSysState.displayPage)
{
case kPage0_Norm:
switch (gSysState.fieldAdjusted)
{
case 0:
gRtcTime.month = Adjust2DgtNumber(gRtcTime.month, isInc, 1, 12);
gSysState.update_rtc_month = 1;
break;
case 1:
tmp = CalcDaysInMonth(gRtcTime.month, gRtcTime.year);
gRtcTime.day = Adjust2DgtNumber(gRtcTime.day, isInc, 1, tmp);
gSysState.update_rtc_day = 1;
break;
case 2:
gRtcTime.year = AdjustYear(gRtcTime.year, isInc);
gSysState.update_rtc_year = 1;
break;
case 3:
gRtcTime.sunsat = Adjust2DgtNumber(gRtcTime.sunsat, isInc, 0, 6);
gSysState.update_rtc_sunsat = 1;
break;
case 4:
gRtcTime.hour = Adjust2DgtNumber(gRtcTime.hour, isInc, 0, 23);
gSysState.update_rtc_hour = 1;
break;
case 5:
gRtcTime.minute = Adjust2DgtNumber(gRtcTime.minute, isInc, 0, 59);
gSysState.update_rtc_minute = 1;
break;
case 6:
gRtcTime.second = Adjust2DgtNumber(gRtcTime.second, isInc, 0, 59);
gSysState.update_rtc_second = 1;
break;
}
break;
case kPage1_Alarm:
switch (gSysState.fieldAdjusted)
{
case 0:
gAlarmTime.hour = Adjust2DgtNumber(gAlarmTime.hour, isInc, 0, 23);
gSysState.update_alm_hour = 1;
break;
case 1:
gAlarmTime.minute = Adjust2DgtNumber(gAlarmTime.minute, isInc, 0, 59);
gSysState.update_alm_minute = 1;
break;
case 2:
gAlarmTime.second = Adjust2DgtNumber(gAlarmTime.second, isInc, 0, 59);
gSysState.update_alm_second = 1;
break;
case 3:
gSysState.alarmOn = AdjustOnoff(gSysState.alarmOn);
gSysState.update_alm_sound = 1;
break;
}
break;
case kPage2_PumpActive:
switch (gSysState.fieldAdjusted)
{
case 0:
gSysState.pumpActivated = AdjustOnoff(gSysState.pumpActivated);
gSysState.update_pa_active = 1;
break;
case 1:
gSysState.pumpSoundOn = AdjustOnoff(gSysState.pumpSoundOn);
gSysState.update_pa_sound = 1;
break;
}
break;
case kPage3_PumpDuration:
switch (gSysState.fieldAdjusted)
{
case 0:
aid = gPumpInfo.duration.minute;
tmp = Adjust2DgtNumber(aid, isInc, 0, 99);
gPumpInfo.duration.minute = tmp;
gSysState.update_pd_minute = 1;
break;
case 1:
aid = gPumpInfo.duration.second;
tmp = Adjust2DgtNumber(aid, isInc, 0, 59);
gPumpInfo.duration.second = tmp;
gSysState.update_pd_second = 1;
break;
}
break;
case kPage4_PumpFreq:
switch (gSysState.fieldAdjusted)
{
case 0:
aid = gPumpInfo.frequency.day;
tmp = Adjust2DgtNumber(aid, isInc, 0, 99);
gPumpInfo.frequency.day = tmp;
gSysState.update_pf_day = 1;
break;
case 1:
aid = gPumpInfo.frequency.hour;
tmp = Adjust2DgtNumber(aid, isInc, 0, 23);
gPumpInfo.frequency.hour = tmp;
gSysState.update_pf_hour = 1;
break;
case 2:
aid = gPumpInfo.frequency.minute;
tmp = Adjust2DgtNumber(aid, isInc, 0, 59);
gPumpInfo.frequency.minute = tmp;
gSysState.update_pf_minute = 1;
break;
}
break;
case kPage5_PumpNext:
switch (gSysState.fieldAdjusted)
{
case 0:
aid = gPumpInfo.next.hour;
tmp = Adjust2DgtNumber(aid, isInc, 0, 23);
gPumpInfo.next.hour = tmp;
gSysState.update_pn_hour = 1;
break;
case 1:
aid = gPumpInfo.next.minute;
tmp = Adjust2DgtNumber(aid, isInc, 0, 59);
gPumpInfo.next.minute = tmp;
gSysState.update_pn_minute = 1;
break;
case 2:
aid = gPumpInfo.next.second;
tmp = Adjust2DgtNumber(aid, isInc, 0, 59);
gPumpInfo.next.second = tmp;
gSysState.update_pn_second = 1;
break;
case 3:
aid = gPumpInfo.next.month;
tmp = Adjust2DgtNumber(aid, isInc, 1, 12);
gPumpInfo.next.month = tmp;
gSysState.update_pn_month = 1;
break;
case 4:
aid = gPumpInfo.next.day;
tmp = CalcDaysInMonth(gPumpInfo.next.month, gPumpInfo.next.year);
gPumpInfo.next.day = Adjust2DgtNumber(aid, isInc, 1, tmp);
gSysState.update_pn_day = 1;
break;
case 5:
gPumpInfo.next.year = AdjustYear(gPumpInfo.next.year, isInc);
gSysState.update_pn_year = 1;
break;
}
break;
}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
u08 AdjustOnoff(u08 val)
{
if (val == 0)
return 1;
return 0;
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void UpdateFieldEdited (void)
{
u08 tot;
if (gSysState.displayState == kState_Edit)
{
tot = PRG_RDB(&NumOfFieldsInPage[gSysState.displayPage]);
if (gSysState.fieldAdjusted >= tot)
{
gSysState.fieldAdjusted -= tot;
TurnToNextPage();
}
}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
void PageTimeout(void)
{
if (gSysState.displayTimeout == 0)
{
if (gSysState.displayState == kState_Edit)
{
lcdStopBlink();
gSysState.displayState= kState_View;
CalcNextPumpTime();
}
if (gSysState.displayPage != kPage0_Norm)
{
gSysState.displayPrevPage = gSysState.displayPage;
gSysState.displayPage = kPage0_Norm;
}
}
}
//---------------------------------------------------------------------------------
// In order to filter out noise during pushbutton clicks
//---------------------------------------------------------------------------------
void timer0OutForButtonClick(void)
{
if (gSysState.bntOneCycCnt > 0)
gSysState.bntOneCycCnt--;
if (gSysState.bntTwoCycCnt > 0)
gSysState.bntTwoCycCnt--;
if (gSysState.bntIncCycCnt > 0)
gSysState.bntIncCycCnt--;
if (gSysState.bntDecCycCnt > 0)
gSysState.bntDecCycCnt--;
}
//---------------------------------------------------------------------------------
// Timer1CmpA_Isr()
// Generate waves for speaker at 2000 Hz
//---------------------------------------------------------------------------------
void Timer1CmpA_Isr(void)
{
if (NeedSpeakerWork()==0)
{
cbi(SPK_PORTO, SPK_POSPIN); //clear 0
return;
}
if (gSysState.spkPosVolt)
{
gSysState.spkPosVolt = 0;
sbi(SPK_PORTO, SPK_POSPIN); //set 1
}
else
{
gSysState.spkPosVolt = 1;
cbi(SPK_PORTO, SPK_POSPIN); //clear 0
}
}
//---------------------------------------------------------------------------------
// Note Frequency
// A5 880
// B5 987
// C6 1046
// D6 1174
// E6 1318
// F6 1397
// G6 1568
// A6 1760
//---------------------------------------------------------------------------------
void ChangeSpeakerFreq(void)
{
if (NeedSpeakerWork())
{
switch (gSysState.spkNoteNum)
{
// Freq Note
case 0: outp(0x02, OCR1AH); outp(0x0B, OCR1AL); break; //3686000/8/523 = 880 =>A4
case 1: outp(0x01, OCR1AH); outp(0xD2, OCR1AL); break; //3686000/8/466 = 987 =>B4
case 2: outp(0x01, OCR1AH); outp(0xB8, OCR1AL); break; //3686000/8/440 = 1046 =>B4
case 3: outp(0x01, OCR1AH); outp(0x88, OCR1AL); break; //3686000/8/392 = 1174 =>A4
case 4: outp(0x01, OCR1AH); outp(0x5D, OCR1AL); break; //3686000/8/349 = 1318 =>B4
case 5: outp(0x01, OCR1AH); outp(0x49, OCR1AL); break; //3686000/8/329 = 1397 =>B4
case 6: outp(0x01, OCR1AH); outp(0x25, OCR1AL); break; //3686000/8/293 = 1568 =>B4
default: outp(0x01, OCR1AH); outp(0x05, OCR1AL); break; //3686000/8/261 = 1760 =>B4
}
}
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
u08 NeedSpeakerWork(void)
{
if (((gSysState.alarmOn==1) &&
(gSysState.alarmCount>0)) ||
((gSysState.pumpActivated==1) &&
(gSysState.pumpSoundOn==1) &&
(gPumpInfo.duration.year>0)))
return 1;
return 0;
}