# 2025 成大嵌入式作業系統分析與實作 Lab1
[github](https://github.com/yuyuan0625/Embedded-System-FreeRTOS-Development)
## 題目說明
- Must using MultiTask, with Inter-Task Communication (ITC) mechanism
- Two Tasks: **LED-task** and **Button-task**
- **LED-task** will have two states (S1, S2)
- S1: First, the Red LED lights up for 1 second, followed by the Orange LED
lighting up for 1 second (with the Red LED turned off), then the Green LED
lights up for 1 second (with both the Red and Orange LEDs turned off). This
sequence repeats, cycling through the Red, Orange, and Green LEDs.
- S2: Only Orange LED is blinking (2 second ON, 2 second OFF, …)
- **Button-task**: If the button is pressed, the LED-task will switch to another state
(S1→S2).
- **Debounce handling**
- Edge detection handling
### bounce and debounce
bounce: 當機械按鈕被按下時,內部的機械接觸可能會導致多次開關信號,這稱為「彈跳效應」。
debounce 是用來限制函數在短時間內多次執行的機制,當偵測到按鈕按下時,先忽略短時間內的變化,直到訊號穩定,才確認一次按鍵輸入。

使用 [`vTaskDelay()`](https://blog.freertos.org/Documentation/02-Kernel/04-API-references/02-Task-control/01-vTaskDelay) 讓程式延遲一段時間再讀取穩定後的狀態。

```c
void vTaskBlueButtonControl(void* pvPatameters)
{
for(;;) {
if (HAL_GPIO_ReadPin(BLUE_BUTTON_GPIO_Port, GPIO_PIN_0))
{
vTaskDelay(pdMS_TO_TICKS(100)); // Delay 100 ms,等 Bounce 結束
// 等待按鈕鬆開,避免重複觸發
while (HAL_GPIO_ReadPin(BLUE_BUTTON_GPIO_Port, GPIO_PIN_0))
{
vTaskDelay(pdMS_TO_TICKS(10));
}
}
}
}
```
## 實作
### FreeRTOS設定
### STM32 Port 設定 (修改 `.ioc檔`)


> 將PD12、PD13、PD14、PA0做對應設定
> 左鍵: 選擇模式
> 右鍵: 重新命名
### 程式碼
1. include RTOS相關程式碼 (順序不能調換,會報錯)
2. 因為要使用 message queue,所以也要 include
```c
/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
#include "task.h"
/* USER CODE END Includes */
```
3. 宣告 message queue
```c
QueueHandle_t MsgQueue = NULL; // 宣告 queue handle
```
4. 藍色按鈕控制函式 `vTaskBlueButtonControl()`
透過訊息佇列 `xQueueSend()` 通知另一個 LED 控制任務 (`vTaskLEDControl`) 來切換 LED 狀態
```c
void vTaskBlueButtonControl(void* pvPatameters)
{
unsigned int state = 0;
unsigned int count = 0;
for(;;) {
// 檢查Pin腳有無訊號傳入
if (HAL_GPIO_ReadPin(BLUE_BUTTON_GPIO_Port, GPIO_PIN_0))
{
vTaskDelay(pdMS_TO_TICKS(100)); // Debounce
// 等待按鈕釋放避免產生多次觸發
while (HAL_GPIO_ReadPin(BLUE_BUTTON_GPIO_Port, GPIO_PIN_0))
{
vTaskDelay(pdMS_TO_TICKS(10));
}
// 更新 state
++count;
state = count & 0x01;
// 透過 MsgQueue 通知 vTaskLEDControl 現在 LED 的顯示狀態
xQueueSend(MsgQueue, &state, pdMS_TO_TICKS(10));
// 主動讓出 CPU 給其他任務
taskYIELD();
}
}
}
```
5. LED 狀態函式
- led_state == 1 時,LED 會紅→橙→綠 依序閃爍,每顆 LED 亮 1 秒
- led_state == 0 時,橙色 LED 會 閃爍 2 秒開、2 秒關
```c
void vTaskLEDControl(void* pvParameters)
{
unsigned int led_state = 1; // LED 預設狀態為 1 (循環三色)
for (;;)
{
unsigned int curr = 0; // 紀錄當前 LED 燈的狀態 (對應到 switch case)
// **模式 1: 三色循環**
curr = 0;
while (led_state == 1)
{
switch (curr)
{
case 0:
// **紅燈亮 1 秒**
HAL_GPIO_WritePin(RED_LED_GPIO_Port, GPIO_PIN_14, GPIO_PIN_SET);
vTaskDelay(pdMS_TO_TICKS(1000));
HAL_GPIO_WritePin(RED_LED_GPIO_Port, GPIO_PIN_14, GPIO_PIN_RESET);
break;
case 1:
// **橙燈亮 1 秒**
HAL_GPIO_WritePin(ORANGE_LED_GPIO_Port, GPIO_PIN_13, GPIO_PIN_SET);
vTaskDelay(pdMS_TO_TICKS(1000));
HAL_GPIO_WritePin(ORANGE_LED_GPIO_Port, GPIO_PIN_13, GPIO_PIN_RESET);
break;
case 2:
// **綠燈亮 1 秒**
HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GPIO_PIN_12, GPIO_PIN_SET);
vTaskDelay(pdMS_TO_TICKS(1000));
HAL_GPIO_WritePin(GREEN_LED_GPIO_Port, GPIO_PIN_12, GPIO_PIN_RESET);
break;
}
// **循環切換 LED 顏色**
++curr;
curr = curr % 3; // 確保 curr 在 0~2 之間循環
// **檢查訊息佇列 (非阻塞)**
// 若有新的 LED 狀態則更新
xQueueReceive(MsgQueue, &led_state, 0);
}
// **模式 2: 橙燈閃爍**
curr = 0;
while (led_state == 0)
{
switch (curr)
{
case 0:
// **橙燈亮 2 秒**
HAL_GPIO_WritePin(ORANGE_LED_GPIO_Port, GPIO_PIN_13, GPIO_PIN_SET);
vTaskDelay(pdMS_TO_TICKS(2000));
HAL_GPIO_WritePin(ORANGE_LED_GPIO_Port, GPIO_PIN_13, GPIO_PIN_RESET);
break;
case 1:
// **橙燈滅 2 秒**
HAL_GPIO_WritePin(ORANGE_LED_GPIO_Port, GPIO_PIN_13, GPIO_PIN_RESET);
vTaskDelay(pdMS_TO_TICKS(2000));
break;
}
// **切換閃爍狀態**
++curr;
curr = curr & 0x01; // 讓 curr 在 0 和 1 之間循環
// **檢查訊息佇列 (非阻塞)**
xQueueReceive(MsgQueue, &led_state, 0);
}
}
}
```