homework 7

Dependencies:   mbed-rtos mbed C12832_lcd LM75B

main.cpp

Committer:
gatedClock
Date:
2013-09-12
Revision:
115:6390d9b9be07
Parent:
114:fbe9ef1630c1
Child:
116:15084f54baf9

File content as of revision 115:6390d9b9be07:

/*----------------------------------------------//------------------------------
    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.
      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. 
//-------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.
//==============================================//==============================
    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);     
      
                                                // 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.
          {
            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.
          {
            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.
          {
              
            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.
          {
              
            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.
          {
            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;
    }
/*----------------------------------------------//----------------------------*/
    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.cBeepEnable is 1.
    {
      static char cState = 0;
      if (giRemainingTime.cBeepEnable)
      {
        if (cState) {speaker = 1; cState = 0;}
        else        {speaker = 0; cState = 1;}
      }
    }
/*----------------------------------------------//----------------------------*/