20.2 FMC驱动SDRAM的时序初始化实现及内存测试

2023-10-15 01:20

本文主要是介绍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的时序初始化实现及内存测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/214445

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount