当前位置: 首页 > news >正文

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);}}
}
http://www.lryc.cn/news/192298.html

相关文章:

  • 联想电脑一键重装系统Win10操作方法
  • Mysql数据库 1.概述
  • Qt编程,文件操作、UDP通信
  • Docker 的数据管理和Dockerfile镜像的创建
  • [python] 利用 Pydoc 快速生成整个 Python 项目的文档
  • Maven 配置指南
  • 第十八章 类和对象——多态
  • 京东数据平台:2023年服饰行业销售数据分析
  • Nginx proxy_set_header参数设置
  • 如何用 ChatGPT 的 Advanced Data Analysis 帮你采集数据?
  • Linux运行环境搭建系列-Flink安装
  • 求最大bit数(java)
  • 【Java 进阶篇】JavaScript 与 HTML 的结合方式
  • 华为云云耀云服务器L实例评测 | 实例评测使用之硬件参数评测:华为云云耀云服务器下的 Linux 磁盘目录分析神器 ncdu
  • Linux大老都是怎么记住这么多命令的?
  • LoRa技术未来发展前景:物联网和边缘计算的引领者
  • 出现 PowerShell终端执行conda activate无效 解决方法
  • Python实现某音短视频JS XB逆向解析
  • SecureCRT 自动测试脚本的使用方法
  • 常用图像标注工具
  • hadoop namenode -format报错显示:命令未找到
  • prostate数据集下载
  • 比较和同步数据库架构和数据:MssqlMerge Pro Crack
  • R语言——赋值(= ,<- ,<<-)
  • 图形界面四则运算计算器(Python+PyQt5)
  • SQL Server 创建表
  • JS数组或数组对象去重常用方法
  • allure测试报告生成逻辑--解决在Jenkins里打开allure报告页面后空白显示无数据问题(以window环境为例)
  • 基于JAYA优化的BP神经网络(分类应用) - 附代码
  • JavaScript进阶(二十六):ES各版本特性详解