2021 嵌入式作業系統分析與實做 Lab1 report === [Lab1](https://docs.google.com/presentation/d/18tCM6G0R3o8nh5XU-BfqL14z4q-0595Z_Zs7Ge1lsOw/edit#slide=id.gc1072bcb39_1_135) contribute by `Liao712148` ###### tags: `FreeRTOS` ### Lab要求 * Must use MultiTask, e.g. One for LED , other for Button. * Inter process communication (IPC). * LED have two state * The green LED lights up for 5 seconds, then turns to a red LED lights up for 5 seconds (green LED off), and then switches back to the green LED lights up for 5 seconds (red LED off), and so on. * The red LED blinking. * If the button is pressed, the LED will switch to another state. * Debounce. * Edge detection. #### 實做流程 ##### Step.1 因為是透過**FreeRTOS**來實做所以要先 ```cpp= #include "FreeRTOS.h" ``` 接著**FreeRTOS**是以task的方式來實行funcion所以要 ```cpp= #include "task.h" ``` 兩者得include的順序不能對調,否則會報錯。 ##### Step.2 **Bounce** 所謂的 Bounce 是指我們在按下電源開關時,電壓不會從 0 伏直接升到 VDD 伏。而是在 0 及 VDD 間震盪好幾次,最後才在 VDD 端穩定下來。 一個電子產品若有彈跳現象的話,最常見到的「症狀」是按下一個開關,結果數字跳好幾下。 ![Bounce](https://upload.wikimedia.org/wikipedia/commons/a/ac/Bouncy_Switch.png) ==導致電路將這類開關脈衝誤以為是資料流==。 **Debounce** * Hardware debouncing: * 在輸入的IO pin接一顆小電容到GND,等同於按鍵旁並聯一顆電容,因為電容的充放電需要時間,輸入pin上面的電壓不會瞬間變化。透過上拉電阻(Pull-up),與電容的選擇可以決定電容充放電的時間。所以當電容充放電的時間大於**Bounce持續的時間**,此時按鍵彈跳現象就被消除了!而上拉電阻、與下拉電阻是用來抗干擾與預設默認電平,干擾是因為pin腳懸空再加上電磁干擾就會產生這種不穩地的訊號。 * 參考資料 * [Hardware debounce](https://www.quora.com/What-is-the-meaning-of-debounce-in-8051-microcontroller) * [Hardware debounce](http://puppyodie.blogspot.com/2016/04/button-debounce.html) * [Hardware debounce](http://edisonyu71.blogspot.com/2017/08/debunce.html) * [上拉、下拉電阻](https://www.youtube.com/watch?v=k_GAuSONCqo) * Software debouncing: * 讓擷取時間延後大於Bounce Time。 ```cpp= void vTaskPressversion2(void) { /* ... */ for(;;) { if(HAL_GPIO_ReadPin(Blue_Button_GPIO_Port,GPIO_PIN_0)) { HAL_Delay(10);//debounce /* ... */ } } } ``` 透過第`9`行讓它的擷取時間延後大於Bounce Time。 ##### Step.3 長按按鈕的話有可能會傳送一次以上的訊號,與Lab要求不合。 ```cpp= void vTaskPressversion2(void) { /* ... */ for(;;) { if(HAL_GPIO_ReadPin(Blue_Button_GPIO_Port,GPIO_PIN_0)) { HAL_Delay(10);//debounce while(HAL_GPIO_ReadPin(Blue_Button_GPIO_Port,GPIO_PIN_0)) {;} /* ... */ } } } ``` 透過第`10,11`可以檢查按鈕是否長按,當讀到PIN腳有訊號時一定要等到訊號消失才開始下面的操作,否則會一直stall。以確保長按按鈕被誤會成要執行一次以上的操作。 ##### Step.4 要用**message queue**讓task之間可以做溝通,所以要先 ```cpp= #include "queue.h" ``` ```cpp= QueueHandle_t MsgQueue = NULL; int main(void) { /* ... */ MsgQueue = xQueueCreate(1,sizeof(unsigned int)); /* ... */ while (1) { /* ... */ } } ``` 透過[xQueueCreate](https://www.freertos.org/a00116.html)產生MsgQueue,這樣task之間就可以利用[xQueueSend](https://www.freertos.org/a00117.html),將資料傳入MsgQueue,或是透過[xQueueReceive](https://www.freertos.org/a00118.html),讀出MsgQueue中的資料。 ##### Step.5 定義偵測button按壓的task ```cpp= void vTaskPressversion2(void) { unsigned int task = 0; unsigned int count = 0; for(;;) { if(HAL_GPIO_ReadPin(Blue_Button_GPIO_Port,GPIO_PIN_0)) { HAL_Delay(10);//debounce while(HAL_GPIO_ReadPin(Blue_Button_GPIO_Port,GPIO_PIN_0)) {;} ++count ; if(count & 0x01) task = 1; else task = 0; xQueueSend(MsgQueue,(int * ) &task,1); taskYIELD(); } } } ``` * 第`7~12`行:是檢查Pin腳有無訊號傳入,且做Software debouncing。 * 第`13~17`行:計算接收到了幾次訊號,每次接受到訊號都要做task的切換。 * 第`18`行:透過MsgQueue通知其他task現在應該執行的動作。 * 第`19`行:主動釋放處理器,使得其他在ready list中的task能快一點被執行。 定義兩個LED狀態切換的task ```cpp= void traffic_light(void) { unsigned int rcv = 0; for(;;) { while(rcv==0) { HAL_GPIO_WritePin(Green_LED_GPIO_Port,GPIO_PIN_12,GPIO_PIN_SET); xQueueReceive(MsgQueue,&rcv, 5000); HAL_GPIO_WritePin(Green_LED_GPIO_Port,GPIO_PIN_12,GPIO_PIN_RESET); if(rcv==1) break; HAL_GPIO_WritePin(Red_LED_GPIO_Port,GPIO_PIN_14,GPIO_PIN_SET); xQueueReceive(MsgQueue,&rcv, 5000); HAL_GPIO_WritePin(Red_LED_GPIO_Port,GPIO_PIN_14,GPIO_PIN_RESET); if(rcv==1) break; } while(rcv==1) { HAL_GPIO_WritePin(Red_LED_GPIO_Port,GPIO_PIN_14,GPIO_PIN_SET); xQueueReceive(MsgQueue,&rcv, 500); HAL_GPIO_WritePin(Red_LED_GPIO_Port,GPIO_PIN_14,GPIO_PIN_RESET); xQueueReceive(MsgQueue,&rcv, 500); if(rcv==0) break; } } } ``` * 第`8`行:開啟綠燈。 * 第`9`行:跟MsgQueue要資料,如果MsgQueue的內容是空的,則此task會進入block state等待5000個Tick。若收到資料或是等待超過5000Tick則task會重新進入到ready state。 * 第`10`行:關掉綠燈 * 第`11,12`行:檢查是否要做狀態切換。 * 第`13`行:開啟紅燈。 * 第`14`行:跟MsgQueue要資料,如果MsgQueue的內容是空的,則此task會進入block state等待5000個Tick。若收到資料或是等待超過5000Tick則task會重新進入到ready state。 * 第`15`行:關掉紅燈 * 第`16,17`行:檢查是否要做狀態切換。 ##### Step.6 初始化task ```cpp= int main(void) { /* ... */ xTaskCreate(vTaskPressversion2,"change Task",1000,NULL,1,NULL); xTaskCreate(traffic_light,"switch between red and green",1000,NULL,1,NULL); vTaskStartScheduler(); while (1) { /* ... */ } } ``` 兩個task的priority都設成相同,因為沒有要對task做其他的操作,所以task handler可以設成NULL。 #### 心得 謝謝助教的幫忙