前言
因为最近要配置 RGB LCD(LTDC) 屏幕要用到 SDRAM,虽然之前也写过一篇这个文章,不过当时太忙,写的也比较潦草,某些细节可能没有写清楚,现在会在这篇文章一一道来,后面也会有一篇 LTDC 配置的文章。
STM32CubeMX 配置
MPU(内存保护单元配置)
跟下面的图片一个个配置就好啦。
FMC 配置
FMC 基本配置
这里也是跟着下面的图片配置,不过要特别注意的是 SDRAM 的引脚有很多,要对应好板子上的引脚不要搞错!否则可能会发生某些问题,如:放在 SDRAM 里面的变量初始化不了、进入硬件故障中断等等。
FMC 时钟配置
我使用的 SDRAM 型号为 "W9825G6KH-6I",其最高时钟频率为166MHz,所以我们这里通过锁相环给到 FMC 的时钟为240MHz 即可,分频后就是120MHz。
程序部分
分散式内存管理
想要将 SDRAM 作为内部 RAM 使用,就要将变量主动分配到这一块内存中,但是应该如何将变量精准分配呢?那么可以看一下这一篇文章,使用分散式管理内存的方式,可以更加方便的管理内存空间:
【STM32H743IIT6 系列】理清 xxRAM、xxROM、xxFlash 的核心作用,附 H7 系列五种内存详解,以及超便捷的内存区域管理方法
startup_stm32h743xx.s 启动文件修改
首先我们要知道,全局变量的定义和初始化是先于 main() 函数的,但是在 main() 函数之前,FMC 所连接的的 SDRAM又没有初始化,所以当你直接用分散式管理内存的方法使用 SDRAM 的时候,必会在没有进入 main() 函数之前就会跳到硬故障中断函数。
由于一定要使用分散式管理内存的方法,那么我们就想到可以在 startup_stm32h743xx.s 启动文件上面动手脚。(其实不将 SDRAM 作为内部 RAM 使用的话可以不用那么操作,直接配置好就可以
点击查看代码- Reset_Handler PROC
- EXPORT Reset_Handler [WEAK]
- IMPORT ExitRun0Mode
- IMPORT SystemInit
- IMPORT __main
- IMPORT SystemInit_ExtMemCtl
- LDR R0, =ExitRun0Mode
- BLX R0
- LDR R0, =SystemInit
- BLX R0
- LDR R0, =SystemInit_ExtMemCtl
- BLX R0
- LDR R0, =__main
- BX R0
- ENDP
复制代码
在你的启动文件中这个位置加上红色框框中的代码,即可,虽然现在还没有定义,但是后面就会说到了。(假如你的程序没有:ExitRun0Mode,可能因为你的软件是旧版本的)。
提前初始化 FMC SDRAM
SDRAM 写寄存器函数
SDRAM 通过内部寄存器来配置,所以我们要写入其寄存器控制其功能,这是必不可少的。
在 fmc.c 文件加上第一个程序,如下所示:
点击查看代码- FMC_SDRAM_CommandTypeDef command;// 控制指令
- /******************************************************************************************************
- * 函 数 名: SDRAM_Initialization_Sequence
- * 入口参数: hsdram - SDRAM_HandleTypeDef定义的变量,即表示定义的sdram
- * Command - 控制指令
- * 返 回 值: 无
- * 函数功能: SDRAM 参数配置
- * 说 明: 配置SDRAM相关时序和控制方式
- *******************************************************************************************************/
- void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command)
- {
- __IO uint32_t tmpmrd = 0;
- register __IO uint32_t index;
- /* Configure a clock configuration enable command 时钟配置使能*/
- Command->CommandMode = FMC_SDRAM_CMD_CLK_ENABLE; // 开启SDRAM时钟
- Command->CommandTarget = FMC_COMMAND_TARGET_BANK; // 选择要控制的区域
- Command->AutoRefreshNumber = 1;
- Command->ModeRegisterDefinition = 0;
-
- HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT); // 发送控制指令
- /* Delay */
- for (index = 0; index<10000; index++);
-
- /* Configure a PALL (precharge all) command 对所有存储区域预充电*/
- Command->CommandMode = FMC_SDRAM_CMD_PALL; // 预充电命令
- Command->CommandTarget = FMC_COMMAND_TARGET_BANK; // 选择要控制的区域
- Command->AutoRefreshNumber = 1;
- Command->ModeRegisterDefinition = 0;
-
- HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT); // 发送控制指令
-
- /* Configure a Auto-Refresh command 设置自动刷新次数*/
- Command->CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE; // 使用自动刷新
- Command->CommandTarget = FMC_COMMAND_TARGET_BANK; // 选择要控制的区域
- Command->AutoRefreshNumber = 8; // 自动刷新次数
- Command->ModeRegisterDefinition = 0;
-
- HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT); // 发送控制指令
-
- /* Program the external memory mode register */
- //配置模式寄存器,SDRAM的bit0-bit2为指定突发访问的长度
- //bit3为指定突发访问的类型,bit4-bit6为CAS值,bit7和bit8为运行模式
- //bit9为指定的写突发模式,bit10和bit11位保留位
- tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2 |//设置突发长度:2(可以是1/2/4/8)
- SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |//设置突发类型:连续(可以是连续/交错)
- SDRAM_MODEREG_CAS_LATENCY_3 |//设置CAS值:3(可以是2/3)
- SDRAM_MODEREG_OPERATING_MODE_STANDARD |//设置操作模式:0,标准模式
- SDRAM_MODEREG_WRITEBURST_MODE_SINGLE; //设置突发写模式:1,单点访问
-
- Command->CommandMode = FMC_SDRAM_CMD_LOAD_MODE; // 加载模式寄存器命令
- Command->CommandTarget = FMC_COMMAND_TARGET_BANK; // 选择要控制的区域
- Command->AutoRefreshNumber = 1;
- Command->ModeRegisterDefinition = tmpmrd;
-
- HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT); // 发送控制指令
-
- //刷新频率计数器(以SDCLK频率计数),计算方法:
- //COUNT=SDRAM刷新周期/行数-20=SDRAM刷新周期(us)*SDCLK频率(Mhz)/行数
- //我们使用的SDRAM刷新周期为64ms,SDCLK=240/2=120Mhz,行数为8192(2^13).
- //所以,COUNT=64*1000*120/8192-20=918(20为刷新等待冗余)
- HAL_SDRAM_ProgramRefreshRate(hsdram, 918); // 配置刷新率
- }
复制代码 然后再对应在 fmc.h 加上对应的宏定义:(注意这里已经提前包含了后面还没加上的程序的全局声明)
点击查看代码- #define SDRAM_Size 32*1024*1024 //32M字节
- #define SDRAM_BANK_ADDR ((uint32_t)0xC0000000) // FMC SDRAM 数据基地址
- #define FMC_COMMAND_TARGET_BANK FMC_SDRAM_CMD_TARGET_BANK1 // SDRAM 的bank选择
- #define SDRAM_TIMEOUT ((uint32_t)0x1000) // 超时判断时间
-
- #define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
- #define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
- #define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
- #define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)
- #define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
- #define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
- #define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
- #define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
- #define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
- #define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
- #define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
- extern FMC_SDRAM_CommandTypeDef command;// 控制指令
- extern void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command);
- extern void SystemInit_ExtMemCtl(void);
- extern void fsmc_sdram_test(void);
复制代码 集成初始化函数
这里的初始化函数 SystemInit_ExtMemCtl() 就是添加在启动文件中、_main函数前的函数,就是用于提前初始化FMC,可以先看一下程序,后面会进行解读:
点击查看代码
最后添加
在 MX_FMC_Init() 函数最后面再添加配置函数,防止在提前初始化后配置丢失。
内存测试
测试程序
添加如下代码,然后放到 main() 函数中测试:
点击查看代码[code]//SDRAM内存测试 void fsmc_sdram_test(void){ __IO uint32_t i=0; __IO uint32_t temp=0; __IO uint32_t sval=0;//在地址0读到的数据 //每隔16K字节,写入一个数据,总共写入2048个数据,刚好是32M字节 for(i=0;i |