博主学艺不精,内容有误请批评指正!

2. 堆栈溢出导致程序崩溃

2.1 遇见问题

在进行消息队列的实验中,由于之前测试按键扫描任务和跑马灯任务没有删除,直接在之前的prj上添加了向队列发送消息任务和从队列接收消息任务,在程序写完后,编译链接下载没有报错,但是在我使用新创建的队列发送和接收消息任务的时候程序只执行一次就卡死,找了很久没找到原因。

在进行将新添加的任务删除的尝试后,发现程序又可以正常进行多任务切换。只要加上新的任务就跑不通。为了避免程序崩溃是由于消息队列引起的嫌疑,我将消息队列任务移植到前面的3个任务中,并把多余的任务删除。实验现象正常。

于是怀疑是堆栈溢出的问题。

2.2 问题解析

在STM32CubeMX中移植FREERTOS,默认配置堆栈空间是:

  • 栈空间为 128Words
  • 堆空间为 3072Bytes

freeRTOS 一个消息队列多个任务_单片机

freeRTOS 一个消息队列多个任务_freeRTOS 一个消息队列多个任务_02

而在CubeMX中创建的任务栈空间大小默认最低为128Words

freeRTOS 一个消息队列多个任务_freertos_03

当任务超过4个时,堆空间就会溢出

freeRTOS 一个消息队列多个任务_嵌入式_04

在创建第5个任务后,其使用的堆空间已经到达3120Bytes,超过了之前分配的3072Bytes。因此堆溢出。

freeRTOS 一个消息队列多个任务_freertos_05

#一些存储器知识

了解到一些堆栈与存储器关系的知识。

freeRTOS 一个消息队列多个任务_rtos_06

可以看到堆栈是存放在RAM中。

查看STM32芯片手册的RAM的大小,以实验使用的STM32F103RCT6为例:

freeRTOS 一个消息队列多个任务_嵌入式_07

可以看到,该单片机型号的SRAM最大为64Kbytes,Flash最大为512Kbytes。

附一些链接

关于单片机型号与RAM容量的关系

STM32中的程序在RAM还是FLASH里运行?

FreeRTOS中的堆栈设置”与“系统启动文件中堆栈”的关系

回到STM32CubeMX,继续看FREERTOS的参数配置选项,选择内存管理设置Memory management settings,然后点击TOTAL_HEAP_SIZE,可以看到最大的堆大小可以设置到48Kbytes,也就是49152 Bytes (0XC000 Bytes)。

freeRTOS 一个消息队列多个任务_单片机_08

2.3 问题解决

将堆大小修改为6144Bytes(若不够则再改大,一般不超过芯片SRAM容量的一半)

freeRTOS 一个消息队列多个任务_单片机_09

修改后不再报错

freeRTOS 一个消息队列多个任务_freeRTOS 一个消息队列多个任务_10

经过检验,将TOTAL_HEAP_SIZE设置为6144后系统最多可以运行9个任务。

在添加一个任务的时候使用的堆空间开销会增大624Bytes。与预期的128*4=512Bytes有所出入,原因尚未了解。

2.4 FreeRTOS中的堆栈设置”与“启动文件中堆栈”的关系

参考FreeRTOS中的堆栈设置”与“系统启动文件中堆栈”的关系。

FreeRTOS中的堆栈,并没有占用系统启动文件中的堆栈空间大小。**两者在空间分配上互不影响,需要单独设置。**并且FreeRTOS有自己的内存申请函数pvPortMalloc()以及一系列的内存管理函数。

增加RTOS的堆栈或启动文件的堆栈都会占用RAM。

一个小测试

FreeRTOS中的堆栈设置界面:

freeRTOS 一个消息队列多个任务_rtos_11

启动文件分配堆栈界面:

freeRTOS 一个消息队列多个任务_单片机_12

计算系统占用RAM大小:0x800 = 2048 Bytes, 2048 + 6144 = 8192 Bytes。

生成keil工程,编译后计算实际RAM开销:

freeRTOS 一个消息队列多个任务_单片机_13

freeRTOS 一个消息队列多个任务_rtos_14

因此,在一个SRAM容量为64KBytes的芯片里,FreeRTOS配置的堆栈大小和启动文件分配的堆栈大小加起来不能超过SRAM容量,否则会出现错误!并且要预留一定的空间防止有额外开销导致程序崩溃。