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、行地址、列地址及数据地址数量来获取,因为内存容量由存储深度和存储宽度决定,这是任何存储芯片存储容量的定义:
当它是 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
为单位
好了,解释完参数后,就来计算一下怎么把时间参数转换成所需的周期参数配置吧。至于你说为什么不像前面说的那样直接用第二张给出的书呢?一是,细心的可能会发现并没有给出 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(10);
/* Step 5 --------------------------------------------------------------------*/
/* Configure a PALL (precharge all) command */
FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_PALL;
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 */
timeout = SDRAM_TIMEOUT;
while((FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) && (timeout > 0))
{
timeout--;
}
/* Send the command */
FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
/* Step 6 --------------------------------------------------------------------*/
/* Configure a Auto-Refresh command */
FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_AutoRefresh;
FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1;
FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 8;
FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;
/* Wait until the SDRAM controller is ready */
timeout = SDRAM_TIMEOUT;
while((FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) && (timeout > 0))
{
timeout--;
}
/* Send the command */
FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
/* Step 7 --------------------------------------------------------------------*/
/* Program the external memory mode register */
tmpr = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 |
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
SDRAM_MODEREG_CAS_LATENCY_3 |
SDRAM_MODEREG_OPERATING_MODE_STANDARD |
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
/* Configure a load Mode register command*/
FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_LoadMode;
FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank1;
FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;
FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = tmpr;
/* Wait until the SDRAM controller is ready */
timeout = SDRAM_TIMEOUT;
while((FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) && (timeout > 0))
{
timeout--;
}
/* Send the command */
FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
/* Step 8 --------------------------------------------------------------------*/
/* Set the refresh rate counter */
/* (7.81 us x Freq) - 20 */
/* Set the device refresh counter */
FMC_SetRefreshCount(636);
/* Wait until the SDRAM controller is ready */
timeout = SDRAM_TIMEOUT;
while((FMC_GetFlagStatus(FMC_Bank1_SDRAM, FMC_FLAG_Busy) != RESET) && (timeout > 0))
{
timeout--;
}
}
Register 版本:
/**
* @brief Setup the external memory controller.
* Called in startup_stm32f4xx.s before jump to main.
* This function configures the external SDRAM mounted on STM324x9I_EVAL board
* This SDRAM will be used as program data memory (including heap and stack).
* @param None
* @retval None
*/
void SystemInit_ExtMemCtl(void)
{
__IO uint32_t tmp = 0x00;
register uint32_t tmpreg = 0, timeout = 0xFFFF;
register __IO uint32_t index;
/*-- GPIOs Configuration -----------------------------------------------------*/
/*
+-------------------+--------------------+------------------+--------------+
+ SDRAM pins assignment +
+-------------------+--------------------+------------------+--------------+
| PC0 <-> FMC_SDNWE | PD0 <-> FMC_D2 | PE0 <-> FMC_NBL0 | PF0 <-> FMC_A0 | PG0 <-> FMC_A10 |
| PC2 <-> FMC_SDNE0 | PD1 <-> FMC_D3 | PE1 <-> FMC_NBL1 | PF1 <-> FMC_A1 | PG1 <-> FMC_A11 |
| PC3 <-> FMC_SDCKE0 | PD8 <-> FMC_D13 | PE7 <-> FMC_D4 | PF2 <-> FMC_A2 | PG2 <-> FMC_A12 |
| | PD9 <-> FMC_D14 | PE8 <-> FMC_D5 | PF3 <-> FMC_A3 | PG4 <-> FMC_BA0 |
| | PD10 <-> FMC_D15 | PE9 <-> FMC_D6 | PF4 <-> FMC_A4 | PG5 <-> FMC_BA1 |
| | PD14 <-> FMC_D0 | PE10 <-> FMC_D7 | PF5 <-> FMC_A5 | PG8 <-> FMC_SDCLK |
| | PD15 <-> FMC_D1 | PE11 <-> FMC_D8 | PF11 <-> FMC_SDNRAS | PG15 <-> FMC_SDNCAS |
| | | PE12 <-> FMC_D9 | PF12 <-> FMC_A6 |---------------------+
| | | PE13 <-> FMC_D10 | PF13 <-> FMC_A7 |
| | | PE14 <-> FMC_D11 | PF14 <-> FMC_A8 |
| | | PE15 <-> FMC_D12 | PF15 <-> FMC_A9 |
+---------------------+------------------+-------------------+---------------------+
*/
#if defined(STM32F446xx)
/* Enable GPIOA, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG interface
clock */
RCC->AHB1ENR |= 0x0000007D;
#else
/* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface
clock */
RCC->AHB1ENR |= 0x000001FC;
#endif /* STM32F446xx */
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);
#if defined(STM32F446xx)
/* Connect PAx pins to FMC Alternate function */
GPIOA->AFR[0] |= 0xC0000000;
GPIOA->AFR[1] |= 0x00000000;
/* Configure PDx pins in Alternate function mode */
GPIOA->MODER |= 0x00008000;
/* Configure PDx pins speed to 50 MHz */
GPIOA->OSPEEDR |= 0x00008000;
/* Configure PDx pins Output type to push-pull */
GPIOA->OTYPER |= 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOA->PUPDR |= 0x00000000;
/* Connect PCx pins to FMC Alternate function */
GPIOC->AFR[0] |= 0x00CC0000;
GPIOC->AFR[1] |= 0x00000000;
/* Configure PDx pins in Alternate function mode */
GPIOC->MODER |= 0x00000A00;
/* Configure PDx pins speed to 50 MHz */
GPIOC->OSPEEDR |= 0x00000A00;
/* Configure PDx pins Output type to push-pull */
GPIOC->OTYPER |= 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOC->PUPDR |= 0x00000000;
#else
/* Connect PCx pins to FMC Alternate function */
GPIOC->AFR[0] = 0x0000CC0C;
GPIOC->AFR[1] = 0x00000000;
/* Configure PCx pins in Alternate function mode */
GPIOC->MODER = 0x000000A2;
/* Configure PCx pins speed to 50 MHz */
GPIOC->OSPEEDR = 0x000000A2;
/* Configure PCx pins Output type to push-pull */
GPIOC->OTYPER = 0x00000000;
/* No pull-up, pull-down for PCx pins */
GPIOC->PUPDR = 0x00000000;
#endif /* STM32F446xx */
/* Connect PDx pins to FMC Alternate function */
GPIOD->AFR[0] = 0x000000CC;
GPIOD->AFR[1] = 0xCC000CCC;
/* Configure PDx pins in Alternate function mode */
GPIOD->MODER = 0xA02A000A;
/* Configure PDx pins speed to 50 MHz */
GPIOD->OSPEEDR = 0xA02A000A;
/* Configure PDx pins Output type to push-pull */
GPIOD->OTYPER = 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOD->PUPDR = 0x00000000;
/* Connect PEx pins to FMC Alternate function */
GPIOE->AFR[0] = 0xC00000CC;
GPIOE->AFR[1] = 0xCCCCCCCC;
/* Configure PEx pins in Alternate function mode */
GPIOE->MODER = 0xAAAA800A;
/* Configure PEx pins speed to 50 MHz */
GPIOE->OSPEEDR = 0xAAAA800A;
/* Configure PEx pins Output type to push-pull */
GPIOE->OTYPER = 0x00000000;
/* No pull-up, pull-down for PEx pins */
GPIOE->PUPDR = 0x00000000;
/* Connect PFx pins to FMC Alternate function */
GPIOF->AFR[0] = 0x00CCCCCC;
GPIOF->AFR[1] = 0xCCCCC000;
/* Configure PFx pins in Alternate function mode */
GPIOF->MODER = 0xAA800AAA;
/* Configure PFx pins speed to 50 MHz */
GPIOF->OSPEEDR = 0xAA800AAA;
/* Configure PFx pins Output type to push-pull */
GPIOF->OTYPER = 0x00000000;
/* No pull-up, pull-down for PFx pins */
GPIOF->PUPDR = 0x00000000;
/* Connect PGx pins to FMC Alternate function */
GPIOG->AFR[0] = 0xCCCCCCCC;
GPIOG->AFR[1] = 0xCCCCCCCC;
/* Configure PGx pins in Alternate function mode */
GPIOG->MODER = 0xAAAAAAAA;
/* Configure PGx pins speed to 50 MHz */
GPIOG->OSPEEDR = 0xAAAAAAAA;
/* Configure PGx pins Output type to push-pull */
GPIOG->OTYPER = 0x00000000;
/* No pull-up, pull-down for PGx pins */
GPIOG->PUPDR = 0x00000000;
#if defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F469_479xx)
/* Connect PHx pins to FMC Alternate function */
GPIOH->AFR[0] = 0x00C0CC00;
GPIOH->AFR[1] = 0xCCCCCCCC;
/* Configure PHx pins in Alternate function mode */
GPIOH->MODER = 0xAAAA08A0;
/* Configure PHx pins speed to 50 MHz */
GPIOH->OSPEEDR = 0xAAAA08A0;
/* Configure PHx pins Output type to push-pull */
GPIOH->OTYPER = 0x00000000;
/* No pull-up, pull-down for PHx pins */
GPIOH->PUPDR = 0x00000000;
/* Connect PIx pins to FMC Alternate function */
GPIOI->AFR[0] = 0xCCCCCCCC;
GPIOI->AFR[1] = 0x00000CC0;
/* Configure PIx pins in Alternate function mode */
GPIOI->MODER = 0x0028AAAA;
/* Configure PIx pins speed to 50 MHz */
GPIOI->OSPEEDR = 0x0028AAAA;
/* Configure PIx pins Output type to push-pull */
GPIOI->OTYPER = 0x00000000;
/* No pull-up, pull-down for PIx pins */
GPIOI->PUPDR = 0x00000000;
#endif /* STM32F427_437xx || STM32F429_439xx || STM32F469_479xx */
/*-- FMC Configuration ------------------------------------------------------*/
/* Enable the FMC interface clock */
RCC->AHB3ENR |= 0x00000001;
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN);
/* Configure and enable SDRAM bank1 */
#if defined(STM32F446xx)
FMC_Bank5_6->SDCR[0] = 0x00001954;
#else
FMC_Bank5_6->SDCR[0] = 0x000009C9;
#endif /* STM32F446xx */
FMC_Bank5_6->SDTR[0] = 0x01115351;
/* SDRAM initialization sequence */
#if 1
/* Clock enable command */
FMC_Bank5_6->SDCMR = 0x00000011;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Delay */
for (index = 0; index<1000; index++);
/* PALL command */
FMC_Bank5_6->SDCMR = 0x00000012;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Auto refresh command */
#if defined(STM32F446xx)
FMC_Bank5_6->SDCMR = 0x000000F3;
#else
FMC_Bank5_6->SDCMR = 0x00000073;
#endif /* STM32F446xx */
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* MRD register program */
#if defined(STM32F446xx)
FMC_Bank5_6->SDCMR = 0x00044014;
#else
FMC_Bank5_6->SDCMR = 0x00046014;
#endif /* STM32F446xx */
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Set refresh count */
tmpreg = FMC_Bank5_6->SDRTR;
#if defined(STM32F446xx)
FMC_Bank5_6->SDRTR = (tmpreg | (0x0000050C<<1));
#else
FMC_Bank5_6->SDRTR = (tmpreg | (0x0000027C<<1));
#endif /* STM32F446xx */
/* Disable write protection */
tmpreg = FMC_Bank5_6->SDCR[0];
FMC_Bank5_6->SDCR[0] = (tmpreg & 0xFFFFFDFF);
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
#endif
(void)(tmp);
/*
Bank1_SDRAM is configured as follow:
FMC_SDRAMTimingInitStructure.FMC_LoadToActiveDelay = 2;
FMC_SDRAMTimingInitStructure.FMC_ExitSelfRefreshDelay = 6;
FMC_SDRAMTimingInitStructure.FMC_SelfRefreshTime = 4;
FMC_SDRAMTimingInitStructure.FMC_RowCycleDelay = 6;
FMC_SDRAMTimingInitStructure.FMC_WriteRecoveryTime = 2;
FMC_SDRAMTimingInitStructure.FMC_RPDelay = 2;
FMC_SDRAMTimingInitStructure.FMC_RCDDelay = 2;
FMC_SDRAMInitStructure.FMC_Bank = FMC_Bank1_SDRAM;
FMC_SDRAMInitStructure.FMC_ColumnBitsNumber = FMC_ColumnBits_Number_9b;
FMC_SDRAMInitStructure.FMC_RowBitsNumber = FMC_RowBits_Number_13b;
FMC_SDRAMInitStructure.FMC_SDMemoryDataWidth = FMC_SDMemory_Width_8b;
FMC_SDRAMInitStructure.FMC_InternalBankNumber = FMC_InternalBank_Number_4;
FMC_SDRAMInitStructure.FMC_CASLatency = FMC_CAS_Latency_3;
FMC_SDRAMInitStructure.FMC_WriteProtection = FMC_Write_Protection_Disable;
FMC_SDRAMInitStructure.FMC_SDClockPeriod = FMC_SDClock_Period_2;
FMC_SDRAMInitStructure.FMC_ReadBurst = FMC_Read_Burst_disable;
FMC_SDRAMInitStructure.FMC_ReadPipeDelay = FMC_ReadPipe_Delay_1;
FMC_SDRAMInitStructure.FMC_SDRAMTimingStruct = &FMC_SDRAMTimingInitStructure;
*/
}