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 端穩定下來。
一個電子產品若有彈跳現象的話,最常見到的「症狀」是按下一個開關,結果數字跳好幾下。

==導致電路將這類開關脈衝誤以為是資料流==。
**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。
#### 心得
謝謝助教的幫忙