单片机学习小组
直播中

niu!kf

12年用户 601经验值
私信 关注

怎样去解决STM32堆栈空间不足的问题呢

怎样去设置STM32启动文件堆栈空间的大小呢?
怎样去解决STM32堆栈空间不足的问题呢?

回帖(1)

臧超楠

2022-2-21 15:00:22
1. 设置堆栈空间大小
在使用STM32编程时,一般情况下我们不会关注堆栈空间的大小,因为在STM32的启动文件中,已经帮我们预先设置好了堆栈空间的大小。一般默认的启动代码中,Stack栈的大小为:0x400(1024Byte),Heap堆的大小为:0x200(512Byte)。


这也是为什么一个基础的工程编译后,RAM的空间也占用了1.6K左右的原因,因为堆栈的空间均分配在RAM中,可在编译的map文件中查看RAM资源占用的情况。
若工程中使用的局部变量较多,定义的数据长度较大时,若不调整栈的空间大小,则会导致程序出现栈溢出,程序运行结果与预期的不符或程序跑飞。这时我们就需要手动的调整栈的大小。
当工程中使用了malloc动态分配内存空间时,这时分配的空间就为堆的空间。所以若默认的堆空间大小不满足工程需求时,就需要手动调整堆空间的大小。
1. 直接在启动文件中修改堆栈空间的大小,如图1所示的位置;

2. 打开启动文件,点击下方Configuration Wizard,可在Option的设置框中设置堆栈空间的大小。

2. 相关理论补充
bss段:
    bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。
    bss是英文Block Started by Symbol的简称。
    bss段属于静态内存分配。
data段:
    数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。
    数据段属于静态内存分配。
text段:
    代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。
    这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。
    在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

堆(heap):
    堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。
    当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);
    当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
查阅网上的资料,理解堆和栈的区别:
栈区(stack):由编译器自动分配和释放,存放函数的参数值、局部变量的值等,其操作方式类似于数据结构中的栈。
堆区(heap):一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。分配方式类似于数据结构中的链表。
全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统自动释放。
文字常量区:常量字符串就是存放在这里的。
程序代码区:存放函数体的二进制代码。
注意:堆和栈,一般堆是由低地址往上(高地址)增长,栈是由高地址向下(低地址)增长。都是连续的,C语言不提供内存保护机制类似的功能,如果一直堆一直增长,栈一直申请,然后就会导致栈溢出,程序崩溃。

STM32堆栈空间不足问题
       先说结论,以STM32F103RCT6为例,初始的栈空间是1KB,堆空间是512Byte。如果动态内存分配需求过多时,需要手动调节堆空间。在启动文件startup_stm32f103xe.s的开头就可以设置堆栈空间大小。同样,在STM32CubeMX中也可对堆栈大小进行修改,在Project -> Settings选项中可以对Minimum Heap Size大小进行更改。扩大之后即可解决堆栈空间不足的问题。


       在STM32F103RCT6上,使用 malloc() 为链表分配内存空间时,忽然遇到一次分配内存过多而死机的问题。查阅官方文档发现此型号的单片机FLASH 256KB,RAM 48KB。我链表的结构体定义如下:
typedef struct LNode{
    uint8_t       data;
    struct LNode *next;
}LNode,*LinkList;

        uint8_t类型在单片机中定义为unsigned char即1个字节,32位系统一个指针变量为4字节。由于“内存对齐”机制的存在,所以实际上一个节点分配的内存为8字节。并且通过输出语句printf("%d",sizeof(*Head));打印到串口助手显示的也是8,证明的分析的正确性。
       经过测试发现,我最多能创建32个节点,因此我只使用了32*8=256 byte 的内存空间。与官方文档的48KB的内存空间相差太多。

       查阅网上的博客发现,堆栈大小可以在stm32的启动文件startup_stm32f103xe.s里面设置,开头就有:

Stack_Size      EQU     0x400
Heap_Size       EQU     0x200

0x00000400 等于1024字节所以等于1K


0x00000200 等于512字节所以等于512 Byte


       由于malloc()分配的动态内存在堆区域,因此调大堆空间Heap_Size为0xC00,即3072字节大小。重新测试,发现可以接收到191个节点,这次使用了191*8=1528 byte大小的内存空间。由此判断,用户可以自由使用的堆空间,大约为堆总空间的一半。超过时系统就会死机。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分