A simple microwave demo

Dependencies:   C12832_lcd LM75B mbed-rtos mbed

Revision:
4:cd87e2050344
Parent:
3:4fb0721ce794
--- a/rtos_hwk7.cpp	Sun Mar 16 02:18:48 2014 +0000
+++ b/rtos_hwk7.cpp	Fri Apr 04 02:16:32 2014 +0000
@@ -1,4 +1,23 @@
+/*
+Assignment: Hwk 7_1
+Due:        3/18/14
+Objective:
+            Finish lab #7. The purpose of this program is to use the RTOS along with
+            Interprocess comunication (IPC). In this program the IPC method utilizes
+            a MemoryPool and a Queue for a messaging system. Anytime a new message comes
+            through the LCD is updated. A union is used to reduce the size of the message
+            data structure. The prgram simulates a microwave with a requirement of having
+            the system shutdown within 1ms. There are no globals that are written to by the
+            threads this reduces the need for mutexes. All communcation is done by signals
+            and IPC in order to reduce global variables.
+            
+            Additions are LED4 light for door open and display the STATE (WAIT, COOK) on the lcd.        
+            
+Test Condition          Pass/Fail Criteria                          Result 
+---------------------   ---------------------------------------     ---------
+See attached sheet
 
+*/
 
 
 #include "mbed.h"
@@ -6,14 +25,23 @@
 #include "C12832_lcd.h" //LCD interface
 #include "LM75B.h"      //temperature interface
 
+#ifndef DEBUG
+    //#define DEBUG
+#endif /* DEBUG */
+
 
 #define DEBOUNCE_TIME_MS    10
 #define MAX_COOK_TIME_S     20 //3min
+#define MIN_COOK_TIME_S     1
 #define INIT_COOK_TIME_S    5 
 #define TEMP_INTERVAL_S     5
+#define TEMP_INCREASE_F     10
 
 
 //globals and types
+#ifdef DEBUG
+    Timer       test_timer; //used to instrument the code
+#endif
 DigitalOut  led1(LED1);
 DigitalOut  led2(LED2);
 DigitalOut  led3(LED3);
@@ -21,59 +49,54 @@
 C12832_LCD  lcd;
 PwmOut      spkr(p26);
 LM75B       therm(p28,p27);
+Mutex       lcd_mutex;      //don't really need this but just to be safe
 Timer       debounceTimer;
 InterruptIn irptTimeDn(p16);    //cook down
 InterruptIn irptTimeUp(p13);    //cook up
 InterruptIn irptStart(p15);    //start cooking
 InterruptIn irptStop(p12);   //stop cooking
 InterruptIn irptDoor(p14);   //door interrupt
-Thread      *proxy_lcd;
+Thread      *proxy_lcd;     //pointers to the threads used for signalling
 Thread      *proxy_temp;
 Thread      *proxy_sound;
 Thread      *proxy_state;
 Thread      *proxy_led;
 Thread      *proxy_timer;
 
-
 typedef enum { //start at one as we are using this as signals
     //states
-    WAITING = 1, 
-    COOKING, 
-    DONE, 
+    WAITING = 1, //everything is ready for cooking, you can change the request time etc
+    COOKING,
+    DONE, //signal for threads to buzz, trurn off lights etc
     //interrupts signals
     TIMEUP, 
     TIMEDN, 
     START, 
     STOP,
     DOOR
-} state_t; 
+} state_t; //this is for signaling of threads with proxies
 
 char *stateStr[] = {"NULL","WAIT","COOK","DONE"};
 
 
-typedef enum {TEMP_VAL, TIME_ELPS, TIME_RQST} type_t;
-typedef enum {STATE, TEMP, SOUND, LCD, LED, TIMER, ALL} thread_t; 
-char *threadStr[] = {"STATE", "TEMP", "SOUND", "LCD", "LED", "TIMER", "ALL"};
+typedef enum {TEMP_VAL, TIME_ELPS, TIME_RQST} data_t; //message data types
 
 typedef struct {
     state_t state;
-    type_t type;
+    data_t type;
     union {
-        float temp;
+        float temp; 
         int time_elapsed; //seconds of cooking
         int time_request; //seconds
     } data;   
-} message_t;
-
-
-
+} message_t; //used to pass messages to the lcd thread
 
 MemoryPool<message_t,10> mpool; //used to hold all messages
