【STM32H743IIT6 系列】将外部SDRAM作为内部SRAM使用的方法及需要解决的问题
引言
STM32H743的片上随机存取存储器(RAM)容量最大约为1KB。对于简单项目而言,这一容量尚可满足需求。但在处理更为复杂的应用程序时,尤其是在随机存取存储器方面,“空间不足”的问题就会不可避免地出现。此时,W9825G6KH便能发挥作用。然而,在将其用作内部静态随机存取存储器(SRAM)时,系统很容易进入硬错误中断(hardfault_handler)。在后续内容中,我将详细阐述如何配置同步动态随机存取存储器(SDRAM)。
单片机内存组成
在真正开始之前,先在这里简单解读一下单片机编译之后产生的内存:

程序存储器(Flash)
- 代码段(Code):这部分存储的是单片机程序的可执行指令,也就是我们编写的程序代码被编译后生成的机器码。如在 C 语言中,函数体里的语句经过编译就会变成代码段中的指令。比如一个简单的加法函数。
- 只读数据段(RO - data):存储的是常量数据,这些数据在程序运行过程中不会被修改。常见的如、字符串常量、使用 const 关键字修饰的变量等。
数据存储器(RAM)
- 可读写数据段(RW - data):存储已经初始化的全局变量和静态变量。这些变量在程序运行过程中可以被修改。
- 零初始化数据段(ZI - data):存储未初始化或初始化为 0 的全局变量和静态变量。在程序启动时,系统会自动将这部分内存初始化为 0。
- 栈(Stack):栈是一种后进先出(LIFO)的数据结构,主要用于存储函数调用时的局部变量、函数参数、返回地址等信息。当调用一个函数时,会在栈上为该函数的局部变量分配内存空间;函数返回时,这些内存空间会被释放。
- 堆(Heap):堆用于动态内存分配。在程序运行过程中,如果需要动态地分配和释放内存,可以使用 malloc、calloc、realloc 等函数从堆中分配内存,使用 free 函数释放内存。
配置MPU
在高性能板子中,MPU的配置是必不可少的,否则一不注意就会进入硬错误中断,同时配置MPU也是为了板子的内存安全。以下就是MPU在CubeMX中的配置:



配置SDRAM
直接看这个博主写的优秀的文章,跟着他配置一下就搞定了:
【CubeMX-HAL库】STM32H743—FMC配置SDRAM_stm32h743 sdram-CSDN博客
外部SDRAM作为内部SRAM
方法一(不可行)
首先在Keil5魔法棒中如此设置:

接着编译...运行...,你就会发现程序卡住了,正好(恰好?)卡在了 hardfault_handler() 当中,然后你想着调试,结果发现一进调试就进了 hardfault_handler() 中,是不是很神奇,还没进到main函数就寄了。
原因
这里就直接说了,当时可是找了我两天,焦头烂额的,要感谢硬汉嵌入式论坛的一篇博文:
STM32H7 SDRAM启动的坑 - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz!
就是在 SystemInit () 函数中(找不到的 Ctrl+F 跳转):

不知道出于什么原因,在进入 _main 前就把FMC关闭了,这时你再调用SDRAM的内存的时候,就会发生硬错误进入中断。为什么要说是 _main 之前呢?详细请看下图的 startup 启动文件(汇编):


可以见到是先进入SystemInit 才进入的 _main。
方法二(可行,重要)
由于上面我们已经知道了问题出在哪里,针对其进行解决就行!
加上宏定义

