Linux搭建FreeRTOS开发环境
Qemu[1]是一个通用且开源的虚拟机平台,可以虚拟不同类型的硬件以供软件上的开发调试,比如ARM架构的Cortex-M3系列或者RISC-V架构。本文以Cortex-M3内核为例,基于Ubuntu 20.04(WSL2)搭建FreeRTOS开发环境,演示最基础的Qemu用法。STM32F103系列的CPU内核就是Cortex-M3
1.准备
1.1 安装qemu
1 | |
1.2 安装arm编译调试工具链
1 | |
Ubuntu软件默认安装在/usr/bin路径下,即arm-none-eabi-gdb的位置是/usr/bin/arm-none-eabi-gdb,用于后续启动调试。也可以通过命令which arm-none-eabi-gdb查询其安装位置。
1.3 其余安装
1 | |
1.4 创建工作区
1 | |
1.5 下载FreeRTOS
1 | |
1.6 下载FreeRTOS Kernel
1 | |
1.7 移动Kernel到Source目录
该步非常重要,如果Source目录下没有FreeRTOS Kernel的源码,则FreeRTOS无法编译运行
1 | |
2.VSCode编译运行
2.1 进入工作区
1 | |
2.2 C/C++配置
Ctrl + Shift + P选择C/C++ Edit Configurations(JSON)生成C/C++配置,否则运行QEMU报错且VSCode无法正常跳转识别头文件。
1 | |
2.3 调试配置
确保launch.json文件中的miDebuggerPath为arm-none-eabi-gdb的安装路径
1 | |
2.4 启动调试
F5启动调试(自动编译),断点自动暂停在main入口处,再按F5运行程序,切换TERMINAL选项卡查看打印情况

如果arm-none-eabi-gcc提示error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file,则为libncurses.so.5创建软连接
查找/usr目录下所有libncursesw.so文件
1 | |
创建软连接
1 | |
3.程序分析
该FreeRTOS的Demo创建了两个任务,一个定时器以及一个先进先出队列
- 发送任务
prvQueueSendTask,每200ms往队列xQueue发送一个消息 - 定时器
xTimer设置为自动重载,每2000ms进入一次定时器回调,在回调中往队列xQueue发送一个消息 - 接收任务
prvQueueReceiveTask,循环等待队列xQueue的消息,收到消息则判断消息的类型并进行打印

4.增加日志打印
复制debug_log.h和debug_log.c到Demo根目录,文件来源于终端彩色打印
- 在
main_blinky.c文件中导入头文件#include "debug_log.h" - 将
prvQueueReceiveTask函数中的printf更换成DBG_LOGI和DBG_LOGW,运行效果如下

5.内存泄露分析
在prvQueueReceiveTask函数里的if( ulReceivedValue == mainVALUE_SENT_FROM_TASK )条件,该条件接收来自发送队列发送的消息,将该条件下的内容修改为以下的代码,分析内存泄露的情况
1 | |

从上面打印可用的堆空间来看,程序没有发生内存泄露。如果只申请不释放,也就是注释vPortFree(ch);这句代码,重新编译运行应该能够看到可用堆空间不断减少,也就是发生了内存泄露

在打印处添加记录点Add Logpoint, 输入以下表达式,同样可以判断出内存泄露。
1 | |

增大申请的空间,能够更快地观察到内存泄露到最后的结果
1 | |

6.定时器分析
在prvQueueReceiveTask函数里的if( ulReceivedValue == mainVALUE_SENT_FROM_TIMER )条件,该条件接收来自定时器中断发出的消息,将该条件下的内容改为以下的代码,粗略分析定时器的重载时间
1 | |

根据Watch窗口的值以及终端打印的值,任务接收到定时器消息的间隔是1999ms或2000ms
7.cJSON内存管理
cJSON是一个非常流行的轻量级的JSON解析器,只需要一个头文件和源文件就可以使用。但是其节点的创建以及回收默认使用的是C语言中的malloc和free,随着程序逻辑复杂度增加,一旦节点没有被正确回收就会发生内存泄露的情况,且难以定位调试。通过FreeRTOS的堆内存管理的方式,可以有效判断在使用cJSON时是否发生了内存泄露。
7.1 导入cJSON文件
根据该cJSON官方Github地址[4]下载cJSON.h和cSJON.c文件
- 复制到
main_blinky.c同级目录下 - 在
Makefile文件中,增加编译选项-lm且将cSJON.c加入编译 - 禁用
malloc警告函数,否则编译不通过 - 增加任务深度,否则调用cJSON函数极容易出现
Stack overflow in Rx的栈溢出错误




7.2 注册FreeRTOS内存管理函数
在实际使用cJSON功能函数之前,将FreeRTOS的内存管理函数注册给cJSON的内存管理函数,则通过检测xPortGetFreeHeapSize()函数返回值就能够快速判断出cJSON是否存在内存泄露。
1 | |
7.3 测试情况
7.3.1 根节点
创建一个root节点,分别测试是否执行了cJSON_Delete函数时的内存情况

7.3.2 测试cJSON_Print函数
创建一个JSON,包含一个字符串的键值对,分别测试在调用cJSON_PrintUnformatted之后是否释放该字符串指针的内存情况

7.3.3 使用原生内存管理函数
在不执行cJSON_Delete函数下,分别测试在启用以及禁用FreeRTOS的内存管理函数的内存情况

