STM32CUBEMX配置SDRAM

STM32CUBEMX配置SDRAM

驱动例子使用的型号为STM32H750XBH6,SDRAM的型号为W9812G6KH-6I。

1.SDRAM的基本工作原理

SDRAM的引脚主要有以下这些。

STM32CUBEMX配置SDRAM_第1张图片

主要包含的功能可以分类为逻辑控制线,地址线和数据线。

SDRAM 内部包含的存储阵列,可以把它理解成一张表格,数据就填在这张表格上。和表格查找一样,指定一个行地址和列地址,就可以精确地找到目标单元格,这是SDRAM 芯片寻址的基本原理。这样的每个单元格被称为存储单元,而这样的表则被称为存储阵列(Bank),目前设计的 SDRAM 芯片基本上内部都包含有 4 个这样的 Bank,寻址时指定 Bank 号以及行地址,然后再指定列地址即可寻找到目标存储单元。

STM32CUBEMX配置SDRAM_第2张图片

在寻址时行和列地址都公用一组地址线,行、列地址选通信号线控制当前地址线表示的行地址还是列地址,参考下面的数据手册,使用的芯片的列地址线宽度是12,行地址线宽度是9。

STM32CUBEMX配置SDRAM_第3张图片

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上电之后需要进行初始化才能正常工作,初始化的流程如下:

STM32CUBEMX配置SDRAM_第4张图片

更详细的关于SDRAM的内容可以去搜索其他资料。

2.开始配置FMC外设驱动SDRAM

W9812G6KH-6I的数据手册截图

​由上图可以得到使用的SDRAM的速度等级是166MHz。最快的驱动SDRAM的时钟不能超过这个数。

SDRAM的驱动需要用到FMC外设,首先需要确定外设的时钟来源,参考STM32H750数据手册和CUBEMX的时钟配置,FMC的时钟来源有这几个,一般就选默认的HCLK3就可以。

STM32CUBEMX配置SDRAM_第5张图片

现在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;

1.使用bank1,这里的bank和SDRAM芯片中的bank不是一个概念,这里的bank是指stm32可以用两个SDRAM,这里使用的是第一个。

2.3.根据上面的数据手册可以知道列地址线宽度是12,行地址线宽度是9

4.SDRAM芯片使用的数据位宽是16位的

5.SDRAM芯片内部有4个bank

6.CAS Latency 可以设置 Latency1,Latency2 和 Latency3,实际测试 Latency3 稳定。

7.关闭写保护

8.控制驱动SDRAM的时钟分频,这里设置的是2分频

9.使能突发读传输

10.此位定义 CAS 延时后延后多少个 SDRAM 时钟周期读取数据,实际测此位可以设置延迟不延迟都行

驱动SDRAM需要获得一些重要的时序参数,

  /* SdramTiming */
  SdramTiming.LoadToActiveDelay = 2;
  SdramTiming.ExitSelfRefreshDelay = 7;
  SdramTiming.SelfRefreshTime = 4;
  SdramTiming.RowCycleDelay = 7;
  SdramTiming.WriteRecoveryTime = 3;
  SdramTiming.RPDelay = 2;
  SdramTiming.RCDDelay = 2;

SdramTiming.LoadToActiveDelay

TMRD参数,TMRD 定义加载模式寄存器的命令与激活命令或刷新命令之间的延迟

这个没在手册中找到,姑且参考别的程序设置为2

SdramTiming.ExitSelfRefreshDelay

TXSR参数

TXSR 定义从发出自刷新命令到发出激活命令之间的延迟。

参考后面表格为72ns,一个时钟周期是8.3ns,所以需要8.6个时钟周期,设置为9个

SdramTiming.SelfRefreshTime

TRAS

TRAS 定义最短的自刷新周期

参考后面的表格,42ns,需要6个时钟周期

SdramTiming.RowCycleDelay

TRC

TRC 定义刷新命令和激活命令之间的延迟。

参考后面的表格,60ns,需要8个时钟周期

SdramTiming.WriteRecoveryTime

TWR

TWR 定义在写命令和预充电命令之间的延迟

根据后面的表格,需要2个时钟周期,芯片的最大时钟周期是166M,因此单个周期的时间是6.02ns,2个时钟周期是12ns,我们的周期是8.3,所以也要设置为2个周期才能满足

SdramTiming.RPDelay

TRP

TRP 定义预充电命令与其它命令之间的延迟。

根据后面的表格,需要15ns,因此设置为2

SdramTiming.RCDDelay

TRCD

TRCD 定义激活命令与读/写命令之间的延迟

根据后面的表格,需要15ns,因此设置为2

​​

​​

STM32CUBEMX配置SDRAM_第6张图片

SDRAM的时序要求可以从数据手册中获得

配置好FMC之后还需要对SDRAM进行初始化操作。

#define SDRAM_BANK_ADDR     ((uint32_t)0xC0000000) 				// FMC SDRAM 数据基地址
#define FMC_COMMAND_TARGET_BANK   FMC_SDRAM_CMD_TARGET_BANK1	//	SDRAM 的bank选择
#define SDRAM_TIMEOUT     ((uint32_t)0x1000) 						// 超时判断时间

