一个使用外部 SRAM 导致死机的案例
问题:
该问题由某客户提出,发生在 STM32F407IGT6 器件上。据其工程师讲述:为了满足软件对大容量内存的需求,将软件中的部分变量从内部 SRAM 转移到片外的 SRAM当中。而这一改变,导致该软件不能运行,每次复位后,随即发生死机。在此之前,对 FSMC 的初化代码,以及片外SRAM 的读写均做过测试,并确认是没有问题的。其内存分配如下表(一)所示。
调研:
使用 Keil MDK 创建工程,测试其所用的FSMC 初始化代码,结果表明该段代码正确无误。修改内存分配,删除其中对外部 SRAM 的分配,如下表(三)所示。重新对其软件编译运行。结果表明,在这种内存分配方式下,其软件可以正常运行。修改其软件代码,在初始化FSMC 之后加入对外部 SRAM 的读写测试,重新编译运行。测试结果表明,此时对外部SRAM 的读写也是正确的。查找其软件对 FSMC 初始化函数调用的位置,发现该函数是在该软件的main()函数中调用的。修改代码,将该函数的调用位置移至 SystemInit()中,并且恢复原来的内存分配,如上表(一)。重新编译并运行,此时该软件正确运行。
结论:
软件中对 FSMC 做初始化的位置不对,导致程序在访问外部 SRAM 时 FSMC 还未被初始化,从而造成总线访问出错,从而产生 HardFault 中断,最终程序停留在 HardFault中断服务程序中,使得程序对外表现出“死机”的现象。
处理:
一般来说, main()函数是 C 语言的入口,C 语言代码从这里开始执行。然而,具体结合到STM32的应用工程,这并不是工程运行的起点。往往在main()函数执行之前,还有一段启动初始化代码,为硬件做最基本的时钟和中断矢量配置等;为 C 语言代码的执行创建一个运行环境。这里主要涉及两个函数,即SystemInit()和__main()。其中__main()是编译系统提供的一个函数,负责完成C库函数和应用程序执行环境的初始化,之后跳转到用户main()。在__main()做 C 环境初始化的时候,会访问相关的存贮器。如果此时,相应的存贮器不可用,就会出现错误。在STM32启动的文件里,SystemInit()函数先于__main()的执行。所以,如果在SystemInit()函数里先对 FSMC 的做好初始化就可以避免后面__main()运行时访问相关内存出现异常的问题。
修改代码,将对 FMSC 初始化函数的调用放在SystemInit()函数中,以保证在 C 环境初始化之前完成对FMSC 的初始化。
=================================
往期话题链接:
4、一个基于STM32 HID例程生成不多于64字节传输的示范