# STM32 教學系列 第 13 節：FreeRTOS（實時操作系統）

# STM32 教學系列 第 13 節：FreeRTOS（實時操作系統）

## 🎯 學習目標

1. **理解 RTOS 基本概念** - 掌握任務調度、同步機制
2. **配置 FreeRTOS** - 在 STM32 上運行多任務
3. **實現任務通訊** - 隊列、信號量、互斥鎖

---

## 🎓 RTOS 基礎

### RTOS 是什麼？

RTOS (Real-Time Operating System) 是實時操作系統。用途：
- **任務調度**：決定哪個任務何時運行
- **優先級管理**：高優先級任務優先執行
- **同步機制**：保護共享資源
- **定時準確**：毫秒級任務切換

### FreeRTOS 核心概念

| 概念 | 說明 |
|------|------|
| **Task（任務）** | 獨立的程式流，有各自的棧和優先級 |
| **Priority（優先級）** | 0 ~ configMAX_PRIORITIES-1，越高越優先 |
| **Scheduler（調度器）** | 根據優先級決定運行哪個任務 |
| **Queue（隊列）** | 任務間安全傳遞訊息 |
| **Semaphore（信號量）** | 控制資源存取 |
| **Mutex（互斥鎖）** | 防止優先級反轉 |

### STM32 上的 FreeRTOS

優勢：
- ✅ 完全免費開源
- ✅ 佔用空間少（最低 3KB）
- ✅ 支援優先級搶占式調度
- ✅ 豐富的同步原語

---

## ⚙️ CubeMX 配置

### 步驟 1：啟用 FreeRTOS

1. 左側選 **Middleware** → 搜尋 **FreeRTOS**
2. **Interface**：CMSIS_V2

### 步驟 2：配置時基

1. **System Timers and Clocks** → **SysTick timer**
2. 設定 **SysTick** 為 1ms（FreeRTOS 心跳）

### 步驟 3：配置任務堆棧

**FreeRTOS**：
- **TOTAL_HEAP_SIZE**：4096 bytes（根據需要調整）
- **configMAX_PRIORITIES**：5（最多 5 級優先級）

### 步驟 4：生成程式碼

---

## 💻 完整程式碼

### FreeRTOS 多任務示例：main.c

```c
/* STM32 Lesson 13 - FreeRTOS
 * 功能：多任務調度、任務通訊
 * 難度：高級
 */

#include "main.h"
#include "cmsis_os.h"
#include "usart.h"
#include <stdio.h>
#include <string.h>

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
void UART_Print(const char *format, ...);

/* 任務宣告 */
void Task1_Start(void *argument);
void Task2_Start(void *argument);
void Task3_Start(void *argument);

UART_HandleTypeDef huart1;

/* 隊列宣告 */
osMessageQueueId_t queueHandle;

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();

  UART_Print("\r\n=== FreeRTOS Multi-Task Demo ===\r\n");

  /* 創建隊列 */
  queueHandle = osMessageQueueNew(10, sizeof(uint32_t), NULL);

  /* 創建任務 */
  osThreadNew(Task1_Start, NULL, NULL);  /* 優先級：osPriorityNormal */
  osThreadNew(Task2_Start, NULL, NULL);  /* 優先級：osPriorityNormal */
  osThreadNew(Task3_Start, NULL, NULL);  /* 優先級：osPriorityHigh */

  /* 啟動 RTOS 調度器 */
  osKernelStart();

  while (1);
}

/**
  * @brief Task 1：每 1 秒發送一個數值到隊列
  */
void Task1_Start(void *argument)
{
  uint32_t counter = 0;

  while (1)
  {
    UART_Print("Task1: Sending data %lu\r\n", counter);
    osMessageQueuePut(queueHandle, &counter, 0, 0);
    counter++;
    osDelay(1000);  /* 延遲 1 秒 */
  }
}

/**
  * @brief Task 2：從隊列接收資料並列印
  */
void Task2_Start(void *argument)
{
  uint32_t rx_data;
  osStatus_t status;

  while (1)
  {
    status = osMessageQueueGet(queueHandle, &rx_data, NULL, osWaitForever);
    if (status == osOK)
    {
      UART_Print("Task2: Received %lu\r\n", rx_data);
    }
  }
}

/**
  * @brief Task 3：高優先級任務，每 500ms 執行一次
  */
void Task3_Start(void *argument)
{
  while (1)
  {
    UART_Print("Task3: High Priority Task Running\r\n");
    osDelay(500);  /* 延遲 500ms */
  }
}

void UART_Print(const char *format, ...)
{
  char buffer[100];
  va_list args;
  va_start(args, format);
  vsnprintf(buffer, sizeof(buffer), format, args);
  va_end(args);
  
  HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY);
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 7;

  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    Error_Handler();

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                              | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    Error_Handler();
}

static void MX_GPIO_Init(void)
{
  __HAL_RCC_GPIOA_CLK_ENABLE();
}

static void MX_USART1_UART_Init(void)
{
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;

  if (HAL_UART_Init(&huart1) != HAL_OK)
    Error_Handler();
}

void Error_Handler(void)
{
  while(1);
}
```

---

## 🔍 預期結果

### UART 輸出示例

