【STM32F429-DISCOVERY学习笔记】STM32F429驱动SDRAM(IS42S16400J)详解

2024-02-05 07:48

本文主要是介绍【STM32F429-DISCOVERY学习笔记】STM32F429驱动SDRAM(IS42S16400J)详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 http://bbs.armfly.com/read.php?tid=1942


 驱动SDRAM的时序比较的麻烦一些,不像驱动SRAM,非常简单,网上搜索一下,估计有非常多的FPGA驱

动SDRAM的资料,而且是各种的给你讲时序问题,现在F429/439集成了控制 器以后就方便很多了,用户只需配
相应的寄存器即可,这里向大家推荐一篇文章,强烈的推荐, 不懂SDRAM为何物的,一定要看看。
《高手进阶,终极内存技术指南——完整/进阶版》 http://bbs.armfly.com/read.php?tid=1930 
1.  学习SDRAM驱动前的准备工作
    学习SDRAM前搞清楚两个问题,一个是SDRAM的基本原理,还有一个就是那几个关键的参数 ,参 数是F429
配置 SDRAM的关键,这几个参数大概知道是什么意思就行了,配置的时候,根据 SDRAM的手册配置 一下就OK
了。 在STM32F429/439的数据手册里关键参数说明,F429/439是把这几个关键的参数做到了一个 存器 里面
了,这些参数,手册上面有一些英文说明,但比较的笼统。

我推荐的那篇文章,建议大家一定要看,别的可以不看,这个必须得看,讲的实在太好了,我这里 把一些关键的
参数摘录出来:
tRCD:
       在发送列读写命令时必须要与行有效命令有一个间隔,这个间隔被定义为tRCD,即RAS to CAS 
Delay(RAS至CAS延迟),大家也可以理解为行选通周期,这应该是根据芯片存储阵列电子元件响
应时间(从一种状态到另一种状态变化的过程)所制定的延迟。tRCDSDRAM的一个重要时序参数
,可以通过主板BIOS经过北桥芯片进行调整,但不能超过厂商的预定范围。广义的tRCD以时钟周期
tCKClock Time)数为单位,比如tRCD=2,就代表延迟周期为两个时钟周期,具体到确切的时
,则要根据时钟频率而定,对于PC100SDRAMtRCD=2,代表20ns的延迟,对于PC133则为15ns
CL(CAS Latency):
        在选定列地址后,就已经确定了具体的存储单元,剩下的事情就是数据通过数据I/O通道(DQ)输
出到内存总线上了。但是在CAS发出之后,仍要经过一定的时间才能有数据输出,从CAS与读取命令发
出到第一笔数据输出的这段时间,被定义为CL(CAS Latency,CAS潜伏期)。由于CL只在读取时出现,
以CL又被称为读取潜伏期(RL,Read Latency)。CL的单位与tRCD一样,为时钟周期数,具体耗时
时钟频率决定。
       数据写入的操作也是在tRCD之后进行,但此时没有了CL(记住,CL只出现在读取操作中)。
tWR:
        数据并不是即时地写入存储电容,因为选通三极管(就如读取时一样)与电容的充电必须要有一段
时间,所以数据的真正写入需要一定的周期。为了保证数据的可靠写入,都会留出足够的写入/校正时间
tWRWriteRecovery Time),这个操作也被称作写回(Write Back)。
tRP:
        在发出预充电命令之后,要经过一段时间才能允许发送RAS行有效命令打开新的工作行,这个间隔被
称为tRP(Precharge command Period,预充电有效周期)。和tRCDCL一样,tRP的单位也是时钟周
期数,具体值视时钟频率而定。
我这里就先贴上这几个参数,其它的参数,大家可以查阅相关的资料。
2.  F429/439手册中对SDRAM的介绍
       如果你看了我在前面推荐的那个文章,现在看这个手册还是比较容易的。我这里就象征性的贴一下F429
