DSP

STM32F429 >> 22. FMC_扩展外部SDRAM(二)

2019-07-13 17:00发布

FMC 简介

STM32F429 使用 FMC 外设来管理扩展的存储器,FMC 是 Flexible Memory Controller的缩写,译为可变存储控制器。
它可以用于驱动包括 SRAM、SDRAM、NOR FLASH 以及NAND FLSAH 类型的存储器。 在其它系列的 STM32 控制器中,只有 FSMC 控制器(Flexible Static Memory Controller),译为可变静态存储控制器,所以它们不能驱动 SDRAM这样的动态存储器,因为驱动 SDRAM 时需要定时刷新,STM32F429 的 FMC 外设才支持该功能,且只支持普通的 SDRAM,不支持 DDR 类型的 SDRAM。

FMC 框图刨析

在这里插入图片描述

1. 通讯引脚

在框图的右侧是 FMC 外设相关的控制引脚,由于控制不同类型存储器的时候会有一些不同的引脚,看起来有非常多,其中地址线 FMC_A 和数据线 FMC_D 是所有控制器都共用的。
这些 FMC 引脚具体对应的 GPIO 端口及引脚号可在《STM32F4xx 规格书》中搜索查找到,不在此列出。 针对 SDRAM 控制器,我们是整理出以下的 FMC 与 SDRAM 引脚对照表:
在这里插入图片描述
在这里插入图片描述
其中比较特殊的是 FMC_A[15:14]引脚用作 Bank 的寻址线; 而 FMC_SDCKE 线和FMC_SDNE 都各有 2 条,FMC_SDCKE 用于控制 SDRAM 的时钟使能,FMC_SDNE 用于控制 SDRAM 芯片的片选使能。 它们用于控制 STM32 使用不同的存储区域驱动 SDRAM,使用编号为 0 的信号线组会使用 STM32 的存储器区域 1,使用编号为 1 的信号线组会使用存储器区域 2。
使用不同存储区域时,STM32 访问 SDRAM 的地址不一样,具体将在“FMC 的地址映射”小节讲解。

2. 存储器控制器

上面不同类型的引脚是连接到 FMC 内部对应的存储控制器中的。 NOR/PSRAM/SRAM设备使用相同的控制器;
NAND/PC 卡设备使用相同的控制器;
而 SDRAM 存储器使用独立的控制器。
不同的控制器有专用的寄存器用于配置其工作模式。 控制 SDRAM 的有 FMC_SDCR1/FMC_SDCR2 控制寄存器、FMC_SDTR1/FMC_SDTR2 时序寄存器、FMC_SDCMR 命令模式寄存器以及 FMC_SDRTR刷新定时器寄存器。
其中控制寄存器及时序寄存器各有 2 个,分别对应于 SDRAM 存储区域 1 和存储区域 2 的配置。
  1. FMC_SDCR 控制寄存器可配置 SDCLK 的同步时钟频率、突发读使能、写保护、CAS延迟、行列地址位数以及数据总线宽度等。
  2. FMC_SDTR 时序寄存器用于配置 SDRAM 访问时的各种时间延迟,如 TRP 行预充电延迟、TMRD 加载模式寄存器激活延迟等。
  3. FMC_SDCMR 命令模式寄存器用于存储要发送到 SDRAM 模式寄存器的配置,以及要向 SDRAM 芯片发送的命令。
  4. FMC_SDRTR 刷新定时器寄存器用于配置 SDRAM 的自动刷新周期。

3. 时钟控制逻辑

FMC 外设挂载在 AHB3 总线上,时钟信号来自于 HCLK(默认 180MHz),控制器的时钟输出就是由它分频得到。
如 SDRAM 控制器的 FMC_SDCLK 引脚输出的时钟,是用于与SDRAM 芯片进行同步通讯,它的时钟频率可通过 FMC_SDCR1 寄存器的 SDCLK 位配置,可以配置为 HCLK 的 1/2 或 1/3,也就是说,与 SDRAM 通讯的同步时钟最高频率为90MHz。

FMC 的地址映射

