STM32-FreeRTOS移植与调试
使用STM32CubeMX生成FreeRTOS中间件并创建两个任务,一个任务循环闪灯,另一个任务循环打印串口消息。进阶使用XRTOS调试,监控FreeRTOS任务堆栈大小以及分析任务的CPU使用率。
1.初始化工程
1.1 新建工程
STM32CubeMX创建工程,系统时钟、时钟树配置和调试口自行配置。PC13设置输出,启用USART1(提前用USB转串口模块接入PA9和PA10),工程名称为freertos_demo。有问题则参考之前的文章。

1.2 配置FreeRTOS
- 展开
Middleware and Software Packs选项卡 - 选择
FreeRTOS,Interface选择CMSIS_V1,因为STM32F103C8T6就是M3内核的芯片,选择CMSIS_V1会更节省空间。

1.3 创建任务
- 选择
Tasks and Queues标签,点击Add按钮 - 将
Task Name修改为LedTask - 和
Entry Function均修改为led_task - 点击
OK按钮确认 - 同理再创建一个串口任务
uart_task


1.4 编译导入
1.4.1 Keil编译
点击GENRATE CODE生成工程代码,点击Open Project,尝试用Keil编译,没有报错则可以将工程导入EIDE

如果STM32CubeMX生成工程之后,KEIL没有编译,则EIDE尝试导入MDK工程时,可能会出现编译失败的情况,比如丢失RAM/ROM布局导致的编译失败
1.4.2 EIDE编译
打开VSCode,EIDE插件选择Import Project,选择刚刚创建的工程, 注意EIDE的工作区不要保存在与MDK同一目录下,虽然不影响EIDE编译调试,但是会导致VSCode工作区无法跟踪工程根目录的文件。EIDE工作区需要保存在工程根目录下


2.编辑任务
2.1 闪灯任务
main.c文件中的MX_FREERTOS_Init();函数执行初始化任务,跳转进入该函数- 往下滑找到
led_task任务,跳转进入该任务 - 将原本任务里的
for循环中的osDelay(1);修改为以下代码1
2osDelay(500);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);

2.2 串口任务
根据STM32-串口USART的第八点重定向printf,成功配置串口重定向之后尝试在串口任务每隔500ms打印数据

3.调试FreeRTOS
在启动任何调试之前都必须确保工程的优化等级为0,否则各种情况都有可能发生,比如:创建了断点,但是程序无法暂停在断点处;或者创建断点的时候断点发生偏移。注意,如果EIDE再次导入STM32CubeMX生成的MDK工程,则需要再次检查优化等级。

F5尝试启动调试,如果可以正常启动调试则在程序运行后几秒后创建一个断点,点击底部栏的XRTOS面板,查看任务状态

XRTOS的面板必须在程序暂停后才能更新,观察XRTOS的任务状态列表,TaskName表示任务名称,黄色高亮的行代表当前断点暂停在哪个任务,任务优先级,程序地址等。但是其中有一些问题,比如Stack End、Stack Used以及RunTime等都是以问号显示。需要配置更多宏以及定时器资源才能正确监控任务状态。
3.1 启动任务监控
在FreeRTOSConfig.h文件中,增加宏configUSE_TRACE_FACILITY
1 | |
编译后启动调试,应该就能看到插件给出的提示, 单击展开提示

3.2 开启栈尾监控
在FreeRTOSConfig.h文件中,增加宏configRECORD_STACK_HIGH_ADDRESS,尽可能确保FreeRTOS的版本在V10.0.0以上
1 | |
编译后启动调试,等任务运行几秒后创建断点暂停程序,查看任务状态,应该可以看到栈尾状态,也就能查看完整的任务深度状态

3.3 统计CPU使用率
CPU使用率,这里称为Runtime,用于统计每个任务的CPU使用率,能够直观地判断各个任务对CPU的占用情况。但是获取该Runtime属性需要消耗一个高速定时器。
根据监控提示,用于统计CPU使用率的高速定时器必须10倍与FreeRTOS的系统中断时间(默认是1ms)。因此定时器的中断时间至少在100us以内,等价于10kHZ频率。STM32的定时器TIM1能够符合要求。
Also, add the following two macros to provide a high speed counter – something at least 10x faster than your RTOS scheduler tick. One strategy could be to use a HW counter and sample its current value when needed
STM32CubeMX配置STM32的TIM1频率为1Mhz,溢出计数为100-1,实现弱函数HAL_TIM_PeriodElapsedCallback并在该函数里递增计数,并将该计数值设置为全局变量以供FreeRTOS调用,就能够实现统计CPU使用率的效果。


main.c中添加变量CPU_Runtime并初始化为0
1 | |
main.c中启用定时器TIM1中断
1 | |
main.c中实现TIM1中断,累加CPU_RunTime
1 | |
FreeRTOSConfig.h中添加外部变量CPU_Runtime以及采样时间的宏, 完整FreeRTOS调试配置如下
1 | |
编译启动调试后,能够正常看到各个任务的CPU使用率

4.监控分析
4.1 任务深度
以闪灯任务LedTask为例,在未申请的临时变量时,其内存使用是96Byte,而在申请了临时变量后,内存使用是352Byte,相差刚好是256Byte。


4.2 CPU使用率
LedTask闪灯任务中增加阻塞延时500msHAL_Delay(500)UartTask串口任务注释printf语句,修改延迟时间为1000ms,并增加切换LED状态代码
此时两个任务执行的内容基本一致:都是延迟1s切换LED灯状态。唯一的区别就是闪灯任务的延迟是HAL库进行阻塞延时。进入调试状态,能够看到闪灯任务的CPU占用率是45.77%,接近50%;而串口任务占用率是0%。

删除闪灯任务中的延时,循环中仅切换LED灯,然后再加入osDelay(1);,调试查看前后任务的CPU占用率时间区别,明显能够看出加入了1ms延时的任务的CPU使用率有了明显的降低。


修改串口任务延时1ms再打印数据,对比闪灯任务能够明显看到printf函数执行是相当耗时的。
