stm32卡死在void HardFault_Handler(void)函数内

        在开发STM32项目时,添加了多个较大的局部变量后,程序无法正常运行。使用Keil的调试模式,发现程序卡死在void HardFault_Handler(void)函数内。经过研究,发现是栈或堆溢出导致的问题。

一种简单的解决方法是,在STM32的启动文件(例如:startup_stm32f407xx.s)中,增大以下两个值:
        Stack_Size     EQU    0x2000  ; 栈大小
        Heap_Size      EQU    0x2000  ; 堆大小
通过增大这两个值,可以有效解决栈或堆溢出的问题。

注:Stack_Size (EQU 0x400):
Stack_Size 表示栈的大小。在 STM32 微控制器中,栈用于存储局部变量、函数调用返回地址、保存的寄存器值等。栈大小为 0x400(即 1024 字节),这意味着分配给栈的内存空间为 1024 字节(1 KB)。
Heap_Size (EQU 0x200):
Heap_Size 表示堆的大小。堆用于动态内存分配,比如通过 malloc() 或 free() 进行的内存操作。这里 Heap_Size 被设置为 0x200(即 512 字节),这意味着堆的内存空间为 512 字节。

        然而,增大栈和堆的大小时,必须考虑芯片的内存容量,避免超出芯片的内存限制。

        因此,在调整栈和堆的大小时,应根据芯片的内存容量进行合理配置,以确保程序的稳定运行。

        那怎么进行分配呢?
        假设你的 STM32 微控制器的总 RAM 大小为 X,你需要分配栈和堆时可以遵循以下原则:

        确定内存总量: 首先,查找你使用的 STM32 微控制器的 RAM 大小。假设 RAM 大小为 X 字节。

        分配栈空间: 通常栈的大小会根据程序的复杂性和功能需求来设定。如果你的程序需要较多的嵌套函数调用或者局部变量,可以适当增大栈的大小。常见的栈大小配置为 1 KB 到 8 KB 之间。

        假设你决定将栈大小设置为 4 KB(0x1000 字节)。

        分配堆空间: 堆空间通常分配为较小的值,特别是在嵌入式开发中,堆的大小一般在 512 字节到 2 KB 之间,取决于是否使用动态内存分配。如果你不打算使用过多的动态内存分配,可以设置较小的堆空间,例如 1 KB(0x400 字节)。

        保证栈和堆不重叠: 在 STM32 中,栈通常从内存的顶部开始向下增长,而堆则从内存的底部向上增长。为了避免栈和堆重叠,需要确保栈和堆的分配范围没有交错。通过合理设置栈和堆的大小,确保它们各自有足够的空间。

        示例配置:
        假设你的 STM32 的 RAM 总大小为 20 KB(0x5000 字节),你可以将栈和堆分别配置如下:

        栈空间大小:4 KB(0x1000 字节)
        堆空间大小:2 KB(0x800 字节)
        那么剩下的空间就是供全局变量、静态变量和程序代码使用的部分。

        以上内容仅为整体分析,具体情况仍需结合实际问题进行具体分析。

        需要注意的是,有些问题无法单纯通过扩大栈或堆空间来解决。由于内存资源有限,解决这类问题的关键在于对代码进行有效的内存空间优化。以下是常见的内存空间优化方法:

1. 减少全局变量的使用

  • 问题:全局变量通常存储在静态存储区,过多的全局变量会占用大量内存。
  • 优化方法:尽量减少全局变量的使用,改为局部变量或者将数据封装在结构体或其他数据类型中。避免不必要的全局变量,尤其是那些可能在多个函数中都被使用的变量。

2. 使用 const 关键字优化数据存储

  • 问题:普通的全局变量占用的数据区(Data Segment)是可读写的,这样会浪费内存。
  • 优化方法:使用 const 关键字将常量数据存储在只读数据区(如 FLASH),这样可以减少 RAM 的使用。例如,将字符串常量或配置数据定义为 const。
const char my_string[] = "Hello, STM32!";

        这样定义的常量会被存储在 FLASH 区域,而不是 RAM。

