# 2025 嵌入式作業系統分析與實作 Lab 0
## lab 目標
* 熟悉 STM32 系列 CPU 的核心特性與開發環境
* 了解 Cort-M4 系列 CPU 的核心特性與開發環境
* 建立開發與編譯流程,掌握交叉編譯與燒錄方式
* 嘗試驗證 FreeRTOS 是否能順利在開發板上運行
## 實作流程
### 1. Download STM32CubeIDE
下載專用的IDE: [STM32CubeIDE](https://www.st.com/en/development-tools/stm32cubeide.html)
 
### 2. Download FreeRTOS source code
下載 FreeRTOS 作業系統原始碼: [Github: FreeRTOS v10.2.1](https://github.com/FreeRTOS/FreeRTOS/tree/V10.2.1)
 
### 3. 建立專案
在 IDE 選擇開發板並建立專案
 
### 4. 整合 FreeRTOS 至 STM32CubeIDE 專案
FreeRTOS 支援多種平台與CPU架構,僅移植與 STM32 Cortex-M4 平台相關的必要檔案,包含以下:
```txt!
FreeRTOS/Source/indlude // FreeRTOS 核心的共同標頭檔
FreeRTOS/Source/*.c // 核心功能實作
FreeRTOS/Demo/CORTEX_M4F_STM32F407ZG-SK/FreeRTOSConfig.h // 參數與功能開關
FreeRTOS/Source/portable/MemMang/heap_4.c // 記憶體分配的策略
FreeRTOS/Source/portable/GCC/ARM_CM4F // 專為 GCC 編譯器 + ARM Cortex-M4F架構寫的 porting 層
```
並修改部分原始碼避免相容性問題:
``` C=44
// @file FreeRTOSConfig.h
//#ifdef __ICCARM__
#if defined(_ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
```
> `45`:僅針對 IAR 編譯器(ICCARM)才會執行
> `46`:確保不管使用哪一種主流編譯器,都會執行 `47-48`
> \_\_CC_ARM\_\_:Keil 編譯器(ARM Compiler)
> \_\_GNUC\_\_:GCC 編譯器(STM32CubeIDE 就是使用這個)
```C=51
// @file FreeRTOSConfig.h
#define configUSE_IDLE_HOOK 0 // 原為 1
#define configUSE_TICK_HOOK 0 // 原為 1
#define configCHECK_FOR_STACK_OVERFLOW 0 // 原為 1 (# 65)
#define configUSE_MALLOC_FAILED_HOOK 0 // 原為 1 (# 67)
```
在預設狀態下,`FreeRTOSConfig.h` 中有啟用多個 Hook function,需實作對應涵式,為避免不必要的錯誤或中斷,先設為 0,以下為各參數作用:
|||
|---|---|
| `configUSE_IDLE_HOOK` | 是否啟用 Idle Hook(CPU idle 時會呼叫的函式) |
| `configUSE_TICK_HOOK` | 是否啟用 Tick Hook(每個 tick interrupt 呼叫一次) |
| `configCHECK_FOR_STACK_OVERFLOW` | 當 `pvPortMalloc()` 分配失敗時是否呼叫錯誤處理函式 |
| `configUSE_MALLOC_FAILED_HOOK` | 是否啟用堆疊溢出檢查機制,2 表示使用更完整的檢查 |
 
### 5. FreeRTOS 相容設定:Basic Timer 與 NVIC
為避免衝突並確保 FreeRTOS 能正確運行,修改 `.ioc` 檔:
* **修改 SYS 的 Timebase Source 為 TIM7**
預設的 SysTick timer 會與 FreeRTOS 的 RTOS tick 衝突,因此改成使用基本計時器(如 TIM7)作為 Timebase,可避免衝突。
* **設定 Debug 模式為 Serial Wire**
可確保使用 ST-Link 進行 Debug 與燒錄的連線順利。
* **修改 NVIC Priority Group 為 4 bits**
為了讓 FreeRTOS 更好地控制中斷優先順序,將優先權分組設為 4 bits(代表全部 bits 都拿來處理 preemption priority)。


:bangbang: 每次修改 `.ioc` 檔並儲存後,系統詢問是否生成程式碼,會覆蓋掉部分程式碼,說明如下:
|可能會被覆蓋的檔案 | 說明 |
|---|---|
|`Src/` 資料夾底下的 `main.c`, `stm32xxxx_it.c`, `syscalls.c`, `system_stm32xxxx.c` |裡面非使用者區塊以外的程式碼 都會被自動重新產生|
|`Inc/` 資料夾裡的 `main.h`, `stm32xxxx_it.h`, `gpio.h` 等|若有自訂變數或未放在保留區,可能被蓋掉|
|`.c/.h` 中 HAL 初始化區域(如 GPIO、TIM、USART) |只要在 .ioc 裡做了變更,這些設定都會依照新內容重寫|
 
### 6. 確保使用 FreeRTOS interrupt
生成程式碼後需在 `stm32f4xx_it.c` 及 `stm32f4xx_it.h` 檔案中註解以下 handler,以確保不會和 FreeRTOS 造成衝突
(`stm32f4xx_it.c` 中定義;`stm32f4xx_it.h`中宣告)
|Handler | FreeRTOS 的用途|
|---|---
|`PendSV_Handler` |進行任務切換(Context Switch) 的關鍵|
|`SVC_Handler` |系統呼叫服務,FreeRTOS 透過它來啟動第一個任務|
|`SysTick_Handler`| 實作 tick timer,決定 RTOS 的排程頻率|
若不註解掉,可能造成:
* 編譯器出現 duplicate symbol 錯誤,因為這些 handler 在 FreeRTOS 的 porting code 已有定義
* 若編譯未出錯,也可能呼叫到錯誤的 handler 實作
 
### 7. 測試 Porting 是否完成
```C=19
// @file main.c
#include "main.h"
#include "FreeRTOS.h" // 載入整個 FreeRTOS 核心、包含系統參數與設定
#include "task.h" // 包含與 Task 操作相關的 API
```
> `21-22`:在 `main.c` 內補兩個 header
```C=76
// @file main.c
int main(void){
/* ... */
vTaskStartScheduler();
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//照理來說不會進入 while 迴圈
}
}
```
>並在 while 迴圈上面補 `vTaskStartScheduler();`,做為 FreeRTOS 的任務排程器啟動點,讓預先建立好的任務可以開始被排入 CPU 執行,ROTS 排程器啟動後,主程式的 `main()` 幾乎不再返回,系統轉為由 RTOS 控制。
 
### 8. 簡單任務實作 - 綠燈閃爍
```C=58
// @file main.c
TaskHandle_t xHandle=NULL;
void LED_Task( void ){
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15, GPIO_PIN_RESET);
for(;;){
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET );
vTaskDelay(500);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET );
vTaskDelay(500);
}
}
main(){
/* ... */
xTaskCreate(LED_Task, "NAME", 128, NULL, 1, &xHandle);
}
```
> `59`:宣告一個任務控制代碼(Task handle),可以用來追蹤或控制這個任務(例如暫停、刪除)
> `61`:先將 D12~D15 四顆 LED 全部關閉(Low)
> `64-67`:讓綠燈亮起 500 tick,再熄滅 500 tick
> `72`:在 `main()` 中使用 `xTaskCreate()` 建立任務
 
#### `xTaskCreate` 函式介紹
`xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask)`
|參數名稱|說明|
|---|---|
|`pvTaskCode`|任務函式名稱|
|`pcName`|任務名稱(optional)|
|`usStackDepth`|stack 大小(以 word 為單位)|
|`pvParameters`|傳給任務的參數|
|`uxPriority`|Priority|
|`pxCreatedTask`|存放任務控制代碼的變數指標|
---
GitHub
[Embedded-System-FreeRTOS-Development/lab_0/](https://github.com/sihjie/Embedded-System-FreeRTOS-Development/tree/main/lab_0)
---
[Demo Video](https://github.com/sihjie/Embedded-System-FreeRTOS-Development/blob/main/lab_0/README.md#demo-video)