背景内容
stm32可以外扩很大的sram,常见外部sram的初始化函数一般是c语言写的,默认写在main函数里面。stm32初始化首先进入汇编代码startup_stm32f407xx.s,在汇编代码中Reset_Handler(复位中断服务程序)里面先调用了SystemInit,然后调用__main进行堆栈的初始化,最后才会跳转到用户main函数。调用SystemInit涉及到局部变量和函数调用,需要用到stack栈空间,如果我们简单的将系统堆栈配置到外部sram,执行SystemInit时外部sram还没有初始化,运行到这里程序就会进入hardfault卡死。
为了解决这个问题,首先可以将外部sram的初始化函数提前,放到SystemInit函数中调用,cube生成的stm32代码已经为我们提供了这样的接口,这样__main就可以正确运行,然而这还没有解决SystemInit本身执行的问题,我们需要保证SystemInit执行时使用STM32内部RAM。
具体步骤:
1、修改启动代码堆栈指针
在startup_stm32fxxxxx.s中,找到 __initial_sp这一行,改为如下内容
Stack_Size EQU 0x2000
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp EQU 0x20002000
注意这里0x20002000 =内部主内存地址0x20000000+栈大小0x2000,也就是说这个地址和stack_size有关,需要根据自己stack_size的设置来修改,这样就强制指定了系统初始化的栈顶指针在内部内存,保证进入main函数之前的其它初始化函数调用堆栈正常使用。
2、添加宏定义使能DATA_IN_ExtSRAM
在option,c/c++选项卡 define后面添加 DATA_IN_ExtSRAM
3、修改外部内存初始化代码
假定外部内存初始化函数为FsmcSramInit(),修改system_stm32f4xx.c文件
找到如下内容,英文说明这个函数将在跳转到main之前执行,可以让外部内存被系统当做程序数据内存使用,包括堆栈,这就是关键了
#elif defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
/**
* @brief Setup the external memory controller.
* Called in startup_stm32f4xx.s before jump to main.
* This function configures the external memories (SRAM/SDRAM)
* This SRAM/SDRAM will be used as program data memory (including heap and stack).
* @param None
* @retval None
*/
void SystemInit_ExtMemCtl(void)
接下来修改SystemInit_ExtMemCtl(void)函数,直接注释掉里面的所有内容,改为
void SystemInit_ExtMemCtl(void)
{
FsmcSramInit();
}
SystemInit_ExtMemCtl()函数是被SystemInit函数调用的,这样执行完SystemInit后,再进入__main就可以初始化配置在外部内存的堆栈了。
4、配置和使用外部内存空间
这里可以有两种方式,如果让mdk自动分配内存,则打开option,target选项卡,右下角 read/write memory areas 添加内存块,勾选RAM1,填入外部内存起始地址和大小即可
否则,如果要手动分配内存,可以参考我另一篇文章stm32管理内存分配以及CCM的使用方法
修改自定义的sct文件,添加如下字段
RW_RAM1 0x68000000 0x00100000 {
startup_stm32f407xx.o (Heap)
.ANY (EXRAM)
}
即可将系统堆,以及通过attribute标记为EXRAM的数据都放在外部内存。