3. 使用合适的数据类型

  • 问题:使用不合适的数据类型会浪费内存。例如,使用 int(通常是 4 字节)存储 0 或 1 的值,或者用 float(通常是 4 字节)存储整数。
  • 优化方法:根据数据的范围选择合适的数据类型,尽量使用 uint8_t(1 字节)、uint16_t(2 字节)等小尺寸数据类型,避免使用过大的数据类型(如 int 或 float),特别是当数据范围很小时。

4. 内存池(Memory Pool)管理

  • 问题:动态内存分配(如 malloc() 和 free())可能会导致内存碎片化,特别是在嵌入式系统中,堆空间有限。
  • 优化方法:使用内存池技术,手动管理内存块。内存池可以避免碎片化,提高内存使用效率。
  • 使用静态分配的内存池来分配和回收内存,而不是动态地使用 malloc(),可以避免内存碎片化和多次内存分配开销。

5. 减少库函数和中间件的使用

  • 问题:STM32 中提供了很多功能强大的中间件和库函数,但这些通常包含很多功能,导致代码和内存占用增大。
  • 优化方法:选择性地使用库中需要的功能,不要引入不必要的中间件。如果只是需要某些特定功能,可以只引入对应的源代码,而不是整个库。

6. 启用优化编译选项

  • 问题:编译器没有启用优化时,生成的代码可能包含未使用的变量或冗余指令,浪费了内存空间。
  • 优化方法:在编译时启用优化选项。例如,使用 -Os 选项来优化代码大小,减少无效的代码和数据段。

        在 GCC 编译器中,可以使用如下编译选项:

        -Os  # 优化代码大小

7. 使用紧凑的算法和数据结构

  • 问题:不高效的算法和数据结构可能导致过多的内存消耗。
  • 优化方法:选择合适的算法和数据结构,避免使用那些空间复杂度较高的算法。对于数据存储,可以使用压缩算法或哈希表等高效数据结构。

        例如,如果有多个布尔值需要存储,可以使用 bitfields 或 bitset 来节省内存,而不是使用多个 bool 类型变量。

8. 避免动态内存分配(尽量避免 malloc() 和 free())

  • 问题:动态内存分配在嵌入式系统中非常危险,尤其是在内存有限的情况下,动态分配可能导致内存碎片化和不稳定。
  • 优化方法:尽量避免在嵌入式系统中使用 malloc() 和 free(),如果确实需要,可以使用内存池进行管理。

9. 优化浮点运算

  • 问题:浮点运算通常需要较大的内存和计算资源。
  • 优化方法:在没有强大硬件浮点支持的情况下,尽量避免浮点运算,可以使用定点运算替代浮点数。这样可以减少内存占用,并提高运行效率。

        下面是一些前置知识补充。

        为什么4kb为0x1000 字节?

        在计算机中,1 KB 通常被定义为 1024 字节(而不是 1000 字节),这是由于计算机使用的是二进制系统,所以基于 2 的幂次方。

        1 KB = 1024 字节
        如果需要计算 4 KB 的字节数,按照上述关系,4 KB 就是 4 倍的 1024 字节:

        4 KB = 4 × 1024 字节 = 4096 字节
        4096 字节可以用十六进制来表示。4096 在十进制下是 4096,而在十六进制中,4096 的值是 0x1000。这是因为:

        4096(十进制)= 0x1000(十六进制)

        stm32中内存是怎么分配的?

        在 STM32 或其他嵌入式系统中,内存的分配通常分为几个区域:

        静态存储区(Data Segment):

        全局变量、静态变量以及常量都存储在这个区域。它通常分为已初始化数据区(Data 区)和未初始化数据区(BSS 区)。这些变量在程序启动时分配,并且在程序的整个生命周期内存在。
        堆(Heap):

        堆用于动态内存分配,如使用 malloc()、calloc() 等函数进行内存分配的区域。堆的大小通常由开发者在链接器脚本中配置(如 Heap_Size)。
        栈(Stack):

        栈用于存储局部变量、函数调用的返回地址和保存的寄存器值。栈的内存是由函数调用栈帧管理的。栈的大小也可以通过配置来设置(如 Stack_Size)。
        总结
        全局变量 存储在 静态存储区 中,而非堆或栈。
        堆 用于动态内存分配。
        栈 用于存储局部变量和函数调用信息。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路弥行至

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值