在STM32F103上进行FreeRTOS移植和配置(源码移植)
个人博客:blogs.wurp.top
1. 移植准备
硬件要求
- STM32F103开发板(如Blue Pill)
- ST-Link/V2调试器
- USB转串口模块(用于调试输出)
软件资源
- FreeRTOS源码(v10.4.6或更高版本)
- STM32标准外设库(STM32F10x_StdPeriph_Lib_V3.5.0)
- 开发环境:Keil MDK-ARM或GCC工具链
创建以下工程目录结构:
/Project├── /CMSIS # 核心支持文件├── /FreeRTOS # FreeRTOS源码│ ├── /Source # 内核源码│ ├── /Portable # 移植层文件│ └── /MemMang # 内存管理实现├── /Libraries # ST标准外设库├── /User # 用户代码└── /Drivers # 板级驱动
2. 添加FreeRTOS源码
从FreeRTOS官网下载源码后,复制以下内容:
FreeRTOS/Source/*.c
到/FreeRTOS/Source
FreeRTOS/Source/include
到/FreeRTOS/include
FreeRTOS/Source/portable/[Compiler]/ARM_CM3
到/FreeRTOS/Portable/GCC
(或对应编译器目录)FreeRTOS/Source/portable/MemMang/heap_4.c
到/FreeRTOS/MemMang
3. 创建FreeRTOSConfig.h
-
在
/User
目录下创建FreeRTOSConfig.h
配置文件:#ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H#include "stm32f10x.h"// 基本配置 #define configUSE_PREEMPTION 1 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ (SystemCoreClock) // 72MHz #define configTICK_RATE_HZ ((TickType_t)1000) // 1ms tick #define configMAX_PRIORITIES (5) #define configMINIMAL_STACK_SIZE ((uint16_t)128) // 字为单位 #define configTOTAL_HEAP_SIZE ((size_t)(10 * 1024)) // 10KB堆 #define configMAX_TASK_NAME_LEN (16)// 功能配置 #define configUSE_16_BIT_TICKS 0 #define configUSE_MUTEXES 1 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configUSE_QUEUES 1 #define configUSE_TIME_SLICING 1// 内存分配方案 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configSUPPORT_STATIC_ALLOCATION 0// 钩子函数 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0// 中断优先级配置 (STM32使用4位优先级) #define configKERNEL_INTERRUPT_PRIORITY 255 // 等价于15 (0xF) #define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 // 等价于11 (0xB)// 移植层重定义 #define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler// 断言配置 #define configASSERT(x) if((x) == 0) { taskDISABLE_INTERRUPTS(); for(;;); }#endif /* FREERTOS_CONFIG_H */
4. 修改启动文件
-
在STM32启动文件(如
startup_stm32f10x_md.s
)中,修改以下中断向量:; 修改前 ; DCD SVC_Handler ; SVCall Handler ; DCD PendSV_Handler ; PendSV Handler ; DCD SysTick_Handler ; SysTick Handler ; 修改后 DCD vPortSVCHandler ; FreeRTOS SVC Handler DCD xPortPendSVHandler ; FreeRTOS PendSV Handler DCD xPortSysTickHandler ; FreeRTOS SysTick Handler
5. 初始化FreeRTOS
-
在
main.c
中添加FreeRTOS初始化和任务创建:#include "stm32f10x.h" #include "FreeRTOS.h" #include "task.h"// LED任务函数 void vLEDTask(void *pvParameters) {const TickType_t xDelay = pdMS_TO_TICKS(500);for(;;) {GPIO_WriteBit(GPIOC, GPIO_Pin_13, (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13)));vTaskDelay(xDelay);} }// 调试任务函数 void vDebugTask(void *pvParameters) {// 初始化串口...for(;;) {// 发送调试信息vTaskDelay(pdMS_TO_TICKS(1000));} }int main(void) {// 系统时钟初始化(72MHz)SystemInit();// 外设初始化(GPIO、USART等)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure);// 创建任务xTaskCreate(vLEDTask, "LED", configMINIMAL_STACK_SIZE, NULL, 2, NULL);xTaskCreate(vDebugTask, "Debug", configMINIMAL_STACK_SIZE+128, NULL, 1, NULL);// 启动调度器vTaskStartScheduler();// 不应执行到这里while(1); }// 空闲任务钩子函数(可选) void vApplicationIdleHook(void) {// 低功耗处理__WFI(); // 进入睡眠模式 }
6. 内存管理配置
在工程中包含heap_4.c
内存管理实现:
- 这是最推荐的内存管理方案,支持内存碎片整理
- 在
FreeRTOSConfig.h
中通过configTOTAL_HEAP_SIZE
设置堆大小
7. 中断优先级配置要点
- 在STM32F103中正确处理中断优先级:
// 设置中断分组(在main初始化中调用) NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);// 外设中断优先级设置示例(USART1) NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 10; // 必须高于configMAX_SYSCALL_INTERRUPT_PRIORITY NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
8. 关键移植文件说明
-
port.c (位于
/FreeRTOS/Portable/GCC/ARM_CM3
)- 实现任务上下文切换
- 提供SysTick和PendSV中断处理
- 定义临界区进入/退出宏
-
portmacro.h
- 定义处理器特定数据类型
- 声明硬件相关宏和函数
-
heap_4.c
- 内存分配实现
- 提供
pvPortMalloc()
和vPortFree()
函数
9. 调试与优化技巧
栈溢出检测
-
在
FreeRTOSConfig.h
中启用栈溢出检测:#define configCHECK_FOR_STACK_OVERFLOW 2
-
实现栈溢出钩子函数:
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {// 处理栈溢出错误while(1); }
堆使用监控
- 在任务中监控堆使用情况:
void vMonitorTask(void *pvParameters) {for(;;) {size_t freeHeap = xPortGetFreeHeapSize();// 通过串口输出堆使用情况vTaskDelay(pdMS_TO_TICKS(5000));} }
任务状态监控
-
启用统计功能:
#define configUSE_TRACE_FACILITY 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 1
-
获取任务状态:
void vTaskStats(char *pcWriteBuffer) {TaskStatus_t *pxTaskStatusArray;volatile UBaseType_t uxArraySize, x;uxArraySize = uxTaskGetNumberOfTasks();pxTaskStatusArray = pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));if(pxTaskStatusArray != NULL) {uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, NULL);for(x = 0; x < uxArraySize; x++) {// 格式化输出任务信息}vPortFree(pxTaskStatusArray);} }
10. 常见问题解决
-
任务无法调度
- 检查
vTaskStartScheduler()
是否被调用 - 验证SysTick中断是否正常工作
- 确保PendSV和SVC中断优先级设置为最低
- 检查
-
HardFault异常
- 检查栈大小是否足够(使用栈高水位线检测)
- 确认内存访问权限(特别是MPU配置)
- 检查中断处理函数是否调用了FreeRTOS API
-
堆分配失败
- 增加
configTOTAL_HEAP_SIZE
- 使用
xPortGetFreeHeapSize()
监控内存使用 - 考虑静态分配任务内存
- 增加
-
时间相关函数不准确
- 确认
configCPU_CLOCK_HZ
和configTICK_RATE_HZ
设置正确 - 检查系统时钟配置是否为72MHz
- 验证SysTick重载值计算
- 确认
11. 高级配置选项
低功耗模式
void vApplicationIdleHook(void) {// 进入STOP模式PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);// 唤醒后重新配置时钟SystemInit();
}
使用静态内存分配
// 定义任务控制块和栈
static StaticTask_t xTaskTCB;
static StackType_t xTaskStack[configMINIMAL_STACK_SIZE];// 创建任务
xTaskCreateStatic(vTaskFunction, "Task", configMINIMAL_STACK_SIZE, NULL, 1, xTaskStack, &xTaskTCB);
软件定时器
在FreeRTOSConfig.h
中启用:
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
#define configTIMER_QUEUE_LENGTH 5
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)
创建定时器:
TimerHandle_t xTimer = xTimerCreate("Timer", pdMS_TO_TICKS(1000), pdTRUE, NULL, vTimerCallback);
xTimerStart(xTimer, 0);
12. 总结
通过以上步骤,你已经完成了:
- 手动移植FreeRTOS到STM32F103
- 正确配置内核参数和中断优先级
- 创建并管理多个任务
- 实现基本调试和监控功能
- 应用高级功能如低功耗模式和静态分配
源码移植方式虽然步骤较多,但提供了更深层次的理解和更大的灵活性。关键点在于:
- 正确配置
FreeRTOSConfig.h
参数 - 确保中断优先级设置合理
- 仔细管理内存资源
- 使用调试工具监控系统状态
这种移植方式特别适合对实时性要求高、需要深度优化的应用场景。