FMC 连接好外部的存储器并初始化后,就可以直接通过访问地址来读写数据,这种地址访问与 I2C EEPROM、SPI FLASH 的不一样,后两种方式都需要控制 I2C 或 SPI 总线给存储器发送地址,然后获取数据;
在程序里,这个地址和数据都需要分开使用不同的变量存储,并且访问时还需要使用代码控制发送读写命令。而使用 FMC 外接存储器时,其存储单元是映射到 STM32 的内部寻址空间的;
在程序里,定义一个指向这些地址的指针,然后就可以通过指针直接修改该存储单元的内容,FMC 外设会自动完成数据访问过程,读写命令之类的操作不需要程序控制。 FMC 地址映射见图:
在这里插入图片描述
图中左侧的是 Cortex-M4 内核的存储空间分配,右侧是 STM32 FMC 外设的地址映射。 可以看到 FMC 的 NOR/PSRAM/SRAM/NAND FLASH 以及 PC 卡的地址都在 External RAM地址空间内,而 SDRAM 的地址是分配到 External device 区域的。
正是因为存在这样的地址映射,使得访问 FMC 控制的存储器时,就跟访问 STM32 的片上外设寄存器一样(片上外设的地址映射即图中左侧的“Peripheral”区域)。

1. SDRAM 的存储区域

FMC 把 SDRAM 的存储区域分成了 Bank1 和 Bank2 两块,这里的 Bank 与 SDRAM 芯片内部的 Bank 是不一样的概念,只是 FMC 的地址区域划分而已。
每个 Bank 有不一样的起始地址,且有独立的 FMC_SDCR 控制寄存器和 FMC_SDTR 时序寄存器,还有独立的FMC_SDCKE 时钟使能信号线和 FMC_SDCLK 信号线。FMC_SDCKE0 和 FMC_SDCLK0对应的存储区域 1 的地址范围是 0xC000 0000-0xCFFF FFFF,而 FMC_SDCKE1 和FMC_SDCLK1 对应的存储区域 2 的地址范围是 0xD000 0000- 0xDFFF FFFF。 当程序里控制内核访问这些地址的存储空间时,FMC 外设会即会产生对应的时序,对它外接的SDRAM 芯片进行读写。

2. External RAM 与 External device 的区别

比较遗憾的是 FMC 给 SDRAM 分配的区域不在 External RAM 区,这个区域可以直接执行代码,而 SDRAM 所在的 External device 区却不支持这个功能。 这里说的可直接执行代码的特性就是在“常用存储器”章节介绍的 XIP(eXecute In Place)特性,即存储器上若存储了代码,CPU 可直接访问代码执行,无需缓存到其它设备上再运行; 而且 XIP 特性还对存储器的种类有要求,SRAM/SDRAM 及 NOR Flash 都支持这种特性,而 NAND FLASH及 PC 卡是不支持 XIP 的。
结合存储器的特性和 STM32 FMC 存储器种类的地址分配,就发现它的地址规划不合理了,NAND FLASH 和 PC 卡这些不支持 XIP 的存储器却占据了External RAM 的空间,而支持 XIP 的 SDRAM 存储器的空间却被分配到了 Extern device 区。 为了解决这个问题,通过配置“SYSCFG_MEMRMP”寄存器的“SWP_FMC”寄存器位可用于交换 SDRAM 与 NAND/PC 卡的地址映射,使得存储在 SDRAM 中的代码能被执行,只是由于 SDRAM 的最高同步时钟是 90MHz,代码的执行速度会受影响。 本章主要讲解当 STM32 的片内 SRAM 不够用时使用 SDRAM 扩展内存,但假如程序太大,它的程序空间 FLASH 不够用怎么办呢?
首先是裁剪代码,目前 STM32F429 系列芯片内部 FLASH 空间最高可达 2MB,实际应用中只要我们把代码中的图片、字模等占据大空间的内容放到外部存储器中,纯粹的代码很难达到 2MB。如果还不够用,非要扩展程序空间的话,一种方法是使用 FMC 扩展 NOR FLASH,把程序存储到 NOR 上,程序代码能够直接在 NOR FLASH 上执行。另一种方法是把程序存储在其它外部存储器,如 SD 卡,需要时把存储在 SD 卡上的代码加载到 SRAM 或 SDRAM 上,再在 RAM 上执行代码。 如果 SDRAM 不是用于存储可执行代码,只是用来保存数据的话,在 External RAM 或Exteranl device 区域都没有区别,不需要与 NAND 的映射地址交换。

