2023 嵌入式作業系統分析與實做 Lab1 report
===
###### 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"
#include "task.h"
```
兩者得include的順序不能對調,否則會報錯。
##### Step.2
**Bounce**
所謂的 Bounce 是指我們在按下電源開關時,電壓不會從 0 伏直接升到 VDD 伏。而是在 0 及 VDD 間震盪好幾次,最後才在 VDD 端穩定下來。
一個電子產品若有彈跳現象的話,最常見到的「症狀」是按下一個開關,結果數字跳好幾下。

==導致電路將這類開關脈衝誤以為是資料流==。
**Debounce**
* Hardware debouncing:
* 在輸入的IO pin接一顆小電容到GND,等同於按鍵旁並聯一顆電容,因為電容的充放電需要時間,輸入pin上面的電壓不會瞬間變化。透過上拉電阻(Pull-up),與電容的選擇可以決定電容充放電的時間。所以當電容充放電的時間大於**Bounce持續的時間**,此時按鍵彈跳現象就被消除了!而上拉電阻、與下拉電阻是用來抗干擾與預設默認電平,干擾是因為pin腳懸空再加上電磁干擾就會產生這種不穩地的訊號。
```cpp=
void vTaskPressversion2(void){
for(;;){
if(HAL_GPIO_ReadPin(Blue_Button_GPIO_Port,GPIO_PIN_0))
HAL_Delay(10);//debounce
}
}
```
透過第`4`行讓它的擷取時間延後大於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))
{;}
}
}
}
```
透過第`7,8`可以檢查按鈕是否長按,以確保長按按鈕被誤會成要執行一次以上的操作。
##### 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產生MsgQueue,這樣task之間就可以利用xQueueSend,將資料傳入MsgQueue,或是透過xQueueReceive,讀出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();
}
}
}
```
* 第`5-6`行:是檢查Pin腳有無訊號傳入,且做Software debouncing。
* 第`9-13`行:計算接收到了幾次訊號,每次接受到訊號都要做task的切換。
* 第`14`行:透過MsgQueue通知其他task現在應該執行的動作。
* 第`15`行:主動釋放處理器,使得其他在ready list中的task能快一點被執行。
定義兩個LED狀態切換的task
```cpp=
void LEDTask_App(void *pvParameters)
{
unsigned int light = 1;
for(;;)
{
while(light==0){
HAL_GPIO_WritePin(led_orange_GPIO_Port, GPIO_PIN_13, GPIO_PIN_SET);
vTaskDelay(1000);
xQueueReceive(MsgQueue,&light, 0);
HAL_GPIO_WritePin(led_orange_GPIO_Port, GPIO_PIN_13, GPIO_PIN_RESET);
vTaskDelay(1000);
if(light==1) break;
}
while(light==1){
HAL_GPIO_WritePin(led_green_GPIO_Port, GPIO_PIN_12, GPIO_PIN_SET);
vTaskDelay(2000);
xQueueReceive(MsgQueue,&light, 0);
HAL_GPIO_WritePin(led_green_GPIO_Port, GPIO_PIN_12, GPIO_PIN_RESET);
vTaskDelay(2000);
HAL_GPIO_WritePin(led_red_GPIO_Port, GPIO_PIN_14, GPIO_PIN_SET);
vTaskDelay(2000);
xQueueReceive(MsgQueue,&light, 0);
HAL_GPIO_WritePin(led_red_GPIO_Port, GPIO_PIN_14, GPIO_PIN_RESET);
vTaskDelay(2000);
if(light==0) break;
}
}
* }
```
* 第`7`行:開啟橘燈。
* 第`9`行:跟MsgQueue要資料,如果MsgQueue的內容是空的,則此task會進入block state等待1000個Tick。若收到資料或是等待超過1000Tick則task會重新進入到ready state。
* 第`10`行:關掉橘燈
* 第`12`行:檢查是否要做狀態切換。
* 第`16`行:開啟綠燈。
* 第`18`行:跟MsgQueue要資料,如果MsgQueue的內容是空的,則此task會進入block state等待2000個Tick。若收到資料或是等待超過2000Tick則task會重新進入到ready state。
* 第`19`行:關掉綠燈
* 第`21`行:開啟紅燈。
* 第`23`行:跟MsgQueue要資料,如果MsgQueue的內容是空的,則此task會進入block state等待2000個Tick。若收到資料或是等待超過2000Tick則task會重新進入到ready state。
* 第`24`行:關掉紅燈
* 第`26`行:檢查是否要做狀態切換。
##### 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。
##### Step.7
Demo
[Github video](https://user-images.githubusercontent.com/48405514/224000585-fed0cc8b-83c4-4591-9b81-ad8049d7540d.mp4)