```
=== FreeRTOS Multi-Task Demo ===
Task3: High Priority Task Running
Task1: Sending data 0
Task2: Received 0
Task3: High Priority Task Running
Task1: Sending data 1
Task2: Received 1
Task3: High Priority Task Running
...
```

### 執行流程說明

1. **Task1** 每 1000ms 發送計數值到隊列
2. **Task2** 在隊列中等待，收到資料後立即列印
3. **Task3** 每 500ms 執行一次（優先級最高）
4. 調度器根據優先級和時間片進行任務切換

---

## 💡 高級應用

### 常用 CMSIS-RTOS 2 API

| 函數 | 用途 |
|------|------|
| `osThreadNew()` | 創建任務 |
| `osDelay()` | 任務延遲（毫秒） |
| `osMessageQueueNew()` | 創建訊息隊列 |
| `osMessageQueuePut()` | 訊息入隊 |
| `osMessageQueueGet()` | 訊息出隊 |
| `osSemaphoreNew()` | 創建信號量 |
| `osMutexNew()` | 創建互斥鎖 |
| `osKernelStart()` | 啟動 RTOS 調度器 |

### 進階：信號量同步

```c
/* 兩個任務的同步範例 */
osSemaphoreId_t semaphore;

void Task_Producer(void *argument)
{
  while (1)
  {
    /* 進行一些工作 */
    UART_Print("Producer: Data ready\r\n");
    
    /* 釋放信號量，喚醒消費者 */
    osSemaphoreRelease(semaphore);
    
    osDelay(1000);
  }
}

void Task_Consumer(void *argument)
{
  while (1)
  {
    /* 等待信號量被釋放 */
    if (osSemaphoreAcquire(semaphore, osWaitForever) == osOK)
    {
      UART_Print("Consumer: Processing data\r\n");
    }
  }
}
```

### 進階：互斥鎖保護共享資源

```c
/* 多個任務共享 UART 列印 */
osMutexId_t uart_mutex;

void Safe_UART_Print(const char *format, ...)
{
  /* 獲取互斥鎖 */
  osMutexAcquire(uart_mutex, osWaitForever);
  
  char buffer[100];
  va_list args;
  va_start(args, format);
  vsnprintf(buffer, sizeof(buffer), format, args);
  va_end(args);
  
  HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY);
  
  /* 釋放互斥鎖 */
  osMutexRelease(uart_mutex);
}
```

---

## 📚 進階主題

### 1. 事件標誌（Event Flags）
用於多個任務的事件同步

```c
osEventFlagsId_t event_flags;

/* 設置標誌 */
osEventFlagsSet(event_flags, 0x01);

/* 等待標誌 */
osEventFlagsWait(event_flags, 0x01, osFlagsWaitAny, osWaitForever);
```

### 2. 軟體計時器（Software Timer）
實現週期性任務或延遲執行

```c
void Timer_Callback(void *argument)
{
  UART_Print("Timer fired!\r\n");
}

osTimerId_t timer = osTimerNew(Timer_Callback, osTimerPeriodic, NULL, NULL);
osTimerStart(timer, 1000);  /* 每 1000ms 執行一次 */
```

### 3. 任務通知（Task Notification）
輕量級的任務喚醒機制（比隊列和信號量快）

```c
/* Task A 喚醒 Task B */
osThreadFlagsSet(task_b_id, 0x01);

/* Task B 等待通知 */
osThreadFlagsWait(0x01, osFlagsWaitAny, osWaitForever);
```

### 4. 動態記憶體管理
RTOS 提供的安全動態分配

```c
void *pvPortMalloc(size_t xSize);  /* 動態分配 */
void vPortFree(void *pv);          /* 動態釋放 */
```

---

## 🎯 性能監測

### 任務運行時統計

```c
/* 獲取任務資訊 */
osThreadState_t state = osThreadGetState(task_id);

/* 狀態值 */
switch (state)
{
  case osThreadInactive:
    UART_Print("Task is inactive\r\n");
    break;
  case osThreadReady:
    UART_Print("Task is ready to run\r\n");
    break;
  case osThreadRunning:
    UART_Print("Task is currently running\r\n");
    break;
  case osThreadBlocked:
    UART_Print("Task is blocked\r\n");
    break;
}
```

---

## 🎉 完成 13 節完整教學！

### 學習成果

✅ STM32 嵌入式開發完整知識體系  
✅ 從基礎到工業級應用  
✅ 13 個主題 50+ 個實踐範例  
✅ 可直接應用於實務項目  

### 建議進階方向

- 🚁 **無人機飛控系統** - 使用 RTOS 進行多傳感器融合
- 📱 **物聯網 IoT 設備** - 集成通訊協議和雲連接
- 🏭 **工業控制系統** - MODBUS 網路和實時控制
- 🤖 **機器人控制板** - 複雜的多軸運動控制
- 🚗 **汽車電子應用** - CAN 網路和診斷協定

---

## 📖 推薦閱讀

- **FreeRTOS 官方文檔**：https://www.freertos.org/
- **CMSIS-RTOS 2 標準**：ARM 官方規範
- **嵌入式實時系統設計**：相關教科書

---

**🚀 STM32 完整系列教學圓滿完成！祝您在嵌入式開發中取得成就！**