引言
本应用笔记提供如何在APM32F4xx系列上配置和应用DMC接口从而访问SDRAM的指南。
SDRAM简介
SDRAM全称为Synchronous Dynamic Random Access Memory,同步动态随机存储器。同步是指数据的传输共用一个时钟线作为基准;动态是指存储阵列需要不断的刷新来保证数据不丢失;随机是指数据不是线性依次存储,而是自由指定地址进行数据读写。
存储结构
SDRAM中,每个行列地址组合对应一个存储单元,存储单元的大小由SDRAM的规格决定,通常为16bit。一块SDRAM通常有2个Bank或4个Bank。一块SDRAM的容量计算是:(2^行地址位数)x(2^列地址位数)x(存储单元大小)x(Bank数量)。
SDRAM参数简述
CAS Latency
CAS指的是列地址选通信号,地址寻址先行地址再列地址,所以等地址选通之后,就确定了存储单元,接下来就是数据传输。CAS等待时间(CAS Latency)指的是CAS发出之后到第一笔数据输出的这一段时间。CAS Latency的数值不能超过芯片的设计规范,否则会导致内存的不稳定。
tRCD
tRCD即RAS to CAS Delay,翻译过来就是RAS到CAS延时,RAS为行地址选通信号,CAS为列地址选通信号。这是根据芯片存储阵列电子元件响应时间所定制的延迟。
tRP
在对SDRAM进行完读写操作后,如果要对同一个Bank的另一行进行寻址,就要将原来有效的行关闭,重新发送行/列地址。关闭现有工作行,准备打开新行的操作就是预充电(Precharge)。在发出预充电命令后,需要经过一段时间才能允许发送RAS信号打开新的工作行,这段时间称为tRP(RAS Precharge Time)。
tRAS
tRAS即Active to Precharge Command,表示行激活到预充电命令的间隔。
tWR
tWR即Write Recovery Time,可以保证数据的可靠写入。
突发长度
突发(Burst)是指在同一行中相邻的存储单元连续进行数据传输的方式,连续传输所涉及到存储单元的数量就是突发长度。
刷新
动态存储器要不断进行刷新(Refresh)才能保留住数据,因此它是SDRAM最重要的操作。刷新操作有固定的周期,依次对所有行进行操作,以保留那些久久没有经历重写的存储体中的数据。
两个刷新的间隔多长合适呢?目前公认的标准是,存储体中电容的数据有效保存期上限是64ms,也就是刷新所有行的时间是64ms,这样刷新速度就是:行数量/64ms。
刷新操作分为两种:自动刷新(Auto Refresh)与自刷新(Self Refresh)。两种刷新都不需要外部提供行地址信息,因为这是一个内部的自动操作。
对于自动刷新,SDRAM内部有一个行刷新计数器自动地依次生成行地址。由于刷新是针对一行中的所有存储体进行,所以无需列寻址,或者说CAS在RAS之前有效。所以,自动刷新又称为CBR式刷新。由于刷新涉及到所有Bank,因此在刷新过程中,所有Bank都停止工作,而每次刷新所占用的时间为9个时钟周期,之后就可以进入正常的工作状态,也就是说在这9个时钟周期内,所有工作指令只能等待而无法执行。64ms之后则再次对同一行进行刷新,如此周而复始进行循环刷新。
自刷新则主要用于休眠模式低功耗状态下的数据保存,这方面最著名的应用就是STR。在发出自动刷新命令时,将CKE置于无效状态,就进入了自刷新模式,此时不再依靠系统时钟工作,而是根据内部的时钟进行刷新操作。在自刷新期间除了CKE之外所有外部信号都是无效的,只有重新使能CKE才能退出自刷新模式并进入正常操作状态
DMC简介
DMC是一个动态存储控制器,通过它外接SDRAM能够实现SDRAM的读写。如下图1所示,通过DMC可以将SDRAM的数据与AHB总线进行传输。
图 1 DMC结构框图
DMC引脚定义
DMC的引脚定义如下表所示:
表格 1 DMC引脚定义
信号名称 | 输入/输出 | 管脚 | 功能 |
A0 | 输出 | PF1 | 地址 |
A1 | 输出 | PF2 | 地址 |
A2 | 输出 | PF3 | 地址 |
A3 | 输出 | PF4 | 地址 |
A4 | 输出 | PF6 | 地址 |
A5 | 输出 | PF7 | 地址 |
A6 | 输出 | PF8 | 地址 |
A7 | 输出 | PF9 | 地址 |
A8 | 输出 | PF10 | 地址 |
A9 | 输出 | PH3 | 地址 |
A10 | 输出 | PF0 | 地址 |
D0 | 输入/输出 | PG3 | 双向数据 |
D1 | 输入/输出 | PG4 | 双向数据 |
D2 | 输入/输出 | PG5 | 双向数据 |
D3 | 输入/输出 | PG6 | 双向数据 |
D4 | 输入/输出 | PG8 | 双向数据 |
D5 | 输入/输出 | PH13 | 双向数据 |
D6 | 输入/输出 | PH15 | 双向数据 |
D7 | 输入/输出 | PI3 | 双向数据 |
D8 | 输入/输出 | PH8 | 双向数据 |
D9 | 输入/输出 | PH10 | 双向数据 |
D10 | 输入/输出 | PD10 | 双向数据 |
D11 | 输入/输出 | PD12 | 双向数据 |
D12 | 输入/输出 | PD13 | 双向数据 |
D13 | 输入/输出 | PD14 | 双向数据 |
D14 | 输入/输出 | PD15 | 双向数据 |
D15 | 输入/输出 | PG2 | 双向数据 |
BA | 输出 | PI11 | Bank 地址 |
CKE | 输出 | PH5 | 时钟使能 |
CLK/CK | 输出 | PG1 | 时钟 |
LDQM | 输入 | PG15 | 16位数据写入 |
UNQM | 输入 | PF11 | 16位数据读取 |
NWE | 输出 | PI7 | 写使能 |
NCAS | 输出 | PI8 | 列地址位选通命令 |
NRAS | 输出 | PI9 | 行地址位选通命令 |
NCS | 输出 | PI10 | 片选 |
行列地址线共用引脚,当前APM32F407支持一根bank地址线,即可以支持2个bank。
DMC引脚初始化
DMC的引脚初始化可以参考如下代码示例,GPIO需要配置引脚复用:
GPIO_Config_T gpioConfig; RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOD); gpioConfig.speed = GPIO_SPEED_50MHz; gpioConfig.mode = GPIO_MODE_AF; gpioConfig.otype = GPIO_OTYPE_PP; gpioConfig.pupd = GPIO_PUPD_NOPULL; gpioConfig.pin = GPIO_PIN_10; /** 以PD10引脚作为配置参考,其他引脚类似 */ GPIO_Config(GPIOD, &gpioConfig); /** 需要配置引脚复用为EMMC */ GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_10, GPIO_AF_EMMC); |
DMC的初始化及SDRAM访问
SDRAM应用的流程图:
图 2 程序流程图
初始化DMC
DMC初始化结构体
DMC_Config_T结构体定义在APM32F4xx_dmc.h文件中,具体定义如下:
/** * @brief DMC Config struct */ typedef struct { DMC_BANK_WIDTH_T bankWidth; //!< Number of bank bits DMC_ROW_WIDTH_T rowWidth; //!< Number of row address bits DMC_COL_WIDTH_T colWidth; //!< Number of col address bits DMC_CLK_PHASE_T clkPhase; //!< Clock phase DMC_TimingConfig_T timing; //!< Timing }DMC_Config_T; |
结构体成员中:
bankWidth表示bank地址宽度,设置为1时,能支持2个bank,为2时,支持4个bank;
rowWidth 表示行地址宽度
colWidth表示行地址宽度
clkPhase表示时钟相位
timing为DMC的时序配置,对应DMC_TimingConfig_T结构体成员。
目前APM32F407的DMC支持行地址11位,支持列地址8位,支持1位bank,在存储单元为16bit数据即2个bytes的情况下,支持的最大容量为2^11 * 2^8 * 2 * 2 = 2Mbytes。
timing类型为DMC_TimingConfig_T结构体,其定义在APM32F4xx_dmc.h文件中,具体定义如下:
/** * @brief Timing config definition */ typedef struct { uint32_t latencyCAS : 2; //!< DMC_CAS_LATENCY_T uint32_t tRAS : 4; //!< DMC_RAS_MINIMUM_T uint32_t tRCD : 3; //!< DMC_DELAY_TIME_T uint32_t tRP : 3; //!< DMC_PRECHARGE_T uint32_t tWR : 2; //!< DMC_NEXT_PRECHARGE_T uint32_t tARP : 4; //!< DMC_AUTO_REFRESH_T uint32_t tCMD : 4; //!< DMC_ATA_CMD_T uint32_t tXSR : 9; //!< auto-refresh commands, can be 0x000 to 0x1FF uint16_t tRFP : 16;//!< Refresh period, can be 0x0000 to 0xFFFF }DMC_TimingConfig_T; |
结构体成员中:
latencyCAS表示CAS等待时间;
tRAS表示行激活和预充电的之间的最小时间;
tRCD表示RAS 到 CAS 的延迟时间;
tRP表示预充电周期;
tWR表示写最后一个数据到下一次预充电的时间;
tARP 表示两次自动刷新命令间的最小时间间隔;
tCMD 表示Active to active 命令周期;
tXSR 表示退出自刷新切换至激活命令或自动刷新读命令之间的最小间隔时间;
tRFP 表示连续两次刷新间隔;
上述参数的单位为DMC时钟周期,DMC的时钟由AHB时钟经过DMC分频而来。
DMC配置
配置DMC可以参考下方代码:
uint32_t sdramCapacity; DMC_Config_T dmcConfig; DMC_TimingConfig_T timingConfig; RCM_EnableAHB3PeriphClock(RCM_AHB3_PERIPH_EMMC); timingConfig.latencyCAS = DMC_CAS_LATENCY_3; timingConfig.tARP = DMC_AUTO_REFRESH_10; timingConfig.tRAS = DMC_RAS_MINIMUM_2; timingConfig.tCMD = DMC_ATA_CMD_1; timingConfig.tRCD = DMC_DELAY_TIME_1; timingConfig.tRP = DMC_PRECHARGE_1; timingConfig.tWR = DMC_NEXT_PRECHARGE_2; timingConfig.tXSR = 3; timingConfig.tRFP = 0x2F9; dmcConfig.bankWidth = DMC_BANK_WIDTH_1; dmcConfig.clkPhase = DMC_CLK_PHASE_REVERSE; dmcConfig.rowWidth = DMC_ROW_WIDTH_11; dmcConfig.colWidth = DMC_COL_WIDTH_8; dmcConfig.timing = timingConfig; DMC_Config(&dmcConfig); //!< 配置DMC DMC_EnableAccelerateModule(); //!<开启DMC缓冲器,可以提升SDRAM读性能 DMC_Enable(); //!< 使能DMC |
在配置时序参数时需要根据SDRAM的数据手册,以及当前系统时钟分配到DMC外设的时钟频率来对各个参数进行合理配置。上述代码中的参数配置示例仅供参考。
SDRAM访问
SDRAM的起始地址为0x60000000,根据不同容量,其结束地址与之对应。例如容量为2Mbytes的SDRAM的结束地址为0x60200000。在程序中我们访问SDRAM可以通过地址直接访问,无需解锁。
例如对SDARM的地址0x60000000进行数据读取,读取大小为1个字节,则可以使用语句:
uint8_t data = *(uint8_t*) (0x6000000); |
例如对SDARM的地址0x60000000写入一个字节,为0x55,则可以使用语句:
*(uint8_t*) (0x6000000) = 0x55; |