STM32CUBEMX配置SDRAM
驱动例子使用的型号为STM32H750XBH6,SDRAM的型号为W9812G6KH-6I。
1.SDRAM的基本工作原理
SDRAM的引脚主要有以下这些。
主要包含的功能可以分类为逻辑控制线,地址线和数据线。
SDRAM 内部包含的存储阵列,可以把它理解成一张表格,数据就填在这张表格上。和表格查找一样,指定一个行地址和列地址,就可以精确地找到目标单元格,这是SDRAM 芯片寻址的基本原理。这样的每个单元格被称为存储单元,而这样的表则被称为存储阵列(Bank),目前设计的 SDRAM 芯片基本上内部都包含有 4 个这样的 Bank,寻址时指定 Bank 号以及行地址,然后再指定列地址即可寻找到目标存储单元。
在寻址时行和列地址都公用一组地址线,行、列地址选通信号线控制当前地址线表示的行地址还是列地址,参考下面的数据手册,使用的芯片的列地址线宽度是12,行地址线宽度是9。
SDRAM的数据线宽度是16bit,但是进行数据传输的时候不一定是按照16位进行传输的,在传输8位数据的时候只有低8位的数据是有效的,因此还需要DQM数据掩码信号来配合使用,每根DQM信号线代表8位数据,高电平表示数据有效,低电平表示数据无效。
操作SDRAM需要一系列命令,如下表。
SDRAM要不断进行刷新(Refresh)才能保留住数据,因此它是 DRAM 最重要的操作刷新操作与预充电中重写的操作本质是一样的。
但因为预充电是对一个或所有 Bank 中的工作行操作,并且不定期,而刷新则是有固定的周期,依次对所有行进行操作,以保证那些久久没被访问的存储单元数据正确。刷新操作分为两种:“自动刷新” (Auto Refresh) 与“自我刷新”(SelfRefresh),发送命令后 CKE 时钟为有效时(低电平),使用自动刷新操作,否则使用自我刷新操作。不论是何种刷新方式,都不需要外部提供行地址信息,因为这是一个内部的自动操作。
对于“自动刷新”,SDRAM 内部有一个行地址生成器(也称刷新计数器) 用来自动地依次生成行地址,每收到一次命令刷新一行。在刷新过程中,所有 Bank 都停止工作,而每次刷新所占用的时间为N个时钟周期(视 SDRAM型号而定,通常为 N=9),刷新结束之后才可进入正常的工作状态,也就是说在这N个时钟期间内,所有工作指令只能等待而无法执行。一次次地按行刷新,刷新完所有行后,将再次对第一行重新进行刷新操作,这人对同一行刷新操作的时间间隔,称为 SDRAM的刷新周期,通常为 64ms。显然刷新会对SDRAM的性能造成影响,但这是它的 DRAM 的特性决定的,也是 DRAM 相对于 SRAM取得成本优势的同时所付出的代价。
“自我刷新”则主要用于休眠模式低功耗状态下的数据保存,也就是说即使外部控制器不工作了,SDRAM都能自己确保数据正常。在发出“自我刷新”命令后,将 CKE 置于无效状态(低电平),就进入自我刷新模式,此时不再依靠外部时钟工作,而是根据 SDRAM内部的时钟进行刷新操作。在自我刷新期间除了 CKE 之外的所有外部信号都是无效的,只有重新使 CKE 有效才能退出自我刷新模式并进入正常操作状态。
SDRAM上电之后需要进行初始化才能正常工作,初始化的流程如下:
更详细的关于SDRAM的内容可以去搜索其他资料。
2.开始配置FMC外设驱动SDRAM
W9812G6KH-6I的数据手册截图
由上图可以得到使用的SDRAM的速度等级是166MHz。最快的驱动SDRAM的时钟不能超过这个数。
SDRAM的驱动需要用到FMC外设,首先需要确定外设的时钟来源,参考STM32H750数据手册和CUBEMX的时钟配置,FMC的时钟来源有这几个,一般就选默认的HCLK3就可以。
现在240M的时钟是输入到FMC外设的时钟,而FMC外设驱动SDRAM的的时钟是240M经过分频之后得到的。必须对这个时钟进行二分频或者三分频之后才能用于驱动SDRAM。
二分频之后是120M,单个时钟的周期是8.333ns。需要记住这个时间。后面需要进行计算。
SDRAM控制器的引脚功能如下:
进行初始化需要配置SDRAM的基本参数
hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1;