homework 7

Dependencies:   mbed-rtos mbed C12832_lcd LM75B

main.cpp

Committer:
gatedClock
Date:
2013-09-12
Revision:
86:388c2b4b7cf5
Parent:
84:fee8fb80d190
Child:
87:147e2b08fae6

File content as of revision 86:388c2b4b7cf5:

/*----------------------------------------------//------------------------------
    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.
    
    
-----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  20                 // 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   1                 // 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 GRANULARITY    0x400                // 1-second countdown ticker granularity.
    
    #define DEBUG1                              // debug preprocessor control.
//--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.
      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;            // message to modify total time.
    Queue<int, 1> queueUpdateFSM;               // message to inform FSM.
    Queue<int, 1> queueUpdateRemainingTime;     // message to update remaining time.
    Queue<int, 1> queueSetRemainingTime;        // tell countdown it's start time.
    Queue<int, 1> queueFSMnewState;             // latest FSM state.
//--global_variables----------------------------//------------------------------ 
    char     gcSignalWaitEnable;                // 1 to wait on a signal.
    char     gcSlowClock;                       // slow-clock signal.
    char     gcInitializePipeline;              // 1 to initialize pipeline state.
    float    gfCelsius;                         // from thermometer.
        
    tButtons gtButtons;                         // ISR button updates.
    
    tRemainingTime giRemainingTime;             // structure instance.
    tLCD           giLCD;                       // structure instance.
    
    int      gdDiagTotalTime;
//--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);
    
    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.

    Ticker       tickerButtonStateManager;      // manage the button states.
    Ticker       tickerSlowClock;               // generate a ~1Hz clock.
    Ticker       tickerCookCountdown;           // remaining cook time.       
    
 //   Timer        timerFSMdone;                  // duration of FSM 'done' state.

//-------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.
//==============================================//==============================
    int main(void) 
    {
      char cLeftButtonState;                    // 1 means button was pressed.
    
    
    
      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.
      
      gcInitializePipeline = 1;                 // tell pipeline to initialize.
      
      initialization();                         // initialize variables.
      gcSlowClock = 0;
      led1 = 0;
      gtButtons.cDoorOpen = 0;                  // initialize with door closed.
      
 //     timerFSMdone.start();                 // start 'done' timer.
 
 //     tickerSlowClock.attach_us(&slowClock         ,SLOWCLOCKuS);
      

       tickerCookCountdown.attach_us(&tickCookRemainingTime,1000000/GRANULARITY);
 
      
      Thread thread_1(temperatureThread       ,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL);
      Thread thread_2(LCDthread               ,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL);
      Thread thread_3(threadTotalTimeControl  ,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL);
      Thread thread_4(threadButtonStateManager,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL); // was osPriorityIdle
      Thread thread_5(threadCookStateFSM      ,NULL,osPriorityIdle,DEFAULT_STACK_SIZE,NULL);

//Thread thread_4(threadButtonStateManager);


 // the message-receiving threads need 'else' for message-receive-timeout.
      
      while(1)
      {
 //       pc.printf("\n\r gdDiagTotalTime %d",gdDiagTotalTime);
 
        Thread::wait(1000);
      
      
      
 
      }
    }     
/*----------------------------------------------//----------------------------*/

//  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 (gtButtons.cLeftButton)                // total time increment button.
      {
        dMessage = MSG_INC_TIME;                // set message.
        queueModTotalTime.put((int *) dMessage,1);// pretend it's a pointer.
        gtButtons.cLeftButton = 0;              // clear the button state.
      }

      if (gtButtons.cRightButton)               // total time decrement button.
      {
        dMessage = MSG_DEC_TIME;                // set message.
        queueModTotalTime.put((int *) dMessage,1);// pretend it's a pointer.
        gtButtons.cRightButton = 0;             // clear the button state.
      }
   
//---                                           // COOK-STATE FSM.
    
      if (gtButtons.cTopButton)                 // start-cook button.             
      {
        dMessage = MSG_START;                   // set message.
        queueUpdateFSM.put((int *) dMessage,1);   // pretend it's a pointer.
        gtButtons.cTopButton = 0;               // clear the button state.
      }
     
      if (gtButtons.cBottomButton)              // stop-cook button.
      {
        dMessage = MSG_STOP;                    // set message.
        queueUpdateFSM.put((int *) dMessage,1);   // pretend it's a pointer.
        gtButtons.cBottomButton = 0;            // clear the button state.
      }
  
      if (gtButtons.cCenterButton)              // door-state-toggle.
      {
        dMessage = gtButtons.cDoorOpen;         // determined in ISR.
        queueUpdateFSM.put((int *) dMessage,1);   // pretend it's a pointer.
        gtButtons.cCenterButton = 0;            // clear the button state.      
      }
