A simple microwave demo
Dependencies: C12832_lcd LM75B mbed-rtos mbed
rtos_hwk7.cpp
- Committer:
- joeroop
- Date:
- 2014-04-04
- Revision:
- 4:cd87e2050344
- Parent:
- 3:4fb0721ce794
File content as of revision 4:cd87e2050344:
/* 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" #include "rtos.h" #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); DigitalOut led4(LED4); 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; //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, //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; //this is for signaling of threads with proxies char *stateStr[] = {"NULL","WAIT","COOK","DONE"}; typedef enum {TEMP_VAL, TIME_ELPS, TIME_RQST} data_t; //message data types typedef struct { state_t state; data_t type; union { float temp; int time_elapsed; //seconds of cooking int time_request; //seconds } data; } 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; bool debounce(void); 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); void thread_lcd(void const *args); void thread_temp(void const *args); void thread_sound(void const *args); void thread_led(void const *args); void thread_timer(void const *args); //ISRs void isrTimeUp(void); void isrTimeDn(void); void isrStart(void); void isrStop(void); void isrDoor(void); int main(void){ debounceTimer.start(); //interrupts debounce everything except for the door irptTimeUp.rise(&isrTimeUp); irptTimeDn.rise(&isrTimeDn); irptStart.rise(&isrStart); irptStop.rise(&isrStop); irptDoor.rise(&isrDoor); //init the threads Thread t1(thread_state); Thread t2(thread_temp); Thread t3(thread_sound); Thread t4(thread_lcd); 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; t1.set_priority(osPriorityRealtime); //state machine most important thread proxy_state->signal_set(WAITING); //initialize all the threads while(1){ Thread::wait(250); } } //function for a global debounce on the isrstick bool debounce(void){ if(debounceTimer.read_ms() > DEBOUNCE_TIME_MS){ debounceTimer.reset(); return true; }else{ return false; } } //ISRs void isrTimeUp(void){ if(debounce()){ proxy_state->signal_set(TIMEUP); //left } } void isrTimeDn(void){ if(debounce()){ proxy_state->signal_set(TIMEDN); //right } } void isrStart(void){ if(debounce()){ proxy_state->signal_set(START); //up } } void isrStop(void){ if(debounce()){ proxy_state->signal_set(STOP); //down } } 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 this is where control happens */ void thread_state(void const *args){ osEvent evt; int32_t mask; bool openDoor = false; //start with door closed state_t state = WAITING; //WAITING, COOKING, DONE while(1){ evt = Thread::signal_wait(0); mask = evt.value.signals; switch(mask){ 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 && state == WAITING){ //can't cook if door is open state = COOKING; proxy_led->signal_set(state); proxy_timer->signal_set(state); proxy_temp->signal_set(state); } break; case DONE: //timer can signal this state = 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); #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); break; case TIMEDN: //change the timer down if(state == WAITING) proxy_timer->signal_set(TIMEDN); break; case START: proxy_state->signal_set(COOKING); break; case STOP: 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; //visual for when door is open proxy_state->signal_set(state); //signal back to state thread break; } } } /* * 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){ //time is up signal state machine to done state = DONE; 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 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; int *ptime = &time; RtosTimer timer(send_time, osTimerPeriodic,(void*)ptime); osEvent evt; int32_t sig; state_t state; while(1){ evt = Thread::signal_wait(0); //will time out then loop not needed sig = evt.value.signals; switch(sig){ case WAITING: state = WAITING; break; case COOKING: 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: state = DONE; time = INIT_COOK_TIME_S; //if(time > 0) time_set = time; //else time = INIT_COOK_TIME_S; timer.stop(); break; case TIMEUP: time_set = (++time_set) > MAX_COOK_TIME_S ? MAX_COOK_TIME_S : time_set; //check limits break; case TIMEDN: 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 to the lcd if(msg != NULL){ msg->state = state; msg->type = TIME_RQST; msg->data.time_request = time_set; queue.put(msg); } } } } /* * 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; int time_set = 0; //used to calculate the elapsed time lcd.cls(); while(1){ evt = queue.get(osWaitForever); //wait for data message to post if(evt.status == osEventMessage){ 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: %3ds ",time_set); //requested time break; case TIME_ELPS: lcd.locate(0,10); lcd.printf("Elapsed: %3ds ",time_set - msg->data.time_elapsed); //elapesed time break; case TEMP_VAL: lcd.locate(0,20); lcd.printf("Temp: %-3.2fF ",msg->data.temp); //current cooking temp updated at different intervals break; } 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; int seconds = -1; float temp = 0.0; while(1){ evt = Thread::signal_wait(0); sig = evt.value.signals; switch(sig){ case COOKING: 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 } break; case WAITING: case DONE: seconds = -1; temp = 0.0; break; } if(sig){ message_t *msg = mpool.alloc(); //allocate a new message 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; while(1){ evt = Thread::signal_wait(0); sig = evt.value.signals; switch(sig){ case DONE: spkr.period(1.0/5000); spkr=0.5; Thread::wait(1000); spkr=0.0; break; } } } /* * 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 */ void thread_led(void const *args){ RtosTimer timer(blink_led, osTimerPeriodic); osEvent evt; int32_t sig; while(1){ evt = Thread::signal_wait(0); //will time out then loop not needed sig = evt.value.signals; switch(sig){ case COOKING: timer.start(250); break; case DONE: timer.stop(); led1 = 0; break; } } }