SDRAM 时序结构体

控制 FMC 使用 SDRAM 存储器时主要是配置时序寄存器以及控制寄存器,利用 ST 标准库的 SDRAM 时序结构体以及初始化结构体可以很方便地写入参数。 SDRAM 时序结构体的成员:
在这里插入图片描述
这个结构体成员定义的都是 SDRAM 发送各种命令后必须的延迟,它的配置对应到FMC_SDTR 中的寄存器位。
所有成员参数值的单位是周期,参数值大小都可设置成“1-16”。 关于这些延时时间的定义可以看STM32F429 >> 22. FMC_扩展外部SDRAM(一)的时序图进行了解

1. FMC_LoadToActiveDelay

本成员设置 TMRD 延迟(Load Mode Register to Active),即发送加载模式寄存器命令后要等待的时间,过了这段时间才可以发送行有效或刷新命令。

2. FMC_ExitSelfRefreshDelay

本成员设置退出 TXSR 延迟(Exit Self-refresh delay),即退出自我刷新命令后要等待的时间,过了这段时间才可以发送行有效命令。

3. FMC_SelfRefreshTime

本成员设置自我刷新时间 TRAS,即发送行有效命令后要等待的时间,过了这段时间才执行预充电命令。

4. FMC_RowCycleDelay

本成员设置 TRC 延迟(Row cycle delay),即两个行有效命令之间的延迟,以及两个相邻刷新命令之间的延迟

5. FMC_WriteRecoveryTime

本成员设置 TWR 延迟(Recovery delay),即写命令和预充电命令之间的延迟,等待这段时间后才开始执行预充电命令。

6. FMC_RPDelay

本成员设置 TRP 延迟(Row precharge delay),即预充电命令与其它命令之间的延迟

7. FMC_RCDDelay

本成员设置 TRCD 延迟(Row to column delay),即行有效命令到列读写命令之间的延迟
这个 SDRAMTimingInitTypeDef 时序结构体配置的延时参数,将作为下一节的 FMC SDRAM 初始化结构体的一个成员。

SDRAM 初始化结构体

在这里插入图片描述
在这里插入图片描述
这个结构体中,除最后一个成员是上一小节讲解的时序配置外,其它结构体成员的配置都对应到 FMC_SDCR 中的寄存器位。

1. FMC_Bank

本成员用于选择 FMC 映射的 SDRAM 存储区域,可选择存储区域 1 或 2 (FMC_Bank1/2_SDRAM)。

2. FMC_ColumnBitsNumber

本成员用于设置要控制的 SDRAM 的列地址宽度,可选择 8-11 位(FMC_ColumnBits_Number_8/9/10/11b)。

3. FMC_RowBitsNumber

本成员用于设置要控制的 SDRAM 的行地址宽度,可选择设置成 11-13 位(FMC_RowBits_Number_11/12/13b)。

4. FMC_SDMemoryDataWidth

本成员用于设置要控制的 SDRAM 的数据宽度,可选择设置成 8、16 或 32 位(FMC_SDMemory_Width_8/16/32b)。

5. FMC_InternalBankNumber

本成员用于设置要控制的 SDRAM 的内部 Bank 数目,可选择设置成 2 或 4 个 Bank 数目(FMC_InternalBank_Number_2/4),请注意区分这个结构体成员与 FMC_Bank 的区别

6. FMC_CASLatency

本成员用于设置 CASLatency 即 CL 的时钟数目,可选择设置为 1、2 或 3 个时钟周期(FMC_CAS_Latency_1/2/3)。

7. FMC_WriteProtection

本成员用于设置是否使能写保护模式,如果使能了写保护则不能向 SDRAM 写入数据,正常使用都是禁止写保护的。

