homework 7
Dependencies: mbed-rtos mbed C12832_lcd LM75B
main.cpp
- Committer:
- gatedClock
- Date:
- 2013-09-12
- Revision:
- 119:e14b0f6e97cb
- Parent:
- 118:d0e057d79acc
- Child:
- 120:64a969984af2
File content as of revision 119:e14b0f6e97cb:
/*----------------------------------------------//------------------------------ student : m-moore class : rtos directory : RTOS_HW_07 file : main.cpp ----description---------------------------------//------------------------------ gotchyas 1. using pc.printf inside a ticker routine will freeze the routine. 2. using queues (get, put) will not work within a ticker routine. 3. Ticker has a bug. http://mbed.org/questions/1563/Mbed-Tickerfunction-hangs-system-from-re/ improvements countdown timer needs to be sync'd to start button press. this could not be done, use granularity setting instead. -----includes-----------------------------------//----------------------------*/ #include "mbed.h" // mbed class. #include "rtos.h" // rtos class. #include "LM75B.h" // thermometer class. #include "C12832_lcd.h" // LCD class. //---defines------------------------------------//------------------------------ #define LCD1 lcd.locate(0, 0); // LCD line 1. #define LCD2 lcd.locate(0,11); // LCD line 2. #define LCD3 lcd.locate(0,22); // LCD line 3. #define DEBOUNCEmS 16 // debounce wait in mS. #define uS_TIMEOUT 100 // Timer uS timeout. #define LBSIG 1 // left button signal code. #define PIPEuS 1000 // pipeline clock period. #define SLOWCLOCKuS 500000 // slow-clock period. #define TEMPCLOCKS 1 // temperature-measuring period in S. #define PIPEDATASIZE 8 // dimension of tPipeData. #define THREAD_1_WAITmS 400 // thread 1 wait in mS. #define THREAD_2_WAITmS 40 // LCD thread wait. #define THREAD_3_WAITmS 80 // thread 3 wait in mS. #define THREAD_4_WAITmS 1 // thread 4 wait in mS. #define THREAD_5_WAITmS 100 // FSM thread wait. #define HB_MODULO 1024 // heartbeat modulo divisor. #define MSG_INC_TIME 0x01 #define MSG_DEC_TIME 0x02 #define MSG_START 0x04 #define MSG_STOP 0x08 #define MSG_OPEN 0x10 #define MSG_CLOSED 0x20 #define FSM_IDLE 0x01 // cook-state state-machine. #define FSM_COOK 0x02 // cook-state state-machine. #define FSM_PAUSE 0x04 // cook-state state-machine. #define FSM_CONTINUE 0x08 // cook-state state-machine. #define FSM_DONE 0x10 // cook-state state-machine. #define RT_PRELOAD 0x01 // remaining-time preload. #define RT_DECREMENT 0x02 // remaining-time decrement. #define RT_PAUSE 0x03 // remaining-time don't change. #define RT_CLEAR 0x04 // remaining-time set to zero. #define TEMP_READ 0x01 // temperature synthesizer control. #define TEMP_CALC 0x02 // temperature synthesizer control. #define TEMP_FREEZE 0x03 // temperature synthesizer control. #define GRANULARITY 0x400 // 1-second countdown ticker granularity. #define MAXSECONDS 180 // maximum microwave-on time. #define BEEPTIME 3 // beep duration in S. #define BEEPFREQ 300 // beep frequency in Hz. #define TIMEINC 10 // time increment in seconds. //--global_definitions--------------------------//------------------------------ struct tButtons // button ISR updates. { char cLeftButton; // cooktime +60S. char cRightButton; // cooktime -60S. char cTopButton; // start cook. char cBottomButton; // stop cook. char cCenterButton; // center button pressed. char cDoorOpen; // door open. }; struct tRemainingTime // remaining time related. { char cControl; // countdown control. char cBeepEnable; // beep control. char cMagnetron; // magnetron blink control. char cCarousel; // carousel blink control. int dTotalTime; // initialize to this. int dRemainingTime; // the countdown value. }; struct tLCD // LCD related. { int dTotalCookTime; // display time in seconds. int dRemainingTime; // display time in seconds. float fCelsius; // temperature in celsius. }; Queue<int, 1> queueModTotalTime; // modify total time. Queue<int, 1> queueUpdateFSM; // tell FSM what button was pressed. Queue<int, 1> queueUpdateRemainingTime; // message to update remaining time. Queue<int, 1> queueSetRemainingTime; // tell countdown it's start time. Queue<int, 1> queueTemperatureControl; // control the temperature synthesizer. Queue<int, 1> queueSuppressTimeAdjust; // total time control. Queue<int, 1> queueClearTotalTime; // total time control. //--global_variables----------------------------//------------------------------ tButtons giButtons; // ISR button updates. tRemainingTime giRemainingTime; // structure instance. tLCD giLCD; // structure instance. //--global_instances----------------------------//------------------------------ Serial pc(USBTX, USBRX); // PuTTY terminal communication. LM75B temperature(p28,p27); // on-board thermometer. C12832_LCD lcd; // LCD object. DigitalOut led0(LED4); // magnetron. DigitalOut led1(LED3); DigitalOut led2(LED2); DigitalOut led3(LED1); DigitalOut speaker(p26); // speaker device. InterruptIn iJoyStickUp (p15); // joystick up rising edge. InterruptIn iJoyStickDown (p12); // joystick down rising edge. InterruptIn iJoyStickLeft (p13); // joystick left rising edge. InterruptIn iJoyStickRight (p16); // joystick right rising edge. InterruptIn iJoyStickCenter(p14); // 1 if joystick middle pressed. Timer debounceTimer; // button debounce timer. Timer beepTimer; // beep-duration timer. Timer temperatureTimer; // how often to raise temperature. Ticker tickerButtonStateManager; // manage the button states. Ticker tickerCookCountdown; // remaining cook time. Ticker tickerBeep; // beeper ticker. Ticker tickerBlinkMagnetron; // magnetron LED blinker. Ticker tickerBlinkCarousel; // caroulsel LED blinker. //-------prototypes-----------------------------//------------------------------ void slowClock(); // 1Hz or thereabouts. void initialization(); // initialize settings. void ISRleftButtonRising(); // cook-time increase. void ISRleftButtonFalling(); // button-release debounce. void ISRrightButtonRising(); // cook-time decrease. void ISRrightButtonFalling(); // button-release debounce. void ISRtopButtonRising(); // cook start. void ISRtopButtonFalling(); // button-release debounce. void ISRbottomButtonRising(); // cook stop. void ISRbottomButtonFalling(); // button-release debounce. void ISRcenterButtonRising(); // door state toggle. void ISRcenterButtonFalling(); // button-release debounce. void disableSignalWaiting(); // break from signal waiting. void threadButtonStateManager(void const *args); void threadTotalTimeControl(void const *args); void threadCookStateFSM(void const *args); void tickCookRemainingTime(); // remaining time countdown. void temperatureThread(void const *args); // temperature measurement. void LCDthread (void const *args); // LCD display thread. void tickerBeeper(); // beep oscillator. void tickerMagnetron(); // blink magnetron LED. void tickerCarousel(); // blink carousel LED. //==============================================//============================== int main(void) { initialization(); // initialize variables. // ISR setup. iJoyStickLeft.rise (&ISRleftButtonRising); iJoyStickLeft.fall (&ISRleftButtonFalling); iJoyStickRight.rise(&ISRrightButtonRising); iJoyStickRight.fall(&ISRrightButtonFalling); iJoyStickUp.rise (&ISRtopButtonRising); iJoyStickUp.fall (&ISRtopButtonFalling); iJoyStickDown.rise (&ISRbottomButtonRising); iJoyStickDown.fall (&ISRbottomButtonFalling); iJoyStickCenter.rise(&ISRcenterButtonRising); iJoyStickCenter.fall(&ISRcenterButtonFalling); debounceTimer.start(); // kick-off debounce timer. // kick-off tickers. tickerCookCountdown.attach_us(&tickCookRemainingTime,1000000/GRANULARITY); tickerBeep.attach_us (&tickerBeeper ,2000); tickerBlinkMagnetron.attach_us(&tickerMagnetron,250000); tickerBlinkCarousel.attach_us(&tickerCarousel,250000); // kick-off threads. Thread thread_1(temperatureThread ,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL); Thread thread_2(LCDthread ,NULL,osPriorityNormal,DEFAULT_STACK_SIZE,NULL); Thread thread_3(threadTotalTimeControl ,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL); Thread thread_4(threadButtonStateManager,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL); Thread thread_5(threadCookStateFSM ,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL); while(1) Thread::wait(10000); // execute microwave controller. } /*----------------------------------------------//----------------------------*/ // this serves as the bottom-half for all of the button-press IRSs. // it sends button-status messages to other threads, and it also // clears the button-state globals to prepare them for the next // button press. void threadButtonStateManager(void const *args) { int dMessage; // message. while(1) // thread loop. { //--- // TOTAL TIME CONTROL. // encoded integers will be sent, // not pointers. if (giButtons.cLeftButton) // total time increment button. { dMessage = MSG_INC_TIME; // set message. queueModTotalTime.put((int *) dMessage,1);// pretend it's a pointer. giButtons.cLeftButton = 0; // clear the button state. } if (giButtons.cRightButton) // total time decrement button. { dMessage = MSG_DEC_TIME; // set message. queueModTotalTime.put((int *) dMessage,1);// pretend it's a pointer. giButtons.cRightButton = 0; // clear the button state. } //--- // COOK-STATE FSM. if (giButtons.cTopButton) // start-cook button. { dMessage = MSG_START; // set message. queueUpdateFSM.put((int *) dMessage,1); // pretend it's a pointer. giButtons.cTopButton = 0; // clear the button state. } if (giButtons.cBottomButton) // stop-cook button. { dMessage = MSG_STOP; // set message. queueUpdateFSM.put((int *) dMessage,1); // pretend it's a pointer. giButtons.cBottomButton = 0; // clear the button state. } if (giButtons.cCenterButton) // door-state-toggle. { dMessage = giButtons.cDoorOpen; // determined in ISR. queueUpdateFSM.put((int *) dMessage,1); // pretend it's a pointer. giButtons.cCenterButton = 0; // clear the button state. } //--- Thread::wait(THREAD_4_WAITmS); // multitasking. } // thread loop. } // threadButtonStateManager. /*----------------------------------------------//----------------------------*/ // total time controller. void threadTotalTimeControl(void const *args) { static int dTotalTime = 0; // total time variable. int dMessage; // message. int dSuppress; // 1 to suppress operation. osEvent queueEvent; // queue event. dSuppress = 0; // initialize. while(1) // thread loop. { // if FSM tells this to clear // the total time. queueEvent = queueClearTotalTime.get(1); if (queueEvent.status == osEventMessage) { dTotalTime = 0; queueSetRemainingTime.put((int *) dTotalTime,1); giRemainingTime.dTotalTime = dTotalTime; } // suppression mail from FSM. queueEvent = queueSuppressTimeAdjust.get(1); if (queueEvent.status == osEventMessage) { dSuppress = (int) queueEvent.value.p; } queueEvent = queueModTotalTime.get(1); // get message. if (queueEvent.status == osEventMessage) { dMessage = (int) queueEvent.value.p; // interpret as integer, not pointer. if (!dSuppress) // increment total time. if (dMessage == MSG_INC_TIME) dTotalTime += TIMEINC; if (!dSuppress) // decrement total time. if (dMessage == MSG_DEC_TIME) dTotalTime -= TIMEINC; // saturations. if (dTotalTime > 180) dTotalTime = 180; if (dTotalTime < 0) dTotalTime = 0; queueSetRemainingTime.put((int *) dTotalTime,1); giRemainingTime.dTotalTime = dTotalTime; } Thread::wait(THREAD_3_WAITmS); // multitasking. } // thread loop. } // threadTotalTimeControl. /*----------------------------------------------//----------------------------*/ void threadCookStateFSM(void const *args) // cook-cycle FSM. { int dFSMstate = FSM_IDLE; // state of this FSM. int dFSMstateLast = FSM_IDLE; // previous FSM state. int dButtonState = 0; // received button state. int dRemainingTime = 0; // received remaining time. int dButtonStart = 0; // received button state. int dButtonStop = 0; // received button state. int dDoorOpen = 0; // received door state. osEvent queueEvent; // from button state manager. beepTimer.start(); // run the beep timer. // constantly read live temperature. queueTemperatureControl.put((int *) TEMP_READ,1); while(1) // thread loop. { switch (dFSMstate) // cook-mode state machine. { //--- case FSM_IDLE : // IDLE. { giRemainingTime.cMagnetron = 0; // highest priority. giRemainingTime.cCarousel = 0; // turn on carousel. if (dFSMstate != dFSMstateLast) // if just entered state. { // live read of temperature, // allow total time adjustments, // clear total time. queueTemperatureControl.put((int *) TEMP_READ,1); queueSuppressTimeAdjust.put((int*) 0,1); queueClearTotalTime.put ((int*) 1,1); } // set global LCD data. giLCD.dTotalCookTime = giRemainingTime.dTotalTime; giLCD.dRemainingTime = 0; // suppress remaining time display. // remaining time loaded with total time. giRemainingTime.cControl = RT_PRELOAD; dFSMstateLast = dFSMstate; // determine next state. // to FSM_COOK if the start button was // pressed, the door is closed, and the // remaining time is more than zero. if ((dButtonStart == 1) && (dDoorOpen == 0) && (giRemainingTime.dTotalTime > 0)) dFSMstate = FSM_COOK; break; } //--- case FSM_COOK : // COOK. { giRemainingTime.cMagnetron = 1; // highest priority. giRemainingTime.cCarousel = 1; // turn on carousel. if (dFSMstate != dFSMstateLast) // if just entered state. { // start decrementing remaining time. // temperature synthesizer is now // calculating the temperature, // user can no longer adjust total time. giRemainingTime.cControl = RT_DECREMENT; queueTemperatureControl.put((int *) TEMP_CALC,1); queueSuppressTimeAdjust.put((int*) 1,1); } // update global LCD data. giLCD.dTotalCookTime = giRemainingTime.dTotalTime; giLCD.dRemainingTime = giRemainingTime.dRemainingTime; dFSMstateLast = dFSMstate; // determine next state. // pause on door-open if remaining time. if ((dDoorOpen == 1) && (dRemainingTime > 0)) dFSMstate = FSM_PAUSE; else // stop button -> idle state. if (dButtonStop) dFSMstate = FSM_IDLE; else // beeper state once countdown complete. if (dRemainingTime <= 0) dFSMstate = FSM_DONE; break; } //--- case FSM_PAUSE : // PAUSE. { giRemainingTime.cMagnetron = 0; // highest priority. giRemainingTime.cCarousel = 0; // turn on carousel. if (dFSMstate != dFSMstateLast) // if just entered state. { // suspend synthesized temperature calculation. queueTemperatureControl.put((int *) TEMP_FREEZE,1); } // update LCD globals. giLCD.dTotalCookTime = giRemainingTime.dTotalTime; giLCD.dRemainingTime = giRemainingTime.dRemainingTime; giRemainingTime.cControl = RT_PAUSE;// pause remaining time countdown. dFSMstateLast = dFSMstate; // determine next state. // continue if user hits start button, // and the door is closed, and there // is some remaining time. if ((dButtonStart == 1) && (dDoorOpen == 0) && (giRemainingTime.dTotalTime > 0)) dFSMstate = FSM_CONTINUE; else // to idle if user hits stop button. if (dButtonStop) dFSMstate = FSM_IDLE; break; } //--- case FSM_CONTINUE : // CONTINUE. { giRemainingTime.cMagnetron = 1; // highest priority. giRemainingTime.cCarousel = 1; // turn on carousel. if (dFSMstate != dFSMstateLast) // if just entered state. { // continue calculating synthetic temperature. queueTemperatureControl.put((int *) TEMP_CALC,1); } // update LCD globals. giLCD.dTotalCookTime = giRemainingTime.dTotalTime; giLCD.dRemainingTime = giRemainingTime.dRemainingTime; // continue decrementing time. giRemainingTime.cControl = RT_DECREMENT; dFSMstateLast = dFSMstate; // determine next state. // beep state when no more time. if (dRemainingTime <= 0) dFSMstate = FSM_DONE; else // user stop causes jump to idle. if (dButtonStop) dFSMstate = FSM_IDLE; else // back to pause if door open, // and there's remaining time. if ((dDoorOpen == 1) && (dRemainingTime > 0)) dFSMstate = FSM_PAUSE; break; } //--- case FSM_DONE : // DONE. { giRemainingTime.cMagnetron = 0; // highest priority. giRemainingTime.cCarousel = 0; // turn on carousel. if (dFSMstate != dFSMstateLast) // if just entered state. { // freeze synthetic temperature. queueTemperatureControl.put((int *) TEMP_FREEZE,1); giRemainingTime.cBeepEnable = 1; // initiate beep. beepTimer.reset(); // clear the beep timer. } // update LCD globals. giLCD.dTotalCookTime = giRemainingTime.dTotalTime; giLCD.dRemainingTime = giRemainingTime.dRemainingTime; giRemainingTime.cControl = RT_CLEAR;// clear remaining time. (needed?) dFSMstateLast = dFSMstate; // determine next state. // to idle once beeper is done. if (beepTimer.read() >= BEEPTIME) {dFSMstate = FSM_IDLE; giRemainingTime.cBeepEnable = 0;} else dFSMstate = FSM_DONE; // (else stay here). break; } //--- default : {dFSMstate = FSM_IDLE; break;} } queueEvent = queueUpdateFSM.get(1); // threadButtonStateManager if (queueEvent.status == osEventMessage)// update state variable. { // interpret as integer, not pointer. dButtonState = (int) queueEvent.value.p; if (dButtonState == MSG_START) { dButtonStart = 1; // copy-in start info. dButtonStop = 0; if (dDoorOpen) dButtonStart = 0; // cancel start if door open. } if (dButtonState == MSG_STOP) { dButtonStart = 0; // copy-in stop state. dButtonStop = 1; queueClearTotalTime.put((int*) 1,1);// clear total time on stop. } if (dButtonState == MSG_OPEN) // if the door opens, clear current 'start' state. { dDoorOpen = 1; dButtonStart = 0; } if (dButtonState == MSG_CLOSED) // detect door closed. { dDoorOpen = 0; } } else // no queue update available. { dButtonStart = 0; dButtonStop = 0; } // fetch from global scope. dRemainingTime = giRemainingTime.dRemainingTime; Thread::wait(THREAD_5_WAITmS); // multitasking. } // thread loop. } // threadCookStateFSM. /*----------------------------------------------//----------------------------*/ void initialization(void) // program initializations. { led1 = 0; giButtons.cDoorOpen = 0; // initialize with door closed. giRemainingTime.cBeepEnable =0; giRemainingTime.cMagnetron = 0; giRemainingTime.cCarousel = 0; } /*----------------------------------------------//----------------------------*/ void ISRleftButtonRising(void) // cooktime plus 60s. { if (debounceTimer.read_ms() > DEBOUNCEmS) giButtons.cLeftButton = 1; // detect left button. debounceTimer.reset(); // begin debounce period. } /*----------------------------------------------//----------------------------*/ void ISRleftButtonFalling(void) // button-release debounce. { debounceTimer.reset(); // begin debounce period. } /*----------------------------------------------//----------------------------*/ void ISRrightButtonRising(void) // cooktime -60s. { if (debounceTimer.read_ms() > DEBOUNCEmS) giButtons.cRightButton = 1; // detect right button. debounceTimer.reset(); // begin debounce period. } /*----------------------------------------------//----------------------------*/ void ISRrightButtonFalling(void) // button-release debounce. { debounceTimer.reset(); // begin debounce period. } /*----------------------------------------------//----------------------------*/ void ISRtopButtonRising(void) // cook start. { if (debounceTimer.read_ms() > DEBOUNCEmS) giButtons.cTopButton = 1; // detect top button. debounceTimer.reset(); // begin debounce period. } /*----------------------------------------------//----------------------------*/ void ISRtopButtonFalling(void) // button-release debounce. { debounceTimer.reset(); // begin debounce period. } /*----------------------------------------------//----------------------------*/ // front-end control of magnetron off. // due to physical danger, the magnetron is turned off immediately // upon off-button press, in the interrupt that it generates. void ISRbottomButtonRising(void) // cook stop. { if (debounceTimer.read_ms() > DEBOUNCEmS) { led0 = 0; // magnetron off. giButtons.cBottomButton = 1; // detect bottom button. } debounceTimer.reset(); // begin debounce period. } /*----------------------------------------------//----------------------------*/ void ISRbottomButtonFalling(void) // button-release debounce. { debounceTimer.reset(); // begin debounce period. } /*----------------------------------------------//----------------------------*/ // front-end control of magnetron off. // due to physical danger, the magnetron is turned off immediately // upon detection of an open door. void ISRcenterButtonRising(void) // toggle door state. { if (debounceTimer.read_ms() > DEBOUNCEmS) { if (giButtons.cDoorOpen == MSG_OPEN) // calculate door state. giButtons.cDoorOpen = MSG_CLOSED; else giButtons.cDoorOpen = MSG_OPEN; // magnetron off. if (giButtons.cDoorOpen == MSG_OPEN) led0 = 0; giButtons.cCenterButton = 1; } debounceTimer.reset(); // begin debounce period. } /*----------------------------------------------//----------------------------*/ void ISRcenterButtonFalling(void) // button-release debounce. { debounceTimer.reset(); // begin debounce period. } /*----------------------------------------------//----------------------------*/ // this will measure live temperature, or calculate it to be // increasing at 1/6 degree per second, or freeze it. void temperatureThread(void const *args) // temperature measurement. { int dTempControl = 0; // temperature control. float fLastMeasuredTemperature = 0.0; // last physically-measured temperature. float fCelsius; // synthetic temperature. osEvent queueEvent; // from button state manager. while(1) // thread loop. { // obtain temperature command. queueEvent = queueTemperatureControl.get(1); if (queueEvent.status == osEventMessage) { dTempControl = (int) queueEvent.value.p; } if (dTempControl == TEMP_READ) { fCelsius = temperature.read(); // physical measurement. fLastMeasuredTemperature = fCelsius; } if (dTempControl == TEMP_CALC) // calculate synthetic temperature. fCelsius = ((float) (giRemainingTime.dTotalTime - giRemainingTime.dRemainingTime) / (float) 6.0) + fLastMeasuredTemperature; giLCD.fCelsius = fCelsius; // to global scope for LCD. Thread::wait(THREAD_1_WAITmS); // multitasking. } // thread loop. } // temperatureThread. /*----------------------------------------------//----------------------------*/ void LCDthread(void const *args) // LCD display thread. { static int dLCDtotalCookTimeSec = 0; // sample current values. static int dCookTimeRemainingSec = 0; static float fLCDcelsius = 0.0; // remember previous values. static int dLCDtotalCookTimeSecLast = 0; static int dCookTimeRemainingSecLast = 0; static float fLCDcelsiusLast = 0.0; while(1) // thread loop. { // don't allow the values to // change in the middle of the // below else the anti-blink // code won't work. dLCDtotalCookTimeSec = giLCD.dTotalCookTime; dCookTimeRemainingSec = giLCD.dRemainingTime; fLCDcelsius = giLCD.fCelsius; // clear display only when // necessary, in order to avoid // 'blinkieness'. if (dLCDtotalCookTimeSec != dLCDtotalCookTimeSecLast || dCookTimeRemainingSec != dCookTimeRemainingSecLast || fLCDcelsius != fLCDcelsiusLast) lcd.cls(); LCD1; // line 1. lcd.printf(" total cook time: %d",dLCDtotalCookTimeSec); LCD2; // line 2. lcd.printf(" remaing cook time: %d",dCookTimeRemainingSec); LCD3; // line 3. lcd.printf(" temperature : %5.3f",fLCDcelsius); // pipeline variables. dLCDtotalCookTimeSecLast = dLCDtotalCookTimeSec; dCookTimeRemainingSecLast = dCookTimeRemainingSec; fLCDcelsiusLast = fLCDcelsius; Thread::wait(THREAD_2_WAITmS); // multitasking. } // thread loop. } // LCDthread. /*----------------------------------------------//----------------------------*/ // cook remaining time countdown counter. // possibly due to a bug in Ticker // http://mbed.org/questions/1563/Mbed-Tickerfunction-hangs-system-from-re/ // I've been unable to detach/attach this routine in order to reset its phase // when I tried it at a 1s resolution. In order to provide the human perception // of an immediate restart, I've increased the ticker frequency by the factor // 'GRANULARITY' and likewise divide that factor out when this routine // promotes the remaining time to the global variable. // communication is via a global instance of struct tRemainingTime. void tickCookRemainingTime(void) // cook-cycle countdown. { static int dRemainingTime = 0; // remaining time in seconds. int dMaximum; // MAXSECONDS * GRANULARITY. dMaximum = MAXSECONDS * GRANULARITY; // precalculate. switch (giRemainingTime.cControl) // control processing. { case RT_PRELOAD : // preload with total time. { // the 'GRANULARITY - 1' factor // compensates for integer division // dropping the right-of-decimal result, // that occuring at the bottom of this // routine. dRemainingTime = (giRemainingTime.dTotalTime * GRANULARITY) + (GRANULARITY - 1); break; } case RT_DECREMENT : // count-down. { dRemainingTime--; break; } case RT_PAUSE : // suspend countdown. { dRemainingTime = dRemainingTime; break; } case RT_CLEAR : // clear countdown. { dRemainingTime = 0; break; } default : // saturate, just in case. { } } // control processing. // saturate value. if (dRemainingTime > dMaximum) dRemainingTime = dMaximum; if (dRemainingTime < 0) dRemainingTime = 0; // promote to global scope. giRemainingTime.dRemainingTime = dRemainingTime/GRANULARITY; } // cookRemainingTime. /*----------------------------------------------//----------------------------*/ void tickerBeeper(void) // beep when giRemainingTime. { static char cState = 0; if (giRemainingTime.cBeepEnable) { if (cState) {speaker = 1; cState = 0;} else {speaker = 0; cState = 1;} } } /*----------------------------------------------//----------------------------*/ void tickerMagnetron(void) // magnetron enunciator. { if (giRemainingTime.cMagnetron) led0 = !led0; else led0 = 0; } /*----------------------------------------------//----------------------------*/ void tickerCarousel(void) // carousel enunciator. { if (giRemainingTime.cCarousel) led1 = !led1; else led1 = 0; } /*----------------------------------------------//----------------------------*/