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的内存管理函数的内存情况