-Queue<message_t,10> queue; //used to hold the messages
+Queue<message_t,10> queue;
 
 bool debounce(void);
-void blink_led(void const *args);
-void send_time(void const *args);
+void blink_led(void const *args); //helper function to led thread
+void send_time(void const *args); //helper function to timer thread
 
 //Threads
 void thread_state(void const *args);
@@ -83,8 +106,6 @@
 void thread_led(void const *args);
 void thread_timer(void const *args);
 
-
-
 //ISRs
 void isrTimeUp(void);
 void isrTimeDn(void);
@@ -92,17 +113,16 @@
 void isrStop(void);
 void isrDoor(void);
 
-
-
 int main(void){
     debounceTimer.start();
-    //interrupts
+    //interrupts debounce everything except for the door
     irptTimeUp.rise(&isrTimeUp);
     irptTimeDn.rise(&isrTimeDn);
     irptStart.rise(&isrStart);
     irptStop.rise(&isrStop);
-    irptDoor.rise(&isrDoor); //debounce everything except for the door
-
+    irptDoor.rise(&isrDoor); 
+    
+    //init the threads
     Thread t1(thread_state);    
     Thread t2(thread_temp);
     Thread t3(thread_sound);
@@ -110,16 +130,14 @@
     Thread t5(thread_led);
     Thread t6(thread_timer);
 
-    proxy_state  = &t1;
-    proxy_temp  =  &t2;
-    proxy_sound  = &t3;
-    proxy_lcd =  &t4;
-    proxy_led =  &t5;
-    proxy_timer = &t6;
+    proxy_state     = &t1;
+    proxy_temp      = &t2;
+    proxy_sound     = &t3;
+    proxy_lcd       = &t4;
+    proxy_led       = &t5;
+    proxy_timer     = &t6;
     
-
-    
-    //t1.set_priority(osPriorityRealtime);
+    t1.set_priority(osPriorityRealtime); //state machine most important thread
     proxy_state->signal_set(WAITING); //initialize all the threads
     
     while(1){
@@ -159,10 +177,15 @@
 }
 void isrDoor(void){
     //no debounce this is most important function!
+#ifdef DEBUG
+    test_timer.start();
+#endif
     proxy_state->signal_set(DOOR);
 }
 
-//State thread to do most of the main state machine logic
+/*
+* State thread to do most of the main state machine logic this is where control happens
+*/
 void thread_state(void const *args){
     osEvent evt;
     int32_t mask;
@@ -172,14 +195,14 @@
         evt = Thread::signal_wait(0);
         mask = evt.value.signals;
         switch(mask){
-            case WAITING:
+            case WAITING: //come from DONE to here
                 state = WAITING;
                 proxy_led->signal_set(state);
                 proxy_timer->signal_set(state);
                 proxy_temp->signal_set(state);
                 break;
             case COOKING:
-                if(!openDoor){ //can't cook if door is open
+                if(!openDoor && state == WAITING){ //can't cook if door is open
                     state = COOKING;
                     proxy_led->signal_set(state);
                     proxy_timer->signal_set(state);
@@ -188,12 +211,14 @@
                 break;
             case DONE: //timer can signal this
                 state = DONE;
-                //tell everyone we are done
+                //tell everyone we are done so they can buzz, dispaly and stop blinking
                 proxy_led->signal_set(state);
                 proxy_timer->signal_set(state);
                 proxy_temp->signal_set(state);
-                
-                proxy_state->signal_set(WAITING); //goto waiting
+#ifdef DEBUG
+                //test_timer.stop(); // ~21 us to here from interrupt
+#endif                    
+                proxy_state->signal_set(WAITING); //goto WAITING
                 break;
             case TIMEUP: //change the timer up
                 if(state == WAITING) proxy_timer->signal_set(TIMEUP); 
@@ -202,38 +227,42 @@
                 if(state == WAITING) proxy_timer->signal_set(TIMEDN);
                 break;
             case START:
-                if(state == WAITING) proxy_state->signal_set(COOKING); //
+                proxy_state->signal_set(COOKING);
                 break;
             case STOP:
-                if(state == COOKING) proxy_state->signal_set(state);
+                proxy_state->signal_set(DONE);
                 break;
             case DOOR: //door changed state
                 openDoor = !openDoor;
                 state = (openDoor == true) ? DONE : WAITING; //open door then done else state back to waiting 
-                led4 = openDoor;
+                led4 = openDoor; //visual for when door is open
                 proxy_state->signal_set(state); //signal back to state thread
                 break;
         }
     }
 }
-//helper function for the timer thread
+/*
+* Helper function for the timer thread gets called at 1sec intervals
+*/
 void send_time(void const *args){
     int time = (*((int*)args))--; //have the time in seconds send to lcd now
     state_t state = COOKING;
-    if(time == 0){
+    if(time == 0){ //time is up signal state machine to done
         state = DONE;
-        proxy_state->signal_set(state); //tell all threads cooking time up
-        proxy_sound->signal_set(state);
+        proxy_state->signal_set(state); //tell threads cooking time up
+        proxy_sound->signal_set(state); 
     }
     proxy_temp->signal_set(state); //use this timer to schedule temperature read
     
-    message_t *msg = mpool.alloc(); //allocate a new message
+    message_t *msg = mpool.alloc(); //allocate a new message lcd is recipient
     msg->state = state;
     msg->type = TIME_ELPS;
     msg->data.time_elapsed = time;
     queue.put(msg);
 }
-
+/*
+* Timer thread to keep track of all the time requirements for temp reading and elapsed time
+*/
 void thread_timer(void const *args){
     int time_set, time;
     time_set = INIT_COOK_TIME_S;
@@ -250,73 +279,85 @@
                 state = WAITING;
                 break;
             case COOKING:
-                if(state != COOKING){ //protect agains multiple start pushed if already cooking
-                    timer.stop();
-                    state = COOKING;
-                    time = time_set;
-                    timer.start(1000); //this is the increments of the timer 1s
-                }
+                state = COOKING;
+                timer.stop(); //stop the timer
+                time = time_set;
+                send_time(ptime); //initialize the elpased time
+                timer.start(1000); //this is the increments of the timer 1s using RtosTimer
                 break;
             case DONE:
-                if(state != DONE){
-                    state = DONE;
-                    time = INIT_COOK_TIME_S;
-                    timer.stop();
-                }
+                state = DONE;
+                time = INIT_COOK_TIME_S;
+                //if(time > 0) time_set = time;
+                //else time = INIT_COOK_TIME_S;
+                timer.stop();
                 break;
             case TIMEUP:
-                if(state == WAITING){
-                    time_set = (++time_set) > MAX_COOK_TIME_S ? MAX_COOK_TIME_S : time_set; //check limits 
-                }
+                time_set = (++time_set) > MAX_COOK_TIME_S ? MAX_COOK_TIME_S : time_set; //check limits 
                 break;
             case TIMEDN:
-                if(state == WAITING)
-                    time_set = (--time_set) < 1 ? 1 : time_set; //check limits  
+                time_set = (--time_set) < MIN_COOK_TIME_S ? MIN_COOK_TIME_S : time_set; //check limits  
                 break;
         }
         if(sig){
-            message_t *msg = mpool.alloc(); //allocate a new message
-            msg->state = state;
-            msg->type = TIME_RQST;
-            msg->data.time_request = time_set;
-            queue.put(msg);
+            message_t *msg = mpool.alloc(); //allocate a new message to the lcd
+            if(msg != NULL){
+                msg->state = state;
+                msg->type = TIME_RQST;
+                msg->data.time_request = time_set;
+                queue.put(msg);
+            }
         }
     }
 }
-//Threads
+/*
+* LCD thread used to receive data from several other threads in the form of a message queue
+*/
 void thread_lcd(void const *args){
     osEvent evt;
-    int32_t sig;
-    state_t state;
-    int time_set = 0;
+    int time_set = 0; //used to calculate the elapsed time
     lcd.cls();
     while(1){
-        evt = queue.get(10); //wait for data message to post
+        evt = queue.get(osWaitForever); //wait for data message to post
         if(evt.status == osEventMessage){
-            message_t *msg = (message_t*)evt.value.p;
-            lcd.locate(70,0);
-            if(msg->state)
+            message_t *msg = (message_t*)evt.value.p; //get the message
+            lcd_mutex.lock(); //dont really need this as this is the only thread that uses the lcd
+            
+            if(msg->state){
+#ifdef DEBUG
+                if(msg->state == DONE){
+                    test_timer.stop(); //~75 us, if move print ahead it is 3145 us! printing takes a while
+                    lcd.locate(70,20);
+                    lcd.printf("%dus",test_timer.read_us());
+                    test_timer.reset();
+                }
+#endif
+                lcd.locate(70,0);
                 lcd.printf("State: %s ",stateStr[msg->state]);
+            }
             switch(msg->type){
                 case TIME_RQST:
                     time_set = msg->data.time_request;
                     lcd.locate(0,0);
-                    lcd.printf("Request: %3d ",time_set);
+                    lcd.printf("Request: %3ds ",time_set); //requested time
                     break; 
                 case TIME_ELPS:
                     lcd.locate(0,10);
-                    lcd.printf("Elapsed: %3d ",time_set - msg->data.time_elapsed);
+                    lcd.printf("Elapsed: %3ds ",time_set - msg->data.time_elapsed); //elapesed time
                     break; 
                 case TEMP_VAL:
                     lcd.locate(0,20);
-                    lcd.printf("Temp: %-3.2f  ",msg->data.temp);
+                    lcd.printf("Temp: %-3.2fF  ",msg->data.temp); //current cooking temp updated at different intervals
                     break;                
             }
-            mpool.free(msg);
+            lcd_mutex.unlock(); //free the mutex 
+            mpool.free(msg); //free the message
         }
     }
 }
-
+/*
+* Thread function to send the lcd the temp recieves timing from timer thread
+*/
 void thread_temp(void const *args){
     osEvent evt;
     int32_t sig;
@@ -327,8 +368,8 @@
         sig = evt.value.signals;
         switch(sig){
             case COOKING:
-                if(!(seconds++ % TEMP_INTERVAL_S)){
-                    temp+= 10.0;
+                if(!(seconds++ % TEMP_INTERVAL_S)){ //increase the temp if meets the interval
+                    temp+= TEMP_INCREASE_F;
                 }else{
                     sig = 0x0; //set to zero so don't print since no changes
                 }
@@ -341,13 +382,18 @@
         }
         if(sig){
             message_t *msg = mpool.alloc(); //allocate a new message
-            msg->state = (state_t)0; //don't pass a value
-            msg->type = TEMP_VAL;
-            msg->data.temp = seconds > 0 ? temp+9.0/5.0*therm.read()+32.0 : 0.0;
-            queue.put(msg);
+            if(msg != NULL){
+                msg->state = (state_t)0; //don't pass a value
+                msg->type = TEMP_VAL;
+                msg->data.temp = seconds > 0 ? temp+9.0/5.0*therm.read()+32.0 : 0.0;
+                queue.put(msg);
+            }
         }
     }
 }
+/*
+* Simple sound thread using pwm to buzz when cooking timer is finished
+*/
 void thread_sound(void const *args){
     osEvent evt;
     int32_t sig;
@@ -364,17 +410,20 @@
         }
     }
 }
-//helper function to thread_led used to blink led
+/*
+* Helper function to thread_led used to blink led
+*/
 void blink_led(void const *args){
     led1 = !led1;   
 }
-//Thread led to blink the light when cooking and stop when done
+/*
+* Thread led to blink the light when cooking and stop when done
+*/
 void thread_led(void const *args){
     RtosTimer timer(blink_led, osTimerPeriodic);
     osEvent evt;
     int32_t sig;
     while(1){
-        //may need to sleep somewhere to give up thread check to see if signal_wait equiv of wait
         evt = Thread::signal_wait(0); //will time out then loop not needed
         sig = evt.value.signals;
         switch(sig){
@@ -385,9 +434,6 @@
                 timer.stop();
                 led1 = 0;
                 break;
-            default:
-                //Thread::wait(2000); if wait can miss signal
-                break;   
         }
     }    
 }