8. FMC_SDClockPeriod

本成员用于设置 FMC 与外部 SDRAM 通讯时的同步时钟参数,可以设置成 STM32 的HCLK 时钟频率的 1/2 、 1/3 或 禁 止 输 出 时 钟 (FMC_SDClock_Period_2/3 或FMC_SDClock_Disable)。

9. FMC_ReadBurst

本成员用于设置是否使能突发读取模式,禁止时等效于 BL=1,使能时 BL 的值等于模式寄存器中的配置。

10. FMC_ReadPipeDelay

本成员用于配置在 CASLatency 个时钟周期后,再等待多少个 HCLK 时钟周期才进行数据采样,在确保正确的前提下,这个值设置为越短越好,可选择设置的参数值为 0、1 或 2 个 HCLK 时钟周期(FMC_ReadPipe_Delay_0/1/2)。

11. FMC_SDRAMTimingStruct

这个成员就是我们上一小节讲解的 SDRAM 时序结构体了,设置完时序结构体后再把赋值到这里即可。

SDRAM 命令结构体

控制 SDRAM 时需要各种命令,通过向 FMC 的命令模式寄存器 FMC_SDCMR 写入控制参数,即可控制 FMC 对外发送命令,为了方便使用,STM32 标准库也把它封装成了结构体:
在这里插入图片描述

1. FMC_CommandMode

本成员用于配置将要发送的命令,它可以被赋值为下表中的宏,这些宏代表了不同命令;
在这里插入图片描述

2. FMC_CommandTarget

本成员用于选择要控制的 FMC 存储区域,可选择存储区域 1 或2(FMC_Command_Target_bank1/2);

3. FMC_AutoRefreshNumber

有时需要连续发送多个 “自动刷新”(Auto Refresh)命令时,配置本成员即可控制它发送多少次,可输入参数值为 1-16,若发送的是其它命令,本参数值无效。 如FMC_CommandMode 成员被配置为宏 FMC_Command_Mode_AutoRefresh,而FMC_AutoRefreshNumber 被设置为 2 时,FMC 就会控制发送 2 次自动刷新命令。

4. FMC_ModeRegisterDefinition

当向 SDRAM 发送加载模式寄存器命令时,这个结构体成员的值将通过地址线发送到SDRAM 的模式寄存器中,这个成员值长度为 13 位,各个位一一对应 SDRAM 的模式寄存器。

编程要点

  1. 初始化通讯使用的目标引脚及端口时钟;
  2. 使能FMC 外设时钟;
  3. 配置FMC SDRAM 的时序、工作模式;
  4. 根据SDRAM 的初始化流程编写初始化函数;
  5. 建立机制访问外部SDRAM 存储器;
  6. 编写测试程序,对读写数据进行校验。

我的一点小想法

其实利用FMC 控制SDRAM 实属方便,只需初始化时配置一下SDRAM 的各种延迟时间和FMC 的各种相应配置,然后即可利用配置好的FMC 的相应封装函数对SDRAM 进行初始化配置。 其实利用FMC 控制SDRAM 和控制其他芯片的过程是相似的,在此:
  1. FMC 相当于进行芯片通讯所使用的通信外设一样,例如SPI、I2C 模块;
  2. 设置SDRAM 延时时间就相当于设置通信时序;
  3. 初始化FMC 和SDRAM 就相当于初始化通信模块和外设芯片;
  4. 然后就可以通过地址映射像写内核外设寄存器的值一样去写SDRAM
需要注意的是,在与外接芯片通讯时往往需要利用相应的通讯协议进行数据发送和接收,但是在初始化SDRAM,即向SDRAM 发送需要配置寄存器的值时,是直接将SDRAM 命令结构体配置成相应模式,然后利用FMC_SDRAMCmdConfig(FMC_SDRAMCommandTypeDef* FMC_SDRAMCommandStruct) 函数把需要配置的模式的值载入SDRAM 中实现初始化。
简言之,向SDRAM 的寄存器写值,不需要专门SendByte() 函数进行数据发送,而是直接调用上面这个命令配置函数即可实现寄存器写值。