记录FreeRtos消息调试问题

记录一次GD32 FreeRtos消息调试问题

问题记录

消息内存池参数创建时参数如下:

消息内存池参数被莫名其妙改变了,导致在内存分配的时候,由于数组越界进hardfault,导致程序崩溃。

异常分配,但是不会进hardfault

当跟踪到消息内存池内存分配函数中时,发现在分配内存时,当前索引变成了0x00000034,已经超过了内存池的大小0x10,且这个时候内存池大小也变成了1,item_sz也变成了0x00000007。说明内存池控制块的数据也被更改。

正常分配

然后我们对比正常分配时pool_sz和item_sz的大小。其中结构体struct_os_pool_cb为内存池控制句柄ChgRecordSaveMsg_Poll的数据类型。

查看内存:创建时

为了进一步确认我们的猜测,直接查看对应内存地址的数据。在内存池创建时,返回的内存池控制句柄地址为0x20003930。在内存查看窗口产看其数据如下:

然后,让程序继续运行。在调用分配内存接口时,在分配内存前打上断点,该地址的数据已经被更新了,如下图所示:

然后,我们再根据我们的思路,往前查找,确定在哪个时候内存被更新的。

问题怀疑

1、怀疑堆栈内存分配不足,增加堆栈内存

在启动文件startup_gd32f407_427.s中找到如下代码,将Stack_Size 字段和Heap_Size后面的数值都增大一倍。同时,将FreeRtos的内存分配也增大。再次测试。

bash 复制代码
Stack_Size      EQU     0x00000800

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00002000

结果:无效

2、怀疑是消息队列创建时,空间设置不足,没有设置队列池的大小。

下面分别是消息队列创建,消息内存池创建调用的函数和参数。尝试对消息队列参数进行更改,把消息大小uint32_t更改为消息结构体大小。

队列

消息内存池

更改后:

更改后:消息内存池便不会被其他数据覆盖。这就说明在消息队列创建时(使用的时STM32CUBDE生成的代码,消息大小默认uint32_t,后面实际使用时未重新更新)

新的问题:

消息通道只能创建9个,多创建一个就会导致任务调度时,无法正常切换,卡在osDelay内部的while循环中,导致程序任务不能正常的调度。

尝试更改初始化位置

原来的初始化:集中在main函数中,在调用系统调用之前,把消息相关的参数初始化完成,存储空间分配完成。

现在初始化更改:分散到各个任务中去,能够启动运行,但是一旦启动后,就卡死消息了。导致原因:暂时不详。

怀疑:

1、创建消息队列数量限制

更改为16试试:

结果:无效

注释第一个任务中的部分代码:数据没有错乱了

初步怀疑是存储结构体?

再次尝试:也能够正常,是按键扫描影响的?

跟这两段都有关:

初步怀疑是结构体比较大,在while循环中定义临时变量导致,如果是在子函数中,退出会销毁。

定义在while循环前面还是一样有数据覆盖。怀疑是因为任务分配的内存过低。

尝试增大任务的内存分配:

问题得到解决!!!

最终确认原因是由于:任务堆栈分配不足导致,但是,问题表现的原因实在是有点费劲,堆栈不足,不继续分配内存得了,为什么要一开始能跑,跑着跑着再挂掉。

然后总结上诉产生的心得问题:

怀疑是内存不足,导致osDelay延迟的ticks数据修改了,所以卡在while中时间比较久,经过排查,确实是数据比较大,但是还不确定是什么导致的数据突变。

但是,修改内存后,还是会出现卡顿现象。

消息队列暂时放到单独文件中创建:暂时不影响程序运行。

关于任务内存调试

可以使用下面两个函数

c 复制代码
		FreeHeapSizeE = xPortGetMinimumEverFreeHeapSize();
		HighWaterE = (int32_t)uxTaskGetStackHighWaterMark(NULL);
