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的时序初始化实现及内存测试
继续上一篇的话题,写到SDRAM通过CubeMx配置后,在工程代码编写时直接引用的是我事先写好的时序初始化、内存测试文件,而未对其进行详细的解释,所以本篇文章就来娓娓道来。不多说,开始吧 SDRAM的初始化流程简述 SDRAM初…...
联想电脑一键重装系统Win10操作方法
很多用户都会利用重装系统的方法,来解决系统崩溃、病毒感染等问题。但是,很多新手用户不知道联想电脑Win10系统重装的详细方法步骤,下面小编给大家详细介绍关于联想电脑Win10系统重装的操作方法,帮助大家轻松快速地完成系统的重装…...
Mysql数据库 1.概述
Mysql内容概述 1. Mysql概述 数据库相关概念: 名称 全称 简称 数据库 存储数据的仓库,数据是有组织的进行存储 …...
Qt编程,文件操作、UDP通信
目录 1、文件类 QFile 2、 UPD/TCP网络编程 1、##UDP客户端 2、##UDP服务器端 1、文件类 QFile QFile file(filename); file.exists() file.setFileName(filename1); file.fileName() file.bytesAvailable() file.size() file.copy("2.txt") file1.errorString(…...
Docker 的数据管理和Dockerfile镜像的创建
目录 Docker 的数据管理 管理 Docker 容器中数据的方式 端口映射 容器互联(使用centos镜像) Docker 镜像的创建 Dockerfile 操作常用的指令 编写 Dockerfile 时格式 Dockerfile 案例 Docker 的数据管理 管理 Docker 容器中数据的方式 管理 Doc…...
[python] 利用 Pydoc 快速生成整个 Python 项目的文档
如何写注释 class MyClass:"""This is a simple example class.Attributes:param1 (int): The first parameter.param2 (str): The second parameter."""def __init__(self, param1, param2):"""The constructor for MyClass.:p…...
Maven 配置指南
目录 一、配置本地存储库 二、配置并行Artifact 解析 三、安全和部署设置 四、将镜像用于存储库 五、Profiles 六、可选配置 七、Settings 八、安全性 九、工具链 Maven配置发生在3个级别: 项目-大多数静态配置发生在pom.xml中安装-这是为Maven安装添加的…...
第十八章 类和对象——多态
一、多态的基本概念 多态是C面向对象三大特性之一 多态分为两类 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名 动态多态: 派生类和虚函数实现运行时多态 静态多态和动态多态区别: 静态多态的函数地址早绑定 - 编译阶段确定函数地址 动…...
京东数据平台:2023年服饰行业销售数据分析
最近看到有些消费机构分析,不少知名的运动品牌都把“主战场”放到了冲锋衣,那么羽绒服市场就比较危险了。但其实羽绒服市场也有机会点可寻。 先来说冲锋衣。的确,从今年的销售数据以及增长情况,冲锋衣的确会是今年冬天的大热门品…...
Nginx proxy_set_header参数设置
一、不设置 proxy_set_header Host 不设置 proxy_set_header Host 时,浏览器直接访问 nginx,获取到的 Host 是 proxy_pass 后面的值,即 $proxy_host 的值,参考Module ngx_http_proxy_module 1 2 3 4 5 6 7 8 # cat ngx_header.c…...
如何用 ChatGPT 的 Advanced Data Analysis 帮你采集数据?
(注:本文为小报童精选文章,已订阅小报童或加入知识星球「玉树芝兰」用户请勿重复付费) 想采集网页数据却不会写 Python 爬虫?不会就不会吧,ChatGPT 会就可以了 😂 问题描述 朋友最近遇到了一点儿…...
Linux运行环境搭建系列-Flink安装
Flink安装 ## 下载 https://archive.apache.org/dist/flink/flink-1.16.2 ## 解压 tar -zxvf flink-1.16.2-bin-scala_2.12.tgz && rm -rf flink-1.16.2-bin-scala_2.12.tgz ## 启动 cd flink-1.16.2/bin ## 修改/etc/hosts文件,把第一行的127.0.0.1改成自…...
求最大bit数(java)
题目描述 求一个int类型数字对应的二进制数字中1的最大连续数 例如3的二进制为00000011,最大连续2个1 数据范围:数据组数:11t15,11n1500000进阶: 时间复杂度: O(logn),空间复杂度: O(1) 输入: 200 输出 2 说明 200的二进制表示是11001000&am…...
【Java 进阶篇】JavaScript 与 HTML 的结合方式
JavaScript是一种广泛应用于Web开发中的脚本语言,它与HTML(Hypertext Markup Language)结合使用,使开发人员能够创建交互式和动态的网页。在这篇博客中,我们将深入探讨JavaScript与HTML的结合方式,包括如何…...
华为云云耀云服务器L实例评测 | 实例评测使用之硬件参数评测:华为云云耀云服务器下的 Linux 磁盘目录分析神器 ncdu
华为云云耀云服务器L实例评测 | 实例评测使用之硬件参数评测:华为云云耀云服务器下的 Linux 磁盘目录分析神器 ncdu 介绍华为云云耀云服务器 华为云云耀云服务器 (目前已经全新升级为 华为云云耀云服务器L实例) 华为云云耀云服务器…...
Linux大老都是怎么记住这么多命令的?
今天给大家带来的是面试/实际工作中经常用到的Linux相关操作命令: 一. vi/vim编辑器 ---->文本编辑器 作用:创建文件,编辑文件,查看文件 格式:vi/vim 文件的名字 解析:如果该文件不存在,vi就会创建该…...
LoRa技术未来发展前景:物联网和边缘计算的引领者
随着物联网和边缘计算的快速发展,低功耗广域网(LoRa)技术在连接远距离设备、实现长距离通信和满足低功耗需求方面崭露头角。本文将分析LoRa技术在未来的发展前景,尤其是在物联网和边缘计算领域的潜在影响。 LoRa技术的核心优势 1…...
出现 PowerShell终端执行conda activate无效 解决方法
目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 在cmd命令行中执行conda activate 可以成功,但是回pycharm的PowerShell终端执行该命令,一直无效 具体过程如下: PS E:\pythonProject\SteganoGAN-master> conda activate py37 PS E:\pythonProject\SteganoGAN-mas…...
Python实现某音短视频JS XB逆向解析
哈喽兄弟们,今天来实现一下某音短视频的JS逆向解析。 知识点 动态数据抓包在这里插入代码片 requests发送请求 X-Bogus 参数逆向环境模块 python 3.8 运行代码 pycharm 2022.3 辅助敲代码 requests pip install request…...
SecureCRT 自动测试脚本的使用方法
脚本示例(get_batteryifo_interval_2s.vbs): Sub Main Do While(1)crt.Screen.Send "pm_client batteryinfo" & chr(13)crt.Sleep 2000 Loop End Sub 1. 解压 SecureCRT 压缩包(网上下载);…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...
