目录
一、SDRAM硬件设计
通过这个硬件设计我们要了解到以下几点知识:
- STM32F429 采用的 32 位 FMC 接口驱动镁光的 SDRAM,型号 MT48LC4M32B2,最高支持 143MHz的时钟, 容量 16MB。
- 标准的 SDRAM 一般都是 4 个 BANK,这个芯片也不例外,芯片的总容量:1Mbit x 32bit x 4bank = 128Mbit 。每个 BANK 由 4096rows x 256columns x 32bits 组成。这个比较重要,配置的时候要用到,也就是 12 行 8 列。
- 片选采用的 SDNE0, 那么 SDRAM 的首地址是 0xC000 000, 控制 16MB 的空间。
- 用到引脚所代表的含义:
二、SRAM驱动设计
1、tRCD( TRCD):
在发送列读写命令时必须要与行有效命令有一个间隔,这个间隔被定义为 tRCD,即 RAS to CAS Delay( RAS 至 CAS 延迟),大家也可以理解为行选通周期,这应该是根据芯片存储阵列电子元件响应时间(从一种状态到另一种状态变化的过程)所制定的延迟。 tRCD 是 SDRAM 的一个重要时序参数,广义的 tRCD 以时钟周期数为单位,比如 tRCD=2,就代表延迟周期为两个时钟周期。 具体到确切的时间,则要根据时钟频率而定,对于 STM32F429 驱动 SDRAM,采用的 168MHz, 实际使用要做 2 分频,即 84MHz, 那么我们设置 tRCD=2, 就代表 23.8ns 的延迟。
2、CL( CAS Latency):
在选定列地址后,就已经确定了具体的存储单元,剩下的事情就是数据通过数据 I/O 通道( DQ)输出到内存总线上了。但是在 CAS 发出之后,仍要经过一定的时间才能有数据输出,从 CAS 与读取命令发出到第一笔数据输出的这段时间,被定义为 CL( CAS Latency, CAS 潜伏期)。由于 CL 只在读取时出现,所以 CL 又被称为读取潜伏期( RL, Read Latency)。 CL 的单位与 tRCD 一样,为时钟周期数,具体耗时由时钟频率决定。数据写入的操作也是在 tRCD 之后进行,但此时没有了 CL(记住, CL 只出现在读取操作中)。
3、tWR( TWR):
数据并不是即时地写入存储电容,因为选通三极管(就如读取时一样)与电容的充电必须要有一段时间,所以数据的真正写入需要一定的周期。为了保证数据的可靠写入,都会留出足够的写入/校正时间( tWR, WriteRecovery Time),这个操作也被称作写回( Write Back)。
4、tRP( TRP):
在发出预充电命令之后,要经过一段时间才能允许发送 RAS 行有效命令打开新的工作行,这个间隔被称为 tRP( Precharge command Period,预充电有效周期)。和 tRCD、 CL 一样, tRP 的单位也是时钟周期数,具体值视时钟频率而定。
三、SRAM相关配置
1、FMC 时钟源选择
STM32F429 的 FMC 是采用的 HCLK 时钟, 位于 AHB 总线上。比如主频设置的是 168MHz,那么FMC 时钟也是 168MHz。FMC 驱动 SDRAM 的话,必须对 FMC 的时钟做 2 分频或者 3 分频, 而且仅支持这两种分频方式,也就是说, SDRAM 时钟可以选择 168MHz/2 = 84MHz 或者 168MHz/3 = 56MHz。
2、时延配置
SDRAM_Timing.LoadToActiveDelay = 2; /* 2x11.9ns, TMRD定义加载模式寄存器的命令与激活命令或刷新命令之间的延迟 */
SDRAM_Timing.ExitSelfRefreshDelay = 7; /* 7x11.9ns, TXSR定义从发出自刷新命令到发出激活命令之间的延迟 */
SDRAM_Timing.SelfRefreshTime = 4; /* 5x11.9ns, TRAS定义最短的自刷新周期 */
SDRAM_Timing.RowCycleDelay = 7; /* 7x11.9ns, TRC定义刷新命令和激活命令之间的延迟 */
SDRAM_Timing.WriteRecoveryTime = 2; /* 2x11.9ns, TWR定义在写命令和预充电命令之间的延迟 */
SDRAM_Timing.RPDelay = 2; /* 2x11.9ns, TRP定义预充电命令与其它命令之间的延迟 */
SDRAM_Timing.RCDDelay = 2; /* 2x11.9ns, TRCD定义激活命令与读/写命令之间的延迟 */
3、SDRAM配置
hsdram.Init.SDBank = FMC_SDRAM_BANK1; /* 硬件设计上用的BANK1 */
hsdram.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8; /* 8列 */
hsdram.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12; /* 12行 */
hsdram.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32; /* 32位带宽 */
hsdram.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; /* SDRAM有4个BANK */
hsdram.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3; /* CAS Latency可以设置Latency1,2和3,实际测试Latency3稳定 */
hsdram.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; /* 禁止写保护 */
hsdram.Init.SDClockPeriod = SDCLOCK_PERIOD; /* FMC时钟168MHz,2分频后给SDRAM,即84MHz */
hsdram.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE; /* 使能读突发 */
hsdram.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1; /* 此位定CAS延时后延后多少个SDRAM时钟周期读取数据,实际测此位可以设置无需延迟 */
四、 用户侧
进行到这一步,已经可以像使用内部 SRAM 一样使用 SDRAM 了。 除了本章节配套例子采用指针方式操作 SDRAM, 也可以采用下面帖子中的超方便使用方式和动态内存分配方式。
#define EXT_SDRAM_ADDR ((uint32_t)0xC0000000)
#define EXT_SDRAM_SIZE (16 * 1024 * 1024)
/* LCD显存,第1页, 分配2M字节 */
#define SDRAM_LCD_BUF1 EXT_SDRAM_ADDR
/* LCD显存,第2页, 分配2M字节 */
#define SDRAM_LCD_BUF2 (EXT_SDRAM_ADDR + SDRAM_LCD_SIZE)
#define SDRAM_LCD_SIZE (2 * 1024 * 1024) /* 每层2M */
#define SDRAM_LCD_LAYER 2 /* 2层 */
/* 剩下的12M字节,提供给应用程序使用 */
#define SDRAM_APP_BUF (EXT_SDRAM_ADDR + SDRAM_LCD_SIZE * SDRAM_LCD_LAYER)
#define SDRAM_APP_SIZE (EXT_SDRAM_SIZE - SDRAM_LCD_SIZE * SDRAM_LCD_LAYER)