复制代码
例如:两个函数的返回值打印出来,比如分别为2200和2000。2200代表目前还有2200可以用,2000:代表程序的堆分配了最多后还剩下2000bytes可以用,我们就可以适当的减小这个数值,但是又不能全部减掉,适当的留一些,以备不时之需。而我们一开始分配时,也要先从大往小的方向适当调整堆的大小。
c 复制代码
xPortGetFreeHeapSize()
可以获取调用时堆中空闲内存的大小,以字节为单位。使用它可以优化堆的大小。需要注意,当使用heap_3时是不能调用这个函数的。
c 复制代码
xPortGetMinimumEverFreeHeapSize()

此函数返回FreeRTOS应用程序开始运行之后,曾经存在的最小的未被分配的存储空间的字节数。
需要注意xPortGetMinimumEverFreeHeapSize()只在使用heap_4或者heap_5时生效。
c 复制代码
uxTaskGetStackHighWaterMark()
printf(" 最小的栈空间大小: %d \r\n",
(int32_t)uxTaskGetStackHighWaterMark(NULL));
可以得出该任务自启动起来最小剩余栈空间大小。然后我们就可以计算出最大使用的大小,一般可以再乘以1.5-2倍左右作为最终分配的值。注意:返回的以字为单位,真实的bytes需要乘以4。

后记

经过长时间的验证,程序运行正常。新增任务和消息队列都无影响,所以综合考虑,可能在任务创建中和消息队列以及消息池的创建中,由于任务堆栈分配不足导致了程序运行异常,中间各种尝试导致的新问题可能都是与内存有关。

所以,排查此类问题应该先检查配置参数,尽量放大参数设置排除内存导致的各种干扰,然后再慢慢尝试减小内存分配(内存充足可忽略)。

# 一些小注意

1、对应芯片平台的堆和栈的空间分配要尽可能的大一些,有些时候整个Rtos进入假死状态(任务不响应,单片机也没有复位)很有可能就是堆空间不够了。

2、建议设计一个任务监视器的Task,Task主要任务有两个,一个是芯片板载级别看门狗喂狗,另一个任务就是监视其他任务执行状态,确保每个任务在最大的容忍时间内能够执行,没有执行就提示故障。

3、对于一些通信和显示的任务可以使用FreeRtos的时间组进行阻塞,中断发出事件通知通信Task执行,通信Task执行完数据处理后,给出通知到显示Task,同时用队列将数据传输过来。(这里后续可以直接用队列来直接通知)

4、对于通信,目前是串口的考虑开DMA来加快处理速度,目前没有DMA的话100ms定时传输时好的。但是更快的传输一段时间后就会出席那队列发送失败的情况。

5、一些后台的执行任务可以周期执行,将一些接受的事件组的延迟设置为0,没有事件就跳过,有事件就继续执行。

相关推荐
小琦QI1 小时前
STM32F407VET6+CCE4503学习笔记---IOLINK server
笔记·stm32·学习
无限进步_1 小时前
基于单向链表的C语言通讯录实现分析
c语言·开发语言·数据结构·c++·算法·链表·visual studio
Darken031 小时前
基于单片机STM32中的OLED显示屏
stm32·单片机·oled·显示屏
无限进步_1 小时前
C语言双向循环链表实现详解:哨兵位与循环结构
c语言·开发语言·数据结构·c++·后端·算法·链表
杨福瑞1 小时前
数据结构:栈
c语言·开发语言·数据结构
Bona Sun1 小时前
单片机手搓掌上游戏机(十九)—pico运行doom之硬件连接
c语言·c++·单片机·游戏机
缘三水2 小时前
【C语言】5.printf和scanf(新手向详细版)
c语言·开发语言·基础语法
努力小周2 小时前
基于STM32物联网智能老年人防摔系统
stm32·单片机·嵌入式硬件·物联网·c#·课程设计
Bona Sun2 小时前
单片机手搓掌上游戏机(二十二)—pico运行doom之固件和rom上传
c语言·c++·单片机·游戏机