#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) 

/******************************************************************************************************
*	函 数 名: SDRAM_Initialization_Sequence
*	入口参数: hsdram - SDRAM_HandleTypeDef定义的变量,即表示定义的sdram
*				 Command	- 控制指令
*	返 回 值: 无
*	函数功能: SDRAM 参数配置
*	说    明: 配置SDRAM相关时序和控制方式
*******************************************************************************************************/

void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command)
{
	__IO uint32_t tmpmrd = 0;

	/* Configure a clock configuration enable command */
	Command->CommandMode 				= FMC_SDRAM_CMD_CLK_ENABLE;	// 开启SDRAM时钟 
	Command->CommandTarget 				= FMC_COMMAND_TARGET_BANK; 	// 选择要控制的区域
	Command->AutoRefreshNumber 		= 1;
	Command->ModeRegisterDefinition 	= 0;

	HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);	// 发送控制指令
	HAL_Delay(1);		// 延时等待

	/* Configure a PALL (precharge all) command */ 
	Command->CommandMode 				= FMC_SDRAM_CMD_PALL;		// 预充电命令
	Command->CommandTarget 				= FMC_COMMAND_TARGET_BANK;	// 选择要控制的区域
	Command->AutoRefreshNumber 		= 1;
	Command->ModeRegisterDefinition 	= 0;

	HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);  // 发送控制指令

	/* Configure a Auto-Refresh command */ 
	Command->CommandMode 				= FMC_SDRAM_CMD_AUTOREFRESH_MODE;	// 使用自动刷新
	Command->CommandTarget 				= FMC_COMMAND_TARGET_BANK;          // 选择要控制的区域
	Command->AutoRefreshNumber			= 8;                                // 自动刷新次数
	Command->ModeRegisterDefinition 	= 0;

	HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);	// 发送控制指令

	/* Program the external memory mode register */
	tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_2         |
							SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |
							SDRAM_MODEREG_CAS_LATENCY_3           |
							SDRAM_MODEREG_OPERATING_MODE_STANDARD |
							SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;

	Command->CommandMode					= FMC_SDRAM_CMD_LOAD_MODE;	// 加载模式寄存器命令
	Command->CommandTarget 				= FMC_COMMAND_TARGET_BANK;	// 选择要控制的区域
	Command->AutoRefreshNumber 		= 1;
	Command->ModeRegisterDefinition 	= tmpmrd;

	HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);	// 发送控制指令

	HAL_SDRAM_ProgramRefreshRate(hsdram, 1855);  // 配置刷新率

}

15-20行发送时钟使能命令

21.延时等待,必不可少

24-29发送整个SDRAM预充电命令

32-37发送自动刷新命令

40-51配置SDRAM模式寄存器

53配置SDRAM刷新率

关于刷新频率的数值是这么得到的。目前公认的标准是SDRAM 中电容保存数据的上限是64ms,也就是说每一行刷新的循环周期是64ms。这样刷新速度就是:64ms /行数量。我们在看内存规格时,经常会看到4096 Refresh Gycles/64ms 或 8192 RefreshCycles/64ms的标识,这里的4096与8192就代表这个芯片中每个L-Bank的行数。刷新命令一次对一行有效,发送间隔也是随总行数而变化,4096行时为15.625μs,8192行时就为7.8125μs。

刷新计数 =(SDRAM refresh period/Number of rows)*SDRAM时钟速度-20=(64/1000/4096) * 120 000 000 - 20 = 1855

实际上这个数值稍差点,在使用 SDRAM 时,基本都没有影响的。

上面这一套操作下来,SDRAM就算初始化完毕了,可以正常使用了。

3.由STM32CUBEMX配置

首先配置好时钟等基础外设,不再赘述

STM32CUBEMX配置SDRAM_第7张图片

按照上面的配置的顺序选就行,第一个红框的时钟使能信号是有两组的,看用的是哪组就选哪组。下面的选项就按照配置即可。

  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;
  /* SdramTiming */
  SdramTiming.LoadToActiveDelay = 2;
  SdramTiming.ExitSelfRefreshDelay = 7;
  SdramTiming.SelfRefreshTime = 4;
  SdramTiming.RowCycleDelay = 7;
  SdramTiming.WriteRecoveryTime = 3;
  SdramTiming.RPDelay = 2;
  SdramTiming.RCDDelay = 2;

配置SdramTiming.WriteRecoveryTime = 3;时可能cubemx说这个太小了,不让设置。但是在生成工程之后在代码里改了,也没出现问题。

STM32CUBEMX配置SDRAM_第8张图片

cubemx生成的工程是只有fmc的初始化,没有用命令初始化SDRAM,因此在生成工程之后还应该在fmc初始化之后还要加上SDRAM初始化的代码。

工程文件也上传了,有需要的可以参考一下。

工程文件下载

你可能感兴趣的:(STM32,stm32,单片机,嵌入式硬件)