439自带SDRAM控制器的特性和引脚,其它的大家自己看手册就行。



3.  F429/439驱动SDRAM详细过程
说明:以官方的Demonstration做例子


第一步:F429的时钟配置
        F429/439支持最高主频180Mhz,相比于407只提高了12M, 感觉劲还是不足,传说STM32F5系列将
做到250MHz,就相当的给力了。
F429配置过程和以前芯片的配置过程是一样的,我这里主要是想通过这个配置得到SDRAM的时钟频率, 先
说明一下这个开发板外接的是8MHz的晶振,通过锁相环得到180的主频。

#define PLL_SOURCE_HSE    // HSE (8MHz) used to clock the PLL, and the PLL is used as system clock source
/*  PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N  */
#if defined  (PLL_SOURCE_HSI)
#define PLL_M      16
#else
#define PLL_M      8
#endif
#define PLL_N      360
/* SYSCLK = PLL_VCO / PLL_P */  
#define PLL_P      2
/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
#define PLL_Q      7
由上面的公式得到 : SYSCLK = PLL_VCO / PLL_P =( (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N)/PLL_P  = 180
/* HCLK = SYSCLK / 1 = 180 */ RCC->CFGR |= RCC_CFGR_HPRE_DIV1;   
/* PCLK2 = HCLK / 2 = 90 */ RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;  
/* PCLK1 = HCLK / 4 = 45 */ RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
第二步:引脚配置
        驱动前要先搞清楚一个问题,驱动SDRAM的行选和列选的地址线是分时复用的,和SRAM不同
需要完整的地址线才可以访问各个地址空间,官方提供的原理图


关于这个芯片的特点:


标准的SDRAM一般都是4个BANK,这个芯片也不例外,芯片的总容量
1Mbitx 16-bit x 4-bank = 67,108,864 bits = 64-Mbit ,每个BANK的组成
4096rows x 256 columns x 16 bits, 这个比较重要,配置的时候要用到,也就是
12行8列。
第三步:FMC配置
这里FMC驱动SDRAM只支持两种频率,分别是
SDCLK period = 2 x HCLK periods
SDCLK period = 3 x HCLK periods
根据我们上面的配置,HCLK是180MHz,这里SDCLK就是2分频或者3分频,官方提供的
这个例子,在注释上面有误,他们是按照主频168MHz,2分频是SDCLK = 84MHz注释的,
不过不影响使用,只是注释上面是这样的。按照90MHz的话,每个时钟周期就是11.1ns
7个SDRAM关键参数的配置,下面一个一个的说。
1.  TMRD
/* TMRD: 2 Clock cycles */
FMC_SDRAMTimingInitStructure.FMC_LoadToActiveDelay = 2; 
这里为什么成2,查手册, 手册上提供的是三种速度等级时提供的参数, 我们这里是用
的90MHz,也取2个肯定是没问题的。


2. TXSR
/* TXSR: min=70ns (6x11.10ns) */
FMC_SDRAMTimingInitStructure.FMC_ExitSelfRefreshDelay = 7;
开发板上面用的SDRAM速度等级的7,最高工作频率时143MHz


3. TRAS
/* TRAS: min=42ns (4x11.10ns) max=120k (ns) */
FMC_SDRAMTimingInitStructure.FMC_SelfRefreshTime = 4;


4. TRC
/* TRC:  min=63 (6x11.10ns) */        
FMC_SDRAMTimingInitStructure.FMC_RowCycleDelay = 7;    


5. TWR
/* TWR:  2 Clock cycles */
FMC_SDRAMTimingInitStructure.FMC_WriteRecoveryTime = 2;  

6. TRP
/* TRP:  15ns => 2x11.10ns */
FMC_SDRAMTimingInitStructure.FMC_RPDelay = 2; 


7. TRCD                
/* TRCD: 15ns => 2x11.10ns */
FMC_SDRAMTimingInitStructure.FMC_RCDDelay = 2;


时序设置好以后就是SDRAM控制器的配置。

/* 支持两个SDRAM的BANK,这里使用的是bank2  */
  FMC_SDRAMInitStructure.FMC_Bank = FMC_Bank2_SDRAM;
  /* 根据这个SDRAM的特性是12行,8列,在这里配置一下 */
  FMC_SDRAMInitStructure.FMC_ColumnBitsNumber = FMC_ColumnBits_Number_8b;
  FMC_SDRAMInitStructure.FMC_RowBitsNumber = FMC_RowBits_Number_12b;
  /* 数据位宽是16 */
  FMC_SDRAMInitStructure.FMC_SDMemoryDataWidth = SDRAM_MEMORY_WIDTH;
  /* 此芯片支持4个bank */
  FMC_SDRAMInitStructure.FMC_InternalBankNumber = FMC_InternalBank_Number_4;
  /* 设置CAS的延迟是3 */
  FMC_SDRAMInitStructure.FMC_CASLatency = SDRAM_CAS_LATENCY; 
  FMC_SDRAMInitStructure.FMC_WriteProtection = FMC_Write_Protection_Disable;
  /* 配置SDCLK的时钟频率 */
  FMC_SDRAMInitStructure.FMC_SDClockPeriod = SDCLOCK_PERIOD;  
  /* 禁止读取时的突发模式 */
  FMC_SDRAMInitStructure.FMC_ReadBurst = SDRAM_READBURST;
  /* 设置ReadPipe时的延迟  */
  FMC_SDRAMInitStructure.FMC_ReadPipeDelay = FMC_ReadPipe_Delay_1;
  FMC_SDRAMInitStructure.FMC_SDRAMTimingStruct = &FMC_SDRAMTimingInitStructure;
第四步:SDRAM初始化

1.  使能时钟
  FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_CLK_Enabled;  
  FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;
  FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;  FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;
  /* Wait until the SDRAM controller is ready */   

  while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)
  {  }
  /* Send the command */  

  FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure); 
2.  插入100ms的延迟

  __Delay(10);
3.  预充电配置  
  /* Configure a PALL (precharge all) command */   
  FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_PALL;
  FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;  
  FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;
  FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;  
  /* Wait until the SDRAM controller is ready */ 
  while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)  
  {
  }  
  /* Send the command */
  FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
4. 自动刷新配置
  /* Configure a Auto-Refresh command */   
  FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_AutoRefresh;
  FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;  
  FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 4;
  FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = 0;  
  /* Wait until the SDRAM controller is ready */ 
  while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)  
  {
  }  
  /* Send the  first command */
  FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);  
  /* Wait until the SDRAM controller is ready */   
  while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)
  {  }
  /* Send the second command */  
  FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
5. 配置外部模式寄存器  
  /* Program the external memory mode register */
  tmpr = (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;  
  /* Configure a load Mode register command*/   
  FMC_SDRAMCommandStructure.FMC_CommandMode = FMC_Command_Mode_LoadMode;
  FMC_SDRAMCommandStructure.FMC_CommandTarget = FMC_Command_Target_bank2;  
  FMC_SDRAMCommandStructure.FMC_AutoRefreshNumber = 1;
  FMC_SDRAMCommandStructure.FMC_ModeRegisterDefinition = tmpr;  
  /* Wait until the SDRAM controller is ready */ 
  while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)  
  {
  }  
  /* Send the command */
  FMC_SDRAMCmdConfig(&FMC_SDRAMCommandStructure);
6. 刷新频率设置
  /* Set the refresh rate counter */  
  /* (7.81 us x Freq) - 20 */
  /* Set the device refresh counter */  
  FMC_SetRefreshCount(683);
  /* Wait until the SDRAM controller is ready */   
  while(FMC_GetFlagStatus(FMC_Bank2_SDRAM, FMC_FLAG_Busy) != RESET)
  {  }