//---     
      
        Thread::wait(THREAD_4_WAITmS);          // multitasking.
      }                                         // thread loop.
    }                                           // threadButtonStateManager.
/*----------------------------------------------//----------------------------*/
//  the incoming messages are mutually-exclusive.
                                                // total time controller.
    void threadTotalTimeControl(void const *args)           
    {
      static int   dTotalTime = 0;              // total time variable.
             int   dMessage;                    // message.
         osEvent   queueEvent;                  // queue event.
             int   dRC;
      
      while(1)                                  // thread loop.
      {
      
        queueEvent = queueModTotalTime.get(1);  // get message.
        if (queueEvent.status == osEventMessage)
        {
          dMessage = (int) queueEvent.value.p;  // interpret as integer, not pointer.
          
                                                // increment total time.
          if (dMessage == MSG_INC_TIME) dTotalTime += 60; 
          
                                                // decrement total time.
          if (dMessage == MSG_DEC_TIME) dTotalTime -= 60;
          
                                                // saturations.
          if (dTotalTime > 180) dTotalTime = 180; 
          if (dTotalTime <   0) dTotalTime =   0;         
          
          dRC = 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;                // received button state.
      int   dRemainingTime = 0;          // received remaining time.
      
      int   dButtonStart   = 0;
      int   dButtonStop    = 0;
      int   dDoorOpen      = 0;
            
      osEvent   queueEvent;                     // from button state manager.

      
      
      while(1)                                  // thread loop.
      {
  
        switch (dFSMstate)                      // cook-mode state machine.
        {
//---        
          case  FSM_IDLE :                      // IDLE.
          {
            if (dFSMstate != dFSMstateLast)     // if just entered state.
            {         
            }
            
            giLCD.dTotalCookTime = giRemainingTime.dTotalTime;
            giLCD.dRemainingTime = 0;           // suppress remaining time display.
            giLCD.fCelsius       = 0;
            
            giRemainingTime.cControl = RT_PRELOAD;                                           
            dFSMstateLast = dFSMstate;          // determine next state.
            if ((dButtonStart == 1) && (dDoorOpen == 0) && (giRemainingTime.dTotalTime > 0)) dFSMstate = FSM_COOK;
            break;
          }
//---   
           case  FSM_COOK :                     // COOK.
          {
            if (dFSMstate != dFSMstateLast)     // if just entered state.
            { 
              giRemainingTime.cControl = RT_DECREMENT;
            }    
            
            giLCD.dTotalCookTime = giRemainingTime.dTotalTime;
            giLCD.dRemainingTime = giRemainingTime.dRemainingTime;
            giLCD.fCelsius       = 0;            
            
            
                                                
            dFSMstateLast = dFSMstate;          // determine next state.
            if ((dDoorOpen == 1) && (dRemainingTime > 0)) dFSMstate = FSM_PAUSE; 
            else
            if (dButtonStop) dFSMstate = FSM_IDLE;
            else
            if (dRemainingTime <= 0) dFSMstate = FSM_DONE;
            break;
          }
//---   
          case  FSM_PAUSE :                     // PAUSE.
          {
              
            if (dFSMstate != dFSMstateLast)     // if just entered state.
            { 
            }
            
            
            giLCD.dTotalCookTime = giRemainingTime.dTotalTime;
            giLCD.dRemainingTime = giRemainingTime.dRemainingTime;
            giLCD.fCelsius       = 0;              
            
            
            giRemainingTime.cControl = RT_PAUSE;
                                                // determine next state.
            dFSMstateLast = dFSMstate;
            if ((dButtonStart == 1) && (dDoorOpen == 0) && (giRemainingTime.dTotalTime > 0)) dFSMstate = FSM_CONTINUE;
            else
            if (dButtonStop) dFSMstate = FSM_IDLE; 
            break;
          }
          
//---             
          case  FSM_CONTINUE :                  // CONTINUE.
          {
              
            if (dFSMstate != dFSMstateLast)     // if just entered state.
            { 
            }
            
            
            giLCD.dTotalCookTime = giRemainingTime.dTotalTime;
            giLCD.dRemainingTime = giRemainingTime.dRemainingTime;
            giLCD.fCelsius       = 0;              
            
            
            giRemainingTime.cControl = RT_DECREMENT;
                                                // determine next state.
            dFSMstateLast = dFSMstate;
            if (dRemainingTime <= 0) dFSMstate = FSM_DONE;
            else
            if (dButtonStop) dFSMstate = FSM_IDLE; 
            else
            if ((dDoorOpen == 1) && (dRemainingTime > 0)) dFSMstate = FSM_PAUSE;
            break;
          }          
          
          
//---             
          case  FSM_DONE :                      // DONE.
          {
            
            if (dFSMstate != dFSMstateLast)     // if just entered state.
            { 
            }
           
            
            giLCD.dTotalCookTime = giRemainingTime.dTotalTime;
            giLCD.dRemainingTime = giRemainingTime.dRemainingTime;
            giLCD.fCelsius       = 0;              
            
            
            
            giRemainingTime.cControl = RT_CLEAR;
                                                // determine next state.
            dFSMstateLast = dFSMstate;
            dFSMstate = FSM_IDLE; 
            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;
            dButtonStop  = 0;
            
                                                // if the door is open, ignore
                                                // and cancel 'start' state.
            if (dDoorOpen) dButtonStart = 0;
          }
          if (dButtonState == MSG_STOP)
          {
            dButtonStart = 0;
            dButtonStop  = 1;
          }    
          
          if (dButtonState == MSG_OPEN)
          {
                                                // if the door opens, clear current 'start' state.
            dDoorOpen    = 1;
            dButtonStart = 0;
          }       
          
          if (dButtonState == MSG_CLOSED)
          {
            dDoorOpen = 0;
          }               
                
        }
 
                                                // fetch from global scope.
        dRemainingTime = giRemainingTime.dRemainingTime;

                      // pipeline variable.
        Thread::wait(THREAD_5_WAITmS);          // multitasking. 
      }                                         // thread loop.
    }                                           // threadCookStateFSM.
/*----------------------------------------------//----------------------------*/
    void slowClock(void)                        // 1Hz or thereabouts.
    {
      gcSlowClock = !gcSlowClock;               // toggle clock.
    }
/*----------------------------------------------//----------------------------*/
    void initialization(void)                   // program initializations.
    {
      gcSignalWaitEnable = 1;
    }
/*----------------------------------------------//----------------------------*/
    void ISRleftButtonRising(void)              // cooktime plus 60s.
    {
      if (debounceTimer.read_ms() > DEBOUNCEmS)
      gtButtons.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)
      gtButtons.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)
      gtButtons.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.
        gtButtons.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 (gtButtons.cDoorOpen == MSG_OPEN)    // calculate door state.
        gtButtons.cDoorOpen = MSG_CLOSED;
        else
        gtButtons.cDoorOpen = MSG_OPEN;
      
                                                // magnetron off.
        if (gtButtons.cDoorOpen == MSG_OPEN) led0 = 0;        
      
        gtButtons.cCenterButton = 1;              
      }
      debounceTimer.reset();                    // begin debounce period.
    } 
