STM32笔记之 SDRAM
Posted 夏沫の浅雨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32笔记之 SDRAM相关的知识,希望对你有一定的参考价值。
写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
目录
SDRAM芯片
简知
SDRAM (Synchronous Dynamic RandomAccess Memory),同步动态随机存储器。同步是指其时钟频率与CPU的前端总线的系统时间频率相同,并且他的内部命令的发送与数据的传输都是以这个时钟为基准的,动态是指存储阵列需要不断的刷新才能保证数据的不丢失。随机是指数据不是线性存储的,是可以自由指定地址进行数据读写。
以常见的 SDRAM芯片为例,一般单片机外围搭配用 TSOP-54pin的 SDRAM,当然也有其他封装的,像 BGA/FBGA这种,这里以 TSOP封装的为例子吧,其它的信息其实都大体相同的。如下图:TSOP-54pin中 8bit data width(左边)和 16bit data width(右边)
上图两个之间的区别是(非引脚结构层面上)左边的一次操作 8bit(一个字节)的数据,而右边的一次可操作 16bit(双字节)的数据,所以从操作上,右边处理数据比左边的要快一倍;而对于 32bit data width 的 SDRAM芯片,则比前面的处理更快了。
类型
类型 | 预取 | 数据速率示例 | Banks 数量 | VDD |
---|---|---|---|---|
SDRAM | 无 | 133 MT/s/pin | 4 Bank | 3.3 V |
DDR SDRAM | 2 预取 | 400 MT/s/pin | 4 Bank | 2.5 V |
DDR2 SDRAM | 4 预取 | 800 MT/s/pin | 4 or 8 Bank | 1.8 V (DDR2L 1.5 V) |
DDR3 SDRAM | 8 预取 | 1600 MT/s/pin | 8 Bank | 1.5 V (DDR3L 1.35 V) |
引脚定义
对于 SDRAM芯片,无论是多少数据位宽的,它的信号引脚分类都是如下几种:
引脚 | 说明 |
---|---|
A[0 : x] | 地址输入 |
DQ[0 : x] | 数据输入 / 输出 |
BA[0 : x] | Bank选择地址,选择要控制的 Bank |
CLK | 同步时钟信号,所有输入信号都在 CLK为上升沿时被采集 |
CKE | 时钟使能信号,禁止时钟信号时 SDRAM会启动自动刷新操作 |
CS# | 片选信号,低电平有效 |
RAS# | 行地址选通,为低电平时地址线输入信号表示为行地址 |
CAS# | 列地址选通,为低电平时地址线输入信号表示为列地址 |
DQM[0 : x] | 数据输入 / 输出掩码信号,表示 DQ信号的有效部分 |
note:
-
DQM[0 : x]:掩码控制位,在 SDRAM 中每个 DQM 引脚控制 8bit Data。在读操作的时候没多大影响,比如读 32bit 数据位宽的 SDRAM,但只要其中的 8bit 数据,则只须先读出 32bit 数据,再在软件里将其余的 24bit 和 0 作 “与” 操作即可,有没有 DQM 关系不大;但在执行写操作时,如果没有 DQM 就麻烦了,可能在软件上是写一个 8bit 数据,但实际上 32 根数据线是物理上连接到 SDRAM 的,只要 WR 信号一出现,这 32bit 数据就会写 SDRAM 中去,而多余的 24bit 数据将会被覆盖,如果通过使用 DQM 就可以将其对应的 24bit 屏蔽,不会因为写操作而覆盖数据了。对于 16bit 数据位宽的 SDRAM,一般描述为 LDQM 和 UDQM 或者是 DQML 和 DQMH,嘛一般看 datasheet知道对应哪个就行了。
-
BA[0 : x]:BA 信号决定了激活哪一个 Banks,一般 SDRAM多为 4-Bank。BA 信号的多少基本决定了 Banks 的多少,例如,BA[0 : 1] -> 2^2 = 4-Bank。8-Bank可以看下 https://www.micross.com/pdf/MYX4DD3256M16GB_DS_REV-1.pdf 的 datasheet,看 BA 信号有多少个引脚。
内存容量
以 IS42S16160J datasheet 中的 16Meg x16为例。
从右上角的方框中,我们可以直接获取到该芯片内存容量为 256Mb = 16MB(Mb表示 M bit,MB表示 M byte),但往往有些 datasheet并没有直观表示容量,那么可以考虑另一种获取的方法。
内存容量除了从 datasheet中直接获取,你也可以直接从 16M x 16 或者 4M x 16 x 4 Banks 来直接计算,单位为 bit,这都是可以的;在这里,更推荐从 SDRAM 的 Bank、行地址、列地址及数据地址数量来获取,因为内存容量由存储深度和存储宽度决定,这是任何存储芯片存储容量的定义:
- Bank:BA[0 : 1] -> 2^2 = 4 Banks
- Row addr:A[0 : 12] -> 2^13 = 8192 Rows
- Column addr:A[0 : 8] -> 2^9 = 512 Coulumns
当它是 16bit data width(也有 8bit data width,即 54 pin TSOP - Type II for x8封装芯片,后面实例用到),
存储深度: IS42S16160J for x16 内部有 4个 Bank,每个 Bank 有行地址 13bit、列地址 9bit;所以每个 Bank 就有 2^13 * 2^9 = 8192 * 512 = 4194304 个存储单元,4个 Bank 就有 4 * 4194304 = 16777216 个存储单元。
存储宽度: 该 SDRAM 的数据位宽为 16bit。
存储容量: 16777216 * 16bit = 268435456bit,就是 32MB。
即:4 * 8192 * 512 * 16 = 2^28 = 256 Mbit = 32MByte
对此它的计算方法可以归算为:Bank、行地址、列地址及数据地址数量彼此之间的乘积。
硬件设计
芯片引脚对应
FMC引脚 | SDRAM引脚 | 说明 |
---|---|---|
SDCLK | CLK | SDRAM 时钟 |
SDCKE[1 : 0] | CKE | SDCKE0:SDRAM 存储区域 1 时钟使能 SDCKE1:SDRAM 存储区域 2 时钟使能 |
SDNE[1 : 0] | CS# | SDNE0:SDRAM 存储区域 1 芯片使能 SDNE1:SDRAM 存储区域 2 芯片使能 |
A[12 : 0] | A[0 : x] | 行 / 列地址线 |
D[31 : 0] | DQ[0 : x] | 双向数据总线 |
BA[1 : 0] | BA[0 : x] | Bank地址线 |
NRAS | RAS# | 行地址选通 |
NCAS | CAS# | 列地址选通 |
SDNWE | - | 写入使能(当使用两个存储区域时需要) |
NBL[3 : 0] | DQM[0 : x] | 写访问的输出字节屏蔽 |
特殊脚 BA(Bank地址线)
对于某些芯片来说,可能并没有标出 BA[0 : x] 引脚,对于这种,要么手册上有说明,要么给出引脚连接图,像 NXP LPC某些系列中:
可以看到并没有标出 BA[0 : x] 引脚,但手册另外有说明:
在 S3C2440芯片中则给出连接示意图:
关于数据线交换问题
能交换的原因来源于其数据访问的特性,并且命令操作不需要涉及到数据信号线,一般是用地址线配合其他控制线来访问(某些特殊的除外),因此地址万万不能交换,而且数据线交换也是有条件的,如下说明。
通常以最小访问位宽,一般八个起始连续位(一个字节)为一组;组内可交换,但不能跨组交换;整组间可交换,但同时还要交换对应控制脚 DQMx。
例如: 16位 SDRAM 有 UDQM 和 LDQM 控制高 8位和低 8位掩码。
对于组内交换,其实也挺简单,就是不管你怎么变的,CPU放进去的是 0xF0
,读出来的也是 0xF0
就行了,你变了顺序的话,只是 0xF0
这个数在 RAM 中存的形式不同;假设你高四位和低四位换了,CPU写进去时是 0xF0 (11110000b)
,但到了 RAM 存在形式是 00001111
,而你 CPU 再读取到寄存器后还是 0xF0
,这就够了。也就是说负负得正,错错得对,明白了吧,调了线序,写进去数据会错位,但读出来也错一次,所以就没有问题了。。。只是改变了在 RAM中的存在格式。所以 DQ0 ~ DQ7 之间可以任意交换,DQ8 ~ DQ15 之间可以任意交换,但像 D8 和 D0 ~ D7 里的数据线就不能交换(即不能跨组交换)。
对于整组间交换,即 D0 ~ D7 整组与 D8 ~ D15 整组进行交换,由于最小访问位宽为 8bit,因此当以 8bit 访问读写时是没有任何数据错乱问题的,但当访问 16bit 以上多字节访问时就会出现错误,这是由于 DQMx 掩码的关系,当进行访问的时候,因为只对数据组交换,而 DQMx 掩码没交换,就会出现对端数据访问交换。
几个重要结构体
初始化结构体
/* @brief
* FMC SDRAM 初始化结构体类型定义
*/
typedef struct
uint32_t FMC_Bank; /* 选择 FMC的 SDRAM存储区域 */
uint32_t FMC_ColumnBitsNumber; /* 定义 SDRAM的列地址宽度 */
uint32_t FMC_RowBitsNumber; /* 定义 SDRAM的行地址宽度 */
uint32_t FMC_SDMemoryDataWidth; /* 定义 SDRAM的数据宽度 */
uint32_t FMC_InternalBankNumber; /* 定义 SDRAM内部的 Bank数目 */
uint32_t FMC_CASLatency; /*定义 CASLatency的时钟个数 */
uint32_t FMC_WriteProtection; /* 定义是否使能写保护模式 */
uint32_t FMC_SDClockPeriod; /* 配置同步时钟 SDCLK的参数 */
uint32_t FMC_ReadBurst; /* 是否使能突发读模式*/
uint32_t FMC_ReadPipeDelay; /* 定义在 CAS个延迟后再等待多少个 HCLK时钟才读取数据 */
FMC_SDRAMTimingInitTypeDef* FMC_SDRAMTimingStruct; /* 定义 SDRAM的时序参数
FMC_SDRAMInitTypeDef;
这部分参数,一般从硬件设计及 SDRAM芯片选型中就能确认了,不需要怎么翻看 datasheet,就算需要查看,在首页产品介绍中也能看到。
-
FMC_Bank: FMC映射到 SDRAM 有两个 bank 可以选择;根据外围 SDRAM硬件连接来决定选择。选择不同的 Bank,SDRAM 需要接到 MCU 不同的
SDNE 以及 SDCKE 引脚,并且访问的地址也不同,如下图所示:
-
FMC_ColumnBitsNumber、FMC_RowBitsNumber、FMC_SDMemoryDataWidth、FMC_InternalBankNumber: 这几项分别对应列地址、行地址、数据位宽、Bank的数量,这里可以从上面内存容量贴的那张图可以了解到,就不再过多阐述了。
-
FMC_CASLatency: 可以设置 Latency1,Latency2 和 Latency3,具体选择哪个,可从下图中选择跟自己设计相匹配的参数,并且这个参数将决定后面时序结构的参考选择:
一般来讲都是选择 Latency3,143MHz,速度等级是 -7。
-
FMC_WriteProtection: 决定是否使用写保护。
-
FMC_SDClockPeriod: 输出到 SDRAM CLK的时钟分频因子配置,同样的这个参数将决定后面时序结构的参考选择。而 FMC 的工作时钟来自 HCLK,如下图;一般来说 F429 的主频可以到 168/180M,那么 HCLK 就是 168/180M,参数可选 2 / 3分频,由于上面 FMC_CASLatency 的配置,SDRAM 支持的最大时钟频率达 143MHz,因此大多数 FMC 时钟分频比选择最小的 2分频,则在主频 180M 时,FMC 频率就是 90M。
-
FMC_ReadBurst、FMC_ReadPipeDelay: 这两个是根据实际读写情况来配置的,前者用于突发读处理,后者定义了在 CAS 延时期间预测延后多少个 SDRAM 时钟周期才读取数据。
时序结构体
/* @brief
* 控制SDRAM的时序参数,这些参数的单位都是“周期”
* 各个参数的值可设置为 1 - 16个 Clock cycles。
*/
typedef struct
uint32_t FMC_LoadToActiveDelay; /* TMRD */
uint32_t FMC_ExitSelfRefreshDelay; /* TXSR */
uint32_t FMC_SelfRefreshTime; /* TRAS */
uint32_t FMC_RowCycleDelay; /* TRC */
uint32_t FMC_WriteRecoveryTime; /* TWR */
uint32_t FMC_RPDelay; /* TRP */
uint32_t FMC_RCDDelay; /* TRCD */
FMC_SDRAMTimingInitTypeDef;
这部分的配置参数跟 datasheet 息息相关,并且在 STM32F4中,官方把这几个参数统一存放到如下的一个寄存器中:
那么这几个参数到底从何而来呢?继续使用 IS42S16160J 的 datasheet,然后逐个分析,首先得获取信息的所在位置,如下图:
第一张图,是该芯片的电气特性参数,第二张是基于第一张给出的参考配置数据(看绿框中的描述),也就是说,你可以直接用现成的第二张的数据填到属性配置中,但是,话都撂这儿了,不解释一下好像有点不好意思。。
在 FMC_SDRAMTimingInitTypeDef 时序结构体中,里面的参数都是以 cycle
为单位
- FMC_LoadToActiveDelay: 对应 tMRD(TMRD)参数;TMRD 定义模式寄存器设置命令或刷新命令所需时间。
- FMC_ExitSelfRefreshDelay: 对应 tXSR(TXSR)参数;TXSR 定义从退出自刷新命令到发出活动命令之间的时间。
- FMC_SelfRefreshTime: 对应 tRAS(TRAS)参数;TRAS 定义从活动命令到预充电命令所需时间。
- FMC_RowCycleDelay: 对应 tRC(TRC)参数;TRC 定义刷新命令之间或者活动命令之间的时间。
- FMC_WriteRecoveryTime: 对应 tDAL(TWR)参数;在 SDRAM手册上并没有 TWR 这样的标识,但是根据定义的涵义,表明:从输入数据到活动命令或者刷新命令所需的时间。因此能从 datasheet 中找到相应的 tDAL 参数;这也就说明了,有时候软件配置参数中并没有清楚写出 tMRD,tRAS 等字样,而是需要自己去了解每个配置参数的意义,才能从 SDRAM 的手册中找到对应的参数填充。
- FMC_RPDelay: 对应 tRP(TRP)参数;TRP 定义从预充电命令到活动命令所需时间,刚好跟 tRAS 相反。
- FMC_RCDDelay: 对应 tRP(TRP)参数;TRP 定义从活动命令到读 /写命令所需时间。
好了,解释完参数后,就来计算一下怎么把时间参数转换成所需的周期参数配置吧。至于你说为什么不像前面说的那样直接用第二张给出的书呢?一是,细心的可能会发现并没有给出 tXSR;二是选取不同的设计参数,将导致不一样的周期参数配置,还记得上面初始化结构体说的 FMC_CASLatency
和 FMC_SDClockPeriod
将决定时序周期数吧。
怎么算?其实很简单:
这里以 HCLK 为 168MHz
为例,FMC_SDClockPeriod = FMC_SDClock_Period_2 即对 HCLK 进行二分频,得到输出给 SDRAM CLK 的频率为 84MHz
;而 FMC_CASLatency = FMC_CAS_Latency_3,对应 datasheet 选为常见的 Latency3
,143MHz
,速度等级是 -7
(这里的 143MHz 为 SDRAM 最大支持频率,但实际我们就只用到了 84MHz)。然后以其中的一个时序配置参数 tRC
为例:
从上图可以看到,在 -7 等级中,tRC 的时间参数只有最小值是值得关注的,对于 配置的 FMC 驱动频率 84MHz,则一个 SDRAM 周期时间约为 11.90ns(即结构体参数的单位都是 11.90ns),那么在 datasheet 中限定 tRC > 60ns,要使得满足条件,只有 FMC_RowCycleDelay >= 6 时方可满足,因此我们只需取其最靠近的值即可,这样就能在满足条件的情况下也不会丢失时间性能(当然是在稳定的前提下),其余的值计算等同,就不再阐述了,自己验算。最终得到的数据,看下面的代码示例吧。
示例代码
驱动 IS42S16160J for x8(4-Bank & 8bit data width & 9bit column addr & 13bit row addr)
API 版本:
/* Private defines -----------------------------------------------------------*/
#define SDRAM_BANK_ADDR ((uint32_t)0xC0000000)
#define SDRAM_MEMORY_WIDTH FMC_SDMemory_Width_8b
/* #define SDRAM_MEMORY_WIDTH FMC_SDMemory_Width_16b */
/* #define SDRAM_MEMORY_WIDTH FMC_SDMemory_Width_32b */
/* #define SDRAM_CAS_LATENCY FMC_CAS_Latency_1 */
/* #define SDRAM_CAS_LATENCY FMC_CAS_Latency_2 */
#define SDRAM_CAS_LATENCY FMC_CAS_Latency_3
#define SDCLOCK_PERIOD FMC_SDClock_Period_2
/* #define SDCLOCK_PERIOD FMC_SDClock_Period_3 */
#define SDRAM_TIMEOUT ((uint32_t)0xFFFF)
#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)
/**
* @brief FMC SDRAM Configuration
* @param None
* @retval None
*/
static void FMC_Config(void)
GPIO_InitTypeDef GPIO_InitStructure;
FMC_SDRAMInitTypeDef FMC_SDRAMInitStructure;
FMC_SDRAMTimingInitTypeDef FMC_SDRAMTimingInitStructure;
FMC_SDRAMCommandTypeDef FMC_SDRAMCommandStructure;
uint32_t tmpr = 0;
uint32_t timeout = SDRAM_TIMEOUT;
/* GPIO configuration ------------------------------------------------------*/
/* Enable GPios clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE |
RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH |
RCC_AHB1Periph_GPIOI, ENABLE);
/* Common GPIO configuration */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
/* GPIOD configuration */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FMC);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_1 |GPIO_Pin_8 |GPIO_Pin_9 |
GPIO_Pin_10 |GPIO_Pin_14 |GPIO_Pin_15;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* GPIOE configuration */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource0 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource1 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource7 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource8 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource9 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource10 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource11 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource12 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource13 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource14 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource15 , GPIO_AF_FMC);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | 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_Init(GPIOE, &GPIO_InitStructure);
/* GPIOF configuration */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource0 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource1 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource2 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource3 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource4 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource5 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource11 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource12 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource13 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource14 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource15 , GPIO_AF_FMC);
GPIO_InitStructure.GPIO_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_Init(GPIOF, &GPIO_InitStructure);
/* GPIOG configuration */
GPIO_PinAFConfig(GPIOG, GPIO_PinSource0 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource1 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource4 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource5 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource8 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource15 , GPIO_AF_FMC);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_1 |GPIO_Pin_4 |GPIO_Pin_5 |
GPIO_Pin_8 | GPIO_Pin_15;
GPIO_Init(GPIOG, &GPIO_InitStructure);
/* GPIOH configuration */
GPIO_PinAFConfig(GPIOH, GPIO_PinSource2 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOH, GPIO_PinSource3 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOH, GPIO_PinSource5 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOH, GPIO_PinSource8 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOH, GPIO_PinSource9 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOH, GPIO_PinSource10 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOH, GPIO_PinSource11 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOH, GPIO_PinSource12 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOH, GPIO_PinSource13 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOH, GPIO_PinSource14 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOH, GPIO_PinSource15 , GPIO_AF_FMC);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_5 | 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_Init(GPIOH, &GPIO_InitStructure);
/* GPIOI configuration */
GPIO_PinAFConfig(GPIOI, GPIO_PinSource0 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOI, GPIO_PinSource1 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOI, GPIO_PinSource2 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOI, GPIO_PinSource3 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOI, GPIO_PinSource4 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOI, GPIO_PinSource5 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOI, GPIO_PinSource6 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOI, GPIO_PinSource7 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOI, GPIO_PinSource9 , GPIO_AF_FMC);
GPIO_PinAFConfig(GPIOI, GPIO_PinSource10 , GPIO_AF_FMC);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 |
GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 |
GPIO_Pin_9 | GPIO_Pin_10;
GPIO_Init(GPIOI, &GPIO_InitStructure);
/* Enable FMC clock */
RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FMC, ENABLE);
/* FMC SDRAM device initialization sequence --------------------------------*/
/* Step 1 ----------------------------------------------------*/
/* Timing configuration for 84 Mhz of SD clock frequency (168Mhz/2) */
/* TMRD: min=14ns (2x11.90ns) */
FMC_SDRAMTimingInitStructure.FMC_LoadToActiveDelay = 2;
/* TXSR: min=70ns (6x11.90ns) */
FMC_SDRAMTimingInitStructure.FMC_ExitSelfRefreshDelay = 6;
/* TRAS: min=42ns max=100Kns (4x11.90ns) */
FMC_SDRAMTimingInitStructure.FMC_SelfRefreshTime = 4;
/* TRC: min=60ns (6x11.90ns) */
FMC_SDRAMTimingInitStructure.FMC_RowCycleDelay = 6;
/* TWR: min=30ns (2x11.90ns) */
FMC_SDRAMTimingInitStructure.FMC_WriteRecoveryTime = 2;
/* TRP: min=15ns (2x11.90ns) */
FMC_SDRAMTimingInitStructure.FMC_RPDelay = 2;
/* TRCD: min=315ns (2x11.90ns) */
FMC_SDRAMTimingInitStructure.FMC_RCDDelay = 2;
/* Step 2 ----------------------------------------------------*/
/* FMC SDRAM control configuration */
FMC_SDRAMInitStructure.FMC_Bank = FMC_Bank1_SDRAM;
/* Row addressing: [8:0] */
FMC_SDRAMInitStructure.FMC_ColumnBitsNumber = FMC_ColumnBits_Number_9b;
/* Column addressing: [12:0] */
FMC_SDRAMInitStructure.FMC_RowBitsNumber = FMC_RowBits_Number_13b;
FMC_SDRAMInitStructure.FMC_SDMemoryDataWidth = SDRAM_MEMORY_WIDTH;
FMC_SDRAMInitStructure.FMC_InternalBankNumber = FMC_InternalBank_Number_4;
/* CL: Cas Latency = 3 clock cycles */
FMC_SDRAMInitStructure.FMC_CASLatency = SDRAM_CAS_LATENCY;
FMC_SDRAMInitStructure.FMC_WriteProtection = FMC_Write_Protection_Disable;
FMC_SDRAMInitStructure.FMC_SDClockPeriod = SDCLOCK_PERIOD;
FMC_SDRAMInitStructure.FMC_ReadBurst = FMC_Read_Burst_Disable;
FMC_SDRAMInitStructure.FMC_ReadPipeDelay = FMC_ReadPipe_Delay_0;
FMC_SDRAMInitStructure.FMC_SDRAMTimingStruct = &FMC_SDRAMTimingInitStructure;
/* FMC SDRAM bank initialization */
FMC_SDRAMInit(&FMC_SDRAMInitStructure);
/* Step 3 --------------------------------------------------------------------*/
/* Configure a clock configuration enable command */
FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_CLK_Enabled;
FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1;
FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;
FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;
/* Wait until the SDRAM controller is ready */
while((FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) && (timeout > 0))
timeout--;
/* Send the command */
FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
/* Step 4 --------------------------------------------------------------------*/
/* Insert 100 ms delay */
Delay以上是关于STM32笔记之 SDRAM的主要内容,如果未能解决你的问题,请参考以下文章
STM32H7教程第49章 STM32H7的FMC总线应用之SDRAM
STM32HAL库 STM32CubeMX教程十五---FMC-SDRAM
STM32HAL库 STM32CubeMX教程十五---FMC-SDRAM