关于刷新频率的的数值是这么得到的,这里得详细的说一下,目前公认的标准是,存储体中电容的数据有效保存期上限是
64ms(毫秒,1/1000秒),也就是说每一行刷新的循环周期是64ms。这样刷新速度就是:行数量/64ms。我们在看内
存规格时,经常会看到4096 Refresh Cycles/64ms或8192 RefreshCycles/64ms的标识,这里的4096与8192就代表这
个芯片中每个L-Bank的行数。刷新命令一次对一行有效,发送间隔也是随总行数而变化,4096行时为15.625μs(微秒,
1/1000毫秒),8192行时就为7.8125μs。        
       SDRAM的手册上说是4096 refresh cycles every 64 ms,这里就应该是15.525us,而注释上面是按8192行计算
,郁闷,有时间得研究研究。


到这里基本就设置完了,剩下就可以像使用SRAM一样,使用SDRAM了。


开发板上面用的SDRAM数据手册
 42-45S16400J.pdf (1136 K) 下载次数:35 


这篇关于【STM32F429-DISCOVERY学习笔记】STM32F429驱动SDRAM(IS42S16400J)详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Python标准库之数据压缩和存档的应用详解

《Python标准库之数据压缩和存档的应用详解》在数据处理与存储领域,压缩和存档是提升效率的关键技术,Python标准库提供了一套完整的工具链,下面小编就来和大家简单介绍一下吧... 目录一、核心模块架构与设计哲学二、关键模块深度解析1.tarfile:专业级归档工具2.zipfile:跨平台归档首选3.

idea的终端(Terminal)cmd的命令换成linux的命令详解

《idea的终端(Terminal)cmd的命令换成linux的命令详解》本文介绍IDEA配置Git的步骤:安装Git、修改终端设置并重启IDEA,强调顺序,作为个人经验分享,希望提供参考并支持脚本之... 目录一编程、设置前二、前置条件三、android设置四、设置后总结一、php设置前二、前置条件

python中列表应用和扩展性实用详解

《python中列表应用和扩展性实用详解》文章介绍了Python列表的核心特性:有序数据集合,用[]定义,元素类型可不同,支持迭代、循环、切片,可执行增删改查、排序、推导式及嵌套操作,是常用的数据处理... 目录1、列表定义2、格式3、列表是可迭代对象4、列表的常见操作总结1、列表定义是处理一组有序项目的

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

SQL Server 中的 WITH (NOLOCK) 示例详解

《SQLServer中的WITH(NOLOCK)示例详解》SQLServer中的WITH(NOLOCK)是一种表提示,等同于READUNCOMMITTED隔离级别,允许查询在不获取共享锁的情... 目录SQL Server 中的 WITH (NOLOCK) 详解一、WITH (NOLOCK) 的本质二、工作

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (

Java Thread中join方法使用举例详解

《JavaThread中join方法使用举例详解》JavaThread中join()方法主要是让调用改方法的thread完成run方法里面的东西后,在执行join()方法后面的代码,这篇文章主要介绍... 目录前言1.join()方法的定义和作用2.join()方法的三个重载版本3.join()方法的工作原

Spring AI使用tool Calling和MCP的示例详解

《SpringAI使用toolCalling和MCP的示例详解》SpringAI1.0.0.M6引入ToolCalling与MCP协议,提升AI与工具交互的扩展性与标准化,支持信息检索、行动执行等... 目录深入探索 Spring AI聊天接口示例Function CallingMCPSTDIOSSE结束语

C语言进阶(预处理命令详解)

《C语言进阶(预处理命令详解)》文章讲解了宏定义规范、头文件包含方式及条件编译应用,强调带参宏需加括号避免计算错误,头文件应声明函数原型以便主函数调用,条件编译通过宏定义控制代码编译,适用于测试与模块... 目录1.宏定义1.1不带参宏1.2带参宏2.头文件的包含2.1头文件中的内容2.2工程结构3.条件编