本文主要是介绍20.2 FMC驱动SDRAM的时序初始化实现及内存测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
继续上一篇的话题,写到SDRAM通过CubeMx配置后,在工程代码编写时直接引用的是我事先写好的时序初始化、内存测试文件,而未对其进行详细的解释,所以本篇文章就来娓娓道来。不多说,开始吧
SDRAM的初始化流程简述
SDRAM初始化流程
SDRAM的初始化流程基本都是一样的
SDRAM并不是一上电就可以直接读写数据的,而是需要一系列的步骤进行初始化,对存储矩阵进行预充电、刷新并设置模式寄存器,详细见下图:
(1)、SDRAM上电并提供稳定的时钟信号,至少等待100us
(2)、发送空操作命令(NOP)
(3)、发送于预充电命令(PRECHANGE),控制所有Bank进行预充电,并等待TRP时间
(4)、发送至少2个自动刷新命令(AUTO REFRESH),每个命令后需等待TRFC时间;TRFC表示自动刷新时间;
(5)、发送加载模式寄存器命令(LOAD MODE REGISTER),配置SDRAM的工作参数,并等待TMRD时间,TMRD表示加载模式寄存器与行或刷新命令之间的延迟时间;
(6)、初始化流程完毕,可以开始读写数据
接下来我们就应该在FMC对SDRAM参数初始化完毕后进行SDRAM流程时序初始化
SDRAM初始化流程代码实现
首先来看下整体流程:
首先MX_FMC_Init()函数内调用HAL_SDRAM_Init()对SDRAM基本参数及时序参数进行初始化,然后才调用编写好的sdram_driver文件内的流程初始化函数void sdram_InitialTimingSequence(SDRAM_HandleTypeDef *hsdram);
此代码是fmc.c文件的函数
/* FMC initialization function */
void MX_FMC_Init(void)
{/* USER CODE BEGIN FMC_Init 0 *//* USER CODE END FMC_Init 0 */FMC_SDRAM_TimingTypeDef SdramTiming = {0};/* USER CODE BEGIN FMC_Init 1 *//* USER CODE END FMC_Init 1 *//** Perform the SDRAM1 memory initialization sequence*/hsdram1.Instance = FMC_SDRAM_DEVICE;/* hsdram1.Init */hsdram1.Init.SDBank = FMC_SDRAM_BANK1;hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;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_DISABLE;hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;/* SdramTiming */SdramTiming.LoadToActiveDelay = 2;SdramTiming.ExitSelfRefreshDelay = 8;SdramTiming.SelfRefreshTime = 5;SdramTiming.RowCycleDelay = 6;SdramTiming.WriteRecoveryTime = 4;SdramTiming.RPDelay = 2;SdramTiming.RCDDelay = 2;if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK){Error_Handler( );}/* USER CODE BEGIN FMC_Init 2 */sdram_InitialTimingSequence(&hsdram1);//SDRAM时序初始化/* USER CODE END FMC_Init 2 */
}
本片主要的SDRAM时序初始化函数就是sdram_InitialTimingSequence(&hsdram1)这个了,函数内部代码也是按照上面讲解的顺序进行一一对应:时钟使能->延时100us->对所有的Banks预充电->自刷新->编程sdram的加载模式寄存器
最后配置stm32 FMC的sdram控制器的自动刷新周期
函数内部使用了HAL带的SDRAM发送命令函数和编程自刷新周期函数
/****************************************************
@function:初始化SDRAM时序
@param:hsdram--sdram句柄
@return:void
@note:
****************************************************/
void sdram_InitialTimingSequence(SDRAM_HandleTypeDef *hsdram)
{FMC_SDRAM_CommandTypeDef Command;//时钟使能Command.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;Command.AutoRefreshNumber = 1;Command.ModeRegisterDefinition = 0;HAL_SDRAM_SendCommand(hsdram,&Command,0xFFFF);//延时至少200usHAL_Delay(1);//对所有的Banks预充电Command.CommandMode = FMC_SDRAM_CMD_PALL;Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;Command.AutoRefreshNumber = 1;Command.ModeRegisterDefinition = 0;HAL_SDRAM_SendCommand(hsdram,&Command,0xFFFF);//插入8个自动刷新周期Command.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;Command.AutoRefreshNumber = 8;Command.ModeRegisterDefinition = 0;HAL_SDRAM_SendCommand(hsdram,&Command,0xFFFF);//编程sdram的加载模式寄存器Command.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;Command.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;Command.AutoRefreshNumber = 1;Command.ModeRegisterDefinition = 0x230;HAL_SDRAM_SendCommand(hsdram,&Command,0xFFFF);//配置stm32 FMC的sdram控制器的自动刷新周期//Refresh rate = (SDRAM refresh rate * SDRAM clock frequency) - 20//SDRAM refresh rate = SDRAM refresh period / Number of rows//SDRAM refresh rate = 64ms / 8196(rows) = 7.81us//Refresh rate = 7.81us * 100Mhz - 20 = 761HAL_SDRAM_ProgramRefreshRate(hsdram,761);
}
SDRAM发送命令函数HAL_SDRAM_SendCommand()
函数原型如下:
/*** @brief Sends Command to the SDRAM bank.* @param hsdram pointer to a SDRAM_HandleTypeDef structure that contains* the configuration information for SDRAM module.* @param Command SDRAM command structure* @param Timeout Timeout duration* @retval HAL status*/
HAL_StatusTypeDef HAL_SDRAM_SendCommand(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_CommandTypeDef *Command,uint32_t Timeout)
FMC_SDRAM_CommandTypeDef 结构体:
/*** @brief SDRAM command parameters structure definition*/
typedef struct
{uint32_t CommandMode; /*!< Defines the command issued to the SDRAM device.This parameter can be a value of @ref FMC_SDRAM_Command_Mode. */uint32_t CommandTarget; /*!< Defines which device (1 or 2) the command will be issued to.This parameter can be a value of @ref FMC_SDRAM_Command_Target. */uint32_t AutoRefreshNumber; /*!< Defines the number of consecutive auto refresh command issuedin auto refresh mode.This parameter can be a value between Min_Data = 1 and Max_Data = 15 */uint32_t ModeRegisterDefinition; /*!< Defines the SDRAM Mode register content */
} FMC_SDRAM_CommandTypeDef;
CommandMode参数是待发送的命令
CommandTarget参数是目标存储器区域;发送给FMC控制下的SDRAM1还是SDRAM2
AutoRefreshNumber参数是自动刷新数;若发送的是自动刷新命令,此处为发送的刷新次数,其它命令时无效
ModeRegisterDefinition参数;若发送的是加载模式寄存器命令,此处为要写入 SDRAM 模式寄存器的参数;当向 SDRAM 发送加载模式寄存器命令时,这个结构体成员的值将通过地址线发送到SDRAM 的模式寄存器中,这个成员值长度为 13 位,各个位一一对应 SDRAM 的模式寄存器。
初始化到这一步SDRAM还差自动刷新配置啦
配置stm32 FMC的sdram控制器的自动刷新周期
SDRAM为什么需要这个自动刷新呢,因为SDRAM内部是一个个的电容,电容电压会随时间进行放电过程,所以原本为1的数据对应电容,在很久不操作就会变成电量0;所以需要间隔一定时间进行及时充电刷新,这个间隔就是自动刷新周期。
可以看到使用的这个W9825G6KH刷新周期要求是64毫秒
看看HAL库刷新周期配置函数:
/*** @brief Programs the SDRAM Memory Refresh rate.* @param hsdram pointer to a SDRAM_HandleTypeDef structure that contains* the configuration information for SDRAM module.* @param RefreshRate The SDRAM refresh rate value* @retval HAL status*/
HAL_StatusTypeDef HAL_SDRAM_ProgramRefreshRate(SDRAM_HandleTypeDef *hsdram, uint32_t RefreshRate)
RefreshRate SDRAM刷新率
那这个参数如何配置呢,可以在参考手册中看这个刷新速率的解释
SDRAM时钟频率在上一篇文档中,工程配置的是FMC 200MHz / SDRAM二分频 = 100MHz
那么SDRAM的行数是多少呢,可以看下芯片数据手册寻找
可以看到行数等于8192行,我的工程代码里面用的是8196行,直接沿用的数据手册,只不过芯片数据手册这个才是最正确的哈,也不修改了喔
此处函数的参数RefreshRate 就是上面看到的COUNT参数喔
刷新速率 = 64ms / 8192行 = 7.812us
RefreshRate = 7.812us * 100MHz - 20 = 761
所以这样配置后SDRAM就会间隔自动刷新了,这之后就可以正常访问SDRAM数据了
SDRAM的内存测试
这部分就不过多说了,只需要申请指定SDRAM1开始的内存地址,32M大小的数组即可
然后进行数据写入、读取对比,若写入数据和读取数据不等,则SDRAM内存数据错误,此时应检查配置参数是否正常,然后进行降低SDRAM频率,直到可以正确读写数据为止。
读写数据测试函数如下所示
/****************************************************
@function:SDRAM内存简单测试
@param:void
@return:void
@note:
****************************************************/
uint32_t pbuffer[(32*1024*1024)/4] __attribute__((at(0xC0000000)));//0xC0000000是SDRAM1的起始地址
void sdram_test(void)
{uint32_t i = 0,err = 0;while(1){for(i=0;i < (32*1024*1024)/4;i++){pbuffer[i] = i;}err = 0;for(i=0;i < (32*1024*1024)/4;i++){if(pbuffer[i] != i)err++;else if(i < 10)printf("pbuffer[%d]=%d\n",i,pbuffer[i]);}if(err){printf("err:%d\n",err);while(1)HAL_Delay(25);}}
}
这篇关于20.2 FMC驱动SDRAM的时序初始化实现及内存测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!