FreeRTOS在STM32F103上的移植实战与内存管理优化(基于heap_4)

张开发
2026/4/14 2:01:18 15 分钟阅读

分享文章

FreeRTOS在STM32F103上的移植实战与内存管理优化(基于heap_4)
1. FreeRTOS移植前的准备工作第一次接触FreeRTOS移植时我也被网上的各种零散资料搞得晕头转向。后来在STM32F103ZE开发板上反复实践后终于摸清了门道。这里分享一个完整的移植流程从裸机工程开始一步步带你实现多任务调度。硬件准备很简单一块STM32F103ZE开发板我用的是正点原子的Mini板、USB转串口工具、以及配套的LED闪烁例程。软件方面需要Keil MDK开发环境我用的V5.23版本STM32标准外设库建议用V3.5.0FreeRTOS源码包最新版是v202212.01特别提醒下载FreeRTOS时注意选择包含所有演示项目的完整包这样能确保拿到heap_4.c等关键文件。我刚开始就犯过只下载内核源码的错结果缺少内存管理实现文件。2. 工程目录结构调整先在原有裸机工程目录下创建FreeRTOS文件夹这个文件夹将作为RTOS的核心容器。接着解压下载的FreeRTOS包找到Source文件夹把里面所有内容复制到新建的FreeRTOS目录。建议按功能重新组织文件结构FreeRTOS ├── include # 存放FreeRTOSConfig.h等头文件 ├── portable # 保留MemMang和RVDS/ARM_CM3 └── source # 所有核心.c文件重点来了portable目录需要大瘦身只保留这两个关键内容MemMang/heap_4.c内存管理实现RVDS/ARM_CM3Cortex-M3端口文件其他编译器相关的port文件统统删掉这样能避免后续编译时出现undefined reference错误。我当初没做清理结果链接阶段报了一堆莫名其妙的错误。3. Keil工程配置实战打开MDK工程按F7调出管理界面。新建两个组FreeRTOS_CORE添加source目录下所有.c文件FreeRTOS_PORTABLE添加heap_4.c和port.c头文件路径要包含FreeRTOS/includeFreeRTOS/portable/RVDS/ARM_CM3编译时会遇到第一个坑重复定义中断服务例程。解决方法是在stm32f1xx_it.c中注释掉这三个函数void SVC_Handler(void) __attribute__((weak)); void PendSV_Handler(void) __attribute__((weak)); void SysTick_Handler(void) __attribute__((weak));4. 系统时钟与延时函数改造FreeRTOS需要独占SysTick定时器所以原工程的延时函数要重写。在delay.c中添加#include FreeRTOS.h #include task.h void SysTick_Handler(void) { if(xTaskGetSchedulerState() ! taskSCHEDULER_NOT_STARTED) { xPortSysTickHandler(); } }关键配置点在FreeRTOSConfig.h#define configUSE_PREEMPTION 1 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ ((unsigned long)72000000) #define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES (5) #define configMINIMAL_STACK_SIZE ((unsigned short)128) #define configTOTAL_HEAP_SIZE ((size_t)(10*1024)) #define configUSE_HEAP4 1 // 启用heap_4内存管理特别注意configTOTAL_HEAP_SIZE要根据实际SRAM大小调整STM32F103ZE有64KB RAM我分配了10KB给堆。5. heap_4内存管理深度优化heap_4是FreeRTOS最常用的内存分配方案它通过合并相邻空闲块来减少碎片。在MemMang目录选择heap_4.c后可以通过这些参数调优// 在FreeRTOSConfig.h中添加 #define configAPPLICATION_ALLOCATED_HEAP 1 // 自定义堆位置 // 在main.c中定义堆空间 uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] __attribute__((at(0x20000000)));实测发现将堆放在RAM起始地址能提升访问效率。通过串口打印剩余堆空间可以监控内存使用void vApplicationMallocFailedHook(void) { printf(Memory overflow!\n); while(1); } void check_heap() { printf(Remain heap: %d bytes\n, xPortGetFreeHeapSize()); }6. 多任务创建与调试技巧创建两个LED闪烁任务来验证移植效果void led0_task(void *pv) { while(1) { LED0_TOGGLE(); vTaskDelay(pdMS_TO_TICKS(500)); } } void led1_task(void *pv) { while(1) { LED1_TOGGLE(); vTaskDelay(pdMS_TO_TICKS(800)); } }调试时遇到任务卡死试试这些方法在HardFault_Handler中添加打印调用栈使用uxTaskGetNumberOfTasks()查看当前任务数通过vTaskList()获取所有任务状态需要开启相关宏7. 常见问题解决方案编译提示undefined reference to vPortSVCHandler 在FreeRTOSConfig.h添加#define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler任务无法调度 检查SystemCoreClock是否正确定义STM32F103ZE是72MHz 确认configTICK_RATE_HZ设置合理通常100-1000Hz内存分配失败 增大configTOTAL_HEAP_SIZE 使用heap_4而非heap_1/2后者容易产生碎片移植完成后我建议运行FreeRTOS自带的内存检测任务void vAssertCalled(const char *file, int line) { printf(Assert failed: %s:%d\n, file, line); }这个移植过程我反复验证了三次每次遇到的坑都不太一样。最头疼的是第一次忘记修改中断优先级导致系统时不时卡死。后来发现FreeRTOS要求SysTick和PendSV必须是最低优先级在HAL库中要这样设置HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0);

更多文章