/*----------------------------------------------//----------------------------*/
    void ISRcenterButtonFalling(void)           // button-release debounce.
    {
      debounceTimer.reset();                    // begin debounce period.
    } 
/*----------------------------------------------//----------------------------*/
    void temperatureThread(void const *args)    // temperature measurement.
    {
      while(1)                                  // thread loop.
      {
        gfCelsius = temperature.read();         // physical measurement.
      
        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.
/*----------------------------------------------//----------------------------*/

    void tickCookRemainingTime(void)            // cook-cycle countdown.
    {
      static int dRemainingTime = 0;            // remaining time in seconds.
      

      
      switch (giRemainingTime.cControl)         // control processing.
      {
        case RT_PRELOAD   :                     // preload with total time.
        {
          dRemainingTime = giRemainingTime.dTotalTime * GRANULARITY;
          if (dRemainingTime > 180 * GRANULARITY) dRemainingTime = 180 * GRANULARITY;
          if (dRemainingTime <   0) dRemainingTime =   0;
          break;
        }
        case RT_DECREMENT :                     // count-down.
        {
          dRemainingTime--;
          if (dRemainingTime > 180 * GRANULARITY) dRemainingTime = 180 * GRANULARITY;
          if (dRemainingTime <   0) dRemainingTime =   0;
          break;
        }
      
        case RT_PAUSE    :                      // suspend countdown.
        {
          dRemainingTime = dRemainingTime;
          if (dRemainingTime > 180 * GRANULARITY) dRemainingTime = 180 * GRANULARITY;
          if (dRemainingTime <   0) dRemainingTime =   0;
          break;
        }
        
        case RT_CLEAR    :                      // clear countdown.
        {
          dRemainingTime = 0;      
          led3 = 1;         
          break;
        }
      
        default          :                      // saturate, just in case.
        {
          if (dRemainingTime > 180 * GRANULARITY) dRemainingTime = 180 * GRANULARITY;
          if (dRemainingTime <   0) dRemainingTime =   0;        
        }
      }                                         // control processing.
      
                                                // promote to global scope.
      giRemainingTime.dRemainingTime = dRemainingTime/GRANULARITY;
      
    }                                           // cookRemainingTime.
/*----------------------------------------------//----------------------------*/