【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]
【STM32H743IIT6】将外部SDRAM作为内部SRAM使用的方法及需要解决的问题的更多相关文章
- 外部SRAM实验,让STM32的外部SRAM操作跟内部SRAM一样(转)
源:外部SRAM实验,让STM32的外部SRAM操作跟内部SRAM一样 前几天看到开源电子论坛(openedv.com)有人在问这个问题,我特意去做了这个实验,这样用外部SRAM就跟用内部SRAM一样 ...
- FMC—扩展外部 SDRAM
本章参考资料:< STM32F4xx 参考手册 2>.< STM32F4xx 规格书>.库帮助文档< stm32f4xx_dsp_stdperiph_lib_um.chm ...
- 第26章 FMC—扩展外部SDRAM
本章参考资料:<STM32F76xxx参考手册2>.<STM32F7xx规格书>.库帮助文档<STM32F779xx_User_Manual.chm>. 关于SDR ...
- 第26章 FMC—扩展外部SDRAM—零死角玩转STM32-F429系列
第26章 FMC—扩展外部SDRAM 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/ ...
- Swift Internal Parameter and External Parameter 外部参数和内部参数
今天跟大神又学习了些关于IOS开发Swift语言的外部参数和内部参数 func doSomething(num1: Int, num2: Int) -> Int { return num1 + ...
- (转)Android如何编程设置APP安装位置(外部存储或内部存储)?
Beginning with API Level 8, you can allow your application to be installed on the external storage ( ...
- 4.hive的外部表和内部表
1.外部表和内部表区别 创建表时:创建内部表时,会将数据移动到数据仓库指向的路径:若创建外部表,仅记录数据所在的路径, 不对数据的位置做任何改变. 删除表时:在删除表的时候,内部表的元数据和数据会被一 ...
- Hive 文件格式 & Hive操作(外部表、内部表、区、桶、视图、索引、join用法、内置操作符与函数、复合类型、用户自定义函数UDF、查询优化和权限控制)
本博文的主要内容如下: Hive文件存储格式 Hive 操作之表操作:创建外.内部表 Hive操作之表操作:表查询 Hive操作之表操作:数据加载 Hive操作之表操作:插入单表.插入多表 Hive语 ...
- hive 外部表和内部表的区别和相互转换
Hive内部表和外部表区别 1.创建内部表时,内部表的数据文件是保存在指定的路径的:如若创建外部表,则只记录数据所在的路径,不会对数据位置做改变. 2.删除表的时候,内部表元数据和数据会跟着一起删除. ...
- linux服务器NAT后无法在内网通过外部IP访问内部服务的问题
场景一: 将外网访问192.168.100.10的80端口转发到192.168.75.5:8000端口. # iptables -t nat -A PREROUTING -d 192.168.100. ...
随机推荐
- 前端开发系列078-Node篇之npm
本文输出Node中和包管理有关的基本内容,即npm的使用. 一.简单介绍 npm全称Node packAge Manager是Node官方提供的包管理工具,下面列出包管理工具的功能边界. > ❏ ...
- Gym - 101147 & 队内自训#2 题解
A - The game of Osho 题意:每次给你G堆石子,每堆有\(n{_i}\)个,一次可挑走\(B{_i}^k\)个.最后不能选的人输.问最后先手赢还是后手赢. 思路:从SG表的方式入手. ...
- C++ 元函数的学习一
简介 参考书籍 C++模板元编程实战 -- 李伟 感觉这本书很超值,因为 zsvh 知乎大佬都给写题序了 简单code header #include <iostream> // 顺序结构 ...
- Ansys 求解直角 应力分布
简介 学习ansys笔记 网格的划分对于精度的准确性有很大影响,10mm的网格的误差比较大 image
- OceanBase数据库结合ETLCloud快速实现数据集成
一.背景 随着信息技术的迅猛发展和数据量的急剧增加,企业面临着前所未有的数据管理挑战.传统的数据库系统在处理大规模.多样化的数据时往往显得力不从心.因此,分布式数据库应运而生,以其优越的性能和扩展性逐 ...
- flink 1.11.2 学习笔记(2)-Source/Transform/Sink
一.flink处理的主要过程 从上一节wordcount的示例可以看到,flink的处理过程分为下面3个步骤: 1.1 .添加数据源addSource,这里的数据源可以是文件,网络数据流,MQ,Mys ...
- Sgt 模板代码
struct Sgt{ int lazyTag; int val; }t[maxn]; void pushUp(int x, int l, int r){ t[x].val = t[x].lazyTa ...
- GROOVY 面向对象
面向对象: class Student{ private int StudentID; private String StudentName; void setStudentID(int pID){ ...
- 常用第三方工具的Docker命令
# clickhouse docker run --restart always \ -d \ --name clickhouse-server \ --ulimit nofile=262144:26 ...
- CF1423G Growing flowers题解
考虑每种颜色的贡献,用总数 \(n-k+1\) 减去没有贡献到的(极长连续段长度为 \(len\) 时),贡献为 \(\max(len-k+1,0)\),所以考虑用 \(\text{ODT}\) 维护 ...