📦 準備工作
-
獲取FreeRTOS源碼:
-
訪問?FreeRTOS官網?或其?GitHub倉庫?下載最新版內核源碼。
-
你也可以使用Git克隆(注意要包含子模塊):
git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules
。
-
-
準備STM32基礎工程:
-
使用?STM32CubeMX?生成一個針對你芯片型號的裸機工程(例如一個簡單的LED閃爍工程),配置好時鐘樹、調試接口(如SYS)和你需要的外設(如GPIO、USART等)。
-
確保這個基礎工程在你的開發板上能夠編譯并通過(例如,LED能閃爍)。
-
📁 文件組織與添加
在你的STM32工程目錄下(通常與Core
、Drivers
等文件夾同級),創建一個用于存放FreeRTOS源碼的文件夾,例如Middlewares/FreeRTOS
。然后按照下表的指引將必要的文件復制到相應位置:
所需文件 | 源路徑 (基于FreeRTOS源碼根目錄) | 目標路徑 (你的工程目錄) | 作用描述 |
---|---|---|---|
核心源文件(.c) | FreeRTOS/Source/*.c ?(如?tasks.c ,?queue.c ,?list.c 等) | Middlewares/FreeRTOS/Source | FreeRTOS內核的核心功能實現 |
核心頭文件(.h) | FreeRTOS/Source/include/*.h | Middlewares/FreeRTOS/Source/include | FreeRTOS內核的頭文件,提供API和數據類型定義 |
移植層文件 | FreeRTOS/Source/portable/[Compiler]/[Architecture]/* | Middlewares/FreeRTOS/Source/portable | 與編譯器及CPU架構相關的移植代碼(關鍵選擇,見下文說明) |
內存管理實現 | FreeRTOS/Source/portable/MemMang/heap_x.c ?(選一個) | Middlewares/FreeRTOS/Source/portable/MemMang | FreeRTOS的動態內存管理方案(五選一,通常推薦heap_4.c ) |
配置文件 | FreeRTOS/Demo/[Demo項目]/FreeRTOSConfig.h | 通常放在工程Inc 目錄或Middlewares/FreeRTOS | FreeRTOS內核的配置文件(需根據你的芯片和需求修改) |
🔧 關鍵選擇說明:
-
移植層文件 ([Compiler]和[Architecture]):
-
[Compiler]: 根據你使用的開發環境選擇。
-
Keil MDK: 選擇?
RVDS
?目錄。 -
IAR: 選擇?
IAR
?目錄。 -
GCC (如STM32CubeIDE, CLion): 選擇?
GCC
?目錄。
-
-
[Architecture]: 根據你STM32芯片的Cortex內核型號選擇。
-
Cortex-M0:?
ARM_CM0
-
Cortex-M3:?
ARM_CM3
-
Cortex-M4?(無FPU):?
ARM_CM4F
-
Cortex-M7?(有FPU):?
ARM_CM7
-
*例如,STM32F103是Cortex-M3,STM32F407是Cortex-M4。*
-
-
-
內存管理實現 (heap_x.c):
FreeRTOS提供了5種內存管理方案,通常選擇?heap_4.c
(支持內存分配與釋放,并能有效減少碎片)。對于極其簡單或從不釋放內存的應用,也可考慮?heap_1.c
。
📝 操作步驟:
-
在你的工程目錄下(例如
Middlewares/FreeRTOS
)創建相應的子文件夾:Source
,?Source/include
,?Source/portable
。 -
根據上表和你的芯片、編譯器情況,將FreeRTOS源碼包中對應的文件復制到剛剛創建的相應文件夾中。
-
從FreeRTOS源碼包的
Demo
文件夾里,找一個與你芯片型號相近的Demo工程,將其中的FreeRTOSConfig.h
文件復制到你的工程目錄下(通常放在Inc
目錄下便于包含)。
?? 工程配置與修改
1. 添加文件到IDE工程
-
打開你的Keil MDK(或其他IDE)工程。
-
在IDE中創建新的分組(Group),例如 "FreeRTOS_CORE", "FreeRTOS_PORTABLE"。
-
將剛才復制到
Middlewares/FreeRTOS/Source
下的.c
文件(如tasks.c
,?queue.c
等)添加到 "FreeRTOS_CORE" 分組。 -
將你選擇的內存管理文件(如
heap_4.c
)和移植層文件(如port.c
)添加到 "FreeRTOS_PORTABLE" 分組。 -
不要添加其他未選擇的內存管理文件和移植層文件。
2. 添加頭文件路徑
在IDE的工程設置("Options for Target" -> "C/C++" -> "Include Paths")中,添加以下頭文件路徑35:
-
../Middlewares/FreeRTOS/Source/include
-
../Middlewares/FreeRTOS/Source/portable/[Compiler]/[Architecture]
?(例如?../Middlewares/FreeRTOS/Source/portable/RVDS/ARM_CM3
) -
確保也包含了存放
FreeRTOSConfig.h
文件的路徑(如../Inc
)。
3. 修改FreeRTOSConfig.h
FreeRTOSConfig.h
是FreeRTOS的核心配置文件,你需要根據你的芯片和項目需求進行修改。以下是一些最關鍵的配置項34:
配置宏 | 說明與典型設置 |
---|---|
configCPU_CLOCK_HZ | 設置為你STM32芯片的主時鐘頻率(Hz),例如STM32F103為72000000,STM32F407為168000000。可直接使用?SystemCoreClock 。 |
configTICK_RATE_HZ | 系統節拍頻率。通常設置為1000Hz,表示1ms一個時鐘節拍。 |
configTOTAL_HEAP_SIZE | FreeRTOS動態內存堆的總大小。根據你計劃創建的任務、隊列等數量估算。如果不夠,任務創建會失敗。例如可先設置為(10 * 1024)(10KB),后續再調整。 |
configMAX_PRIORITIES | 系統支持的最大任務優先級數。設置一個夠用的值即可,如5-8,不是越大越好。 |
configKERNEL_INTERRUPT_PRIORITY``configMAX_SYSCALL_INTERRUPT_PRIORITY | 中斷優先級配置,非常重要!需要根據你芯片的NVIC優先級位數(如STM32F1/F4是4位,即0-15)和你的應用來設置。設置錯誤可能導致系統不穩定或無法運行。務必仔細查閱FreeRTOS手冊和芯片數據手冊。 |
configUSE_PREEMPTION | 設置為1啟用搶占式調度器,這是最常用的模式。 |
configUSE_TIMERS``configTIMER_TASK_PRIORITY``configTIMER_QUEUE_LENGTH``configTIMER_TASK_STACK_DEPTH | 如果你要使用軟件定時器,需要將這些配置使能并設置相關參數。 |
其他常用配置:你還可以根據需求使能或禁用互斥量(configUSE_MUTEXES
)、遞歸互斥量(configUSE_RECURSIVE_MUTEXES
)、事件組(configUSE_EVENT_GROUPS
)、棧溢出檢查(configCHECK_FOR_STACK_OVERFLOW
)等功能。
4. 處理中斷服務程序(ISR)
FreeRTOS需要接管SVC、PendSV和SysTick這三個中斷246。
-
打開你的工程中
stm32fxxx_it.c
文件(例如stm32f1xx_it.c
或stm32f4xx_it.c
)。 -
找到并注釋掉或刪除以下三個函數的具體實現:
-
SVC_Handler(void)
-
PendSV_Handler(void)
-
SysTick_Handler(void)
-
-
原因:這些中斷的服務程序已經在你之前添加的移植層文件(如
port.c
)中實現了。如果不注釋掉,會導致函數重復定義。
注意SysTick的特殊情況:如果你的HAL庫仍然使用SysTick作為時基源(HAL_InitTick()
),你可能需要修改SysTick_Handler
而不是簡單地刪除它。一種常見的做法是28:
c
#include "FreeRTOS.h" #include "task.h"void SysTick_Handler(void) {HAL_IncTick(); // 維持HAL庫的時基if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {xPortSysTickHandler(); // 調用FreeRTOS的SysTick Handler} }
確保在FreeRTOSConfig.h
中啟用了INCLUDE_xTaskGetSchedulerState
宏。
5. 修改HAL庫的時基源(強烈推薦)
STM32的HAL庫默認使用SysTick作為其時基源(用于HAL_Delay()
,?HAL_GetTick()
等)。而FreeRTOS也使用SysTick作為其任務調度的時鐘節拍。雖然通過一些技巧可以讓兩者共享SysTick,但更推薦的做法是將HAL庫的時基源切換到另一個硬件定時器(如TIM1, TIM6等),以避免潛在沖突9。
-
你可以在STM32CubeMX中重新配置:在
SYS
選項下,將Timebase Source
從SysTick
改為其他的硬件定時器(如TIM1
)。 -
或者直接修改代碼:在
main.c
的HAL_Init()
調用之后,重新初始化一個定時器作為HAL庫的時基源。
🧪 編寫測試代碼
完成以上步驟后,就可以編寫簡單的FreeRTOS任務來測試移植是否成功了。
-
包含頭文件:在
main.c
中包含FreeRTOS頭文件。#include "FreeRTOS.h" #include "task.h" #include "queue.h" // 如果需要使用隊列等功能
-
創建任務函數:定義至少一個簡單的任務函數。
void vTaskLED(void *pvParameters) {for (;;) {HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 假設LED連接在PC13vTaskDelay(500); // 延遲500個時鐘節拍,即500ms (假設configTICK_RATE_HZ=1000)} }
-
創建任務并啟動調度器:在
main()
函數的初始化代碼之后(while (1)
之前)創建任務并啟動FreeRTOS調度器。int main(void) {HAL_Init();SystemClock_Config();// ... 其他外設初始化代碼// 創建任務xTaskCreate(vTaskLED, "LED_Task", 128, NULL, 2, NULL);// 啟動FreeRTOS調度器,永遠不會返回vTaskStartScheduler();for (;;) {} // 調度器啟動后,不會執行到這里 }
🔬 編譯、下載與調試
-
編譯工程:解決所有編譯錯誤。常見的錯誤包括頭文件路徑不正確、函數未定義(可能是移植層文件沒添加或路徑錯誤)、重復定義(中斷服務函數沒注釋掉)等。
-
下載到開發板并運行。
-
觀察現象:如果一切正常,LED應該會以你設置的周期閃爍。
-
使用調試器:如果程序運行不正常,使用調試器進行單步調試,檢查系統是否能成功創建任務、是否成功啟動調度器、是否進入正確的硬件中斷等。
?? 常見問題排查
-
編譯錯誤?
undefined reference to ...
: 檢查FreeRTOS的.c
文件是否都已添加到工程組中,頭文件路徑是否設置正確。 -
編譯錯誤?
redefinition of ...
: 檢查stm32fxxx_it.c
中的SVC、PendSV、SysTick中斷處理函數是否已注釋掉。 -
程序在啟動調度器后卡死或進入HardFault:
-
檢查
FreeRTOSConfig.h
中的configCPU_CLOCK_HZ
是否設置正確。 -
檢查
FreeRTOSConfig.h
中的中斷優先級配置(configKERNEL_INTERRUPT_PRIORITY
和configMAX_SYSCALL_INTERRUPT_PRIORITY
)是否正確。這是非常常見的錯誤來源。 -
檢查堆大小
configTOTAL_HEAP_SIZE
是否足夠創建初始任務。 -
使用調試器檢查是否成功進入
SVC_Handler
(用于啟動第一個任務)。
-
-
SysTick中斷沖突: 確保HAL庫的時基源已切換至非SysTick的定時器,或者按照前述方法修改了
SysTick_Handler
函數。