增加 SystemInit_ExtMemCtl 函数
新版的库函数中没有这个(官方例程里都有,用来在 SystemInit 中初始化SDRAM的),也不知道是不是ST公司又犯病了,所以这里我们自己加上,加到fmc.c文件里吧:
注意!注意!注意!这个函数中的FMC的SDRAM的初始化都是直接从下面的 MX_FMC_Init 和 HAL_FMC_MspInit初始化函数中复制的,目的就是为了让 FMC 的 SDRAM 在进入_main之前初始化好!!!
/****************************************************************************************************** * 函 数 名: SystemInit_ExtMemCtl * 入口参数: 无 * 返 回 值: 无 * 函数功能: 初始化外部 SDRAM 控制器 * 说 明: 此函数用于初始化 FMC 外设,配置 GPIO 引脚,并对 SDRAM 进行初始化和参数配置。 * 仅在定义了 DATA_IN_ExtSDRAM 时执行相关操作。 * 作用: 在进入main函数之前就对FMC进行初始化(很重要!!!!!) *******************************************************************************************************/ void SystemInit_ExtMemCtl(void) { // 仅在定义了 DATA_IN_ExtSDRAM 时执行 SDRAM 初始化操作 #if defined (DATA_IN_ExtSDRAM)// 定义 SDRAM 时序结构体并初始化为 0 FMC_SDRAM_TimingTypeDef SdramTiming = {0}; // 用于临时存储 MRD(模式寄存器定义)的变量 __IO uint32_t tmpmrd = 0; // 用于标记 FMC 是否已经初始化的标志变量 uint32_t FMC_Initialized = 0; // 检查 FMC 是否已经初始化,如果是则直接返回 if (FMC_Initialized) { return; } // 标记 FMC 已经初始化 FMC_Initialized = 1; // 定义外设时钟初始化结构体 RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; //------------------------ 初始化外设时钟 ------------------------ // 选择要初始化的外设时钟为 FMC PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_FMC; // 选择 FMC 的时钟源为 D1HCLK PeriphClkInitStruct.FmcClockSelection = RCC_FMCCLKSOURCE_D1HCLK; // 配置外设时钟,如果配置失败则调用错误处理函数 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { Error_Handler(); } //------------------------ 初始化 GPIO 引脚 ------------------------ // 定义 GPIO 初始化结构体 GPIO_InitTypeDef GPIO_InitStruct = {0}; // 配置 GPIOF 引脚 GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3 |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12 |GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF12_FMC; HAL_GPIO_Init(GPIOF, &GPIO_InitStruct); // 配置 GPIOC 引脚 GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); // 配置 GPIOG 引脚 GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4 |GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_15; HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); // 配置 GPIOE 引脚 GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10 |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14 |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); // 配置 GPIOD 引脚 GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_13 |GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1 |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_7; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); //------------------------ 使能 FMC 时钟 ------------------------ // 使能 FMC 接口时钟 __HAL_RCC_FMC_CLK_ENABLE(); //------------------------ 初始化 SDRAM 句柄 ------------------------ // 定义 SRAM 句柄(这里未使用,可考虑移除) SRAM_HandleTypeDef hsram1; // 定义 SDRAM 句柄 SDRAM_HandleTypeDef hsdram1; // 配置 SDRAM 句柄的实例 hsdram1.Instance = FMC_SDRAM_DEVICE; // 配置 SDRAM 句柄的初始化参数 hsdram1.Init.SDBank = FMC_SDRAM_BANK1; hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9; hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13; hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16; hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3; hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2; hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE; hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; //------------------------ 配置 SDRAM 时序 ------------------------ // 配置 SDRAM 时序参数 SdramTiming.LoadToActiveDelay = 2; SdramTiming.ExitSelfRefreshDelay = 8; SdramTiming.SelfRefreshTime = 6; SdramTiming.RowCycleDelay = 6; SdramTiming.WriteRecoveryTime = 4; SdramTiming.RPDelay = 2; SdramTiming.RCDDelay = 2; // 初始化 SDRAM,如果初始化失败则调用错误处理函数 if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK) { Error_Handler(); } //------------------------ 执行 SDRAM 初始化序列 ------------------------ // 调用 SDRAM 初始化序列函数,配置 SDRAM SDRAM_Initialization_Sequence(&hsdram1, &command); #endif
}
改变启动文件startup
如下修改:


使用SDRAM(必须要那么用)
请看硬汉的博客:
1、【原创】像使用内部SRAM一样定义使用STM32H7的外部SDRAM,含MDK和IAR两版 - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz!
原FMC_Init函数初始化
除了在启动文件提前初始化之外,还要在原本的fmc初始化函数中再进行初始化(即加上配置SDRAM一句),否则会读写不正常——3.12更新。


测试
【【STM32H7教程】第26章 STM32H7的TCM,SRAM等五块内存的超方便使用方式 - CSDN App】https://blog.csdn.net/Simon223/article/details/95200519?sharetype=blog&shareId=95200519&sharerefer=APP&sharesource=2301_79288228&sharefrom=link
根据这样方法来使用并且测试就行,不要用方法一来测试,因为方法一会之间在_main前就会分配好内存,就会直接进入硬错误中断!接下来就可以给一个超大的数组来体验32MB的超大内存了,假如还使用FFT的话,也就可以体验一下几十万甚至上百万个点的FFT了[doge]
博客导航
本文来自博客园,作者:膝盖中箭卫兵,转载请注明原文链接:https://www.cnblogs.com/Skyrim-sssuuu/p/18774932

浙公网安备 33010602011771号
https://orcid.org/0000-0001-5102-772X