当前位置:   article > 正文

STM32H7x3 FMC拓展外部SDRAM全总结_stm32h7 sdram

stm32h7 sdram

STM32H743 FMC拓展外部SDRAM全总结

一、SDRAM通用知识点总结

  SDRAM:Synchronous Dynamic Random Access Memory,同步动态随机存储器。同步是指其时钟频率和CPU前端总线的系统时钟相同,并且内部命令的发送与数据的传输都以它为基准;动态是指存储阵列需要不断的刷新来保证数据不丢失;随机是指数据不是线性依次存储,而是自由指定地址进行数据的读写。

1.1 SDRAM引脚定义

引脚名称功能描述
A 0 − A 12 A0-A12 A0A12地址线可作为行地址和列地址线,
行地址:A0-A12
列地址:A0-A8
A10在预充电阶段也会被采样,其值决定是否所有的banks都进行预充电;也可以通过BS0,BS1信号线选择banks
B S 0 − B S 1 BS0-BS1 BS0BS1Bank选择在行地址锁存时或在读写时地址锁存了选择对应的Bank
D Q 0 − D Q 15 DQ0-DQ15 DQ0DQ15数据线用于数据输入输出的多路数据线
C S ‾ \overline{CS} CS片选使能或失能命令解码器
R A S ‾ \overline{RAS} RAS行地址选通信号有效时,地址线表示的为行地址
C A S ‾ \overline{CAS} CAS列地址选通信号有效时,地址线表示的为列地址
W E ‾ \overline{WE} WE写使能数据写入时能
L D Q M ‾ , U D Q M ‾ \overline{LDQM},\overline{UDQM} LDQM,UDQM输入/输出掩码表示数据线的有效部分
C K E CKE CKE时钟输入时钟失能信号,失能时SDRAM会开启自刷新
C L K CLK CLK时钟使能同步时钟信号,所有的信号都在CLK的上升沿时采样

1.2 SDRAM结构与控制

1.存储阵列

  SDRAM的存储结构为存储阵列,而非管道式存储,如上图所示,存储阵列类似为一张表格,通过指定行列地址就可以定位到最基本的存储单元,一个存储单元的大小通常就为数据通道数。这也是SDRAM可以实现随机访问的原因。一个存储阵列称之为一个逻辑Bank,为了在保证性能的前提下提升存储空间,一般整片SDRAM又会以若干个逻辑Banks组成,通常为2个或4个。单一Bank在访问时可能会造成非常严重的寻址冲突。
  寻址时,只需要指定对应的Bank,然后确定其行地址(Row address),再确定列地址(Cloumn address),就可以对应到具体的存储单元。
  因此,整个SDRAM的存储空间就算为:Size = n × R × C × D,n为Bank数,R和C分别为存储阵列行列数,D为存储单元的容量。

2.寻址过程

  整个SDRAM结构如图一所示,地址线一共有A0~A12,BS0~BS1。通讯时,RAS有效时,行地址选通器被选通,地址线A0~A12表示的地址数据会送入到行地址译码锁存器中译码锁存,作为寻址的行地址,同时BS0~BS1用于锁定对应的Bank;然后释放RAS,CAS有效时,列地址选通器被选通,A0~A9表征的列地址数据被送入列地址译码器中作为寻址的列地址,确定到对应的存储单元,完成整个寻址过程。

3.数据访问

  图一所示SDRAM的数据宽度为16bit,其存储单元容量亦为2Bytes = 16bits。当寻址完成后,可根据DQ0~DQ15数据传输数据。写数据时,经过响应时间后,DQ数据线上的数据被采集暂存到DQ Buffer中,然后通过数据控制电路写入到对应的地址存储单元;同理,读数据即为逆过程。在读写数据时,如果不需要一次性操作16bits有效数据时,可以直接通过UDQM,LDQM选择数据的有效部分,实现字节访问。

4.控制命令
  • 命令禁止
      当器件未被选中时,此时控制器无法与SDRAM进行数据传输与通讯,防止设备执行新命令,即为命令禁止状态。即拉高CS引脚,但是不能通知SDRAM正在执行的命令。

  • 无操作
      无操作命令(No-operation),也称为空命令、NOP命令。不论 SDRAM 处于何种状态,此命令均可被写 入,该命令给被选中的 SDRAM 芯片传递一个空操作信息,目的是为了防止 SDRAM 处于空闲或等待状态时,其他命令被写入,已在进行的操作不受影响。命令禁止的反操作。

  • 激活
      ACTIVE命令用于激活特定Bank中的行,以便后续访问。BA0~BA1的值选择Bank,A0~A12的值选择行。该行对于访问保持活跃状态,直到向该Bank发出预充电命令。在打开同一Bank中的不同行之前,必须发出PRECHARGE命令。

  • 读命令
      READ命令用于对激活的行进行突发读访问。通过BA0~BA1选择对应的Bank,地址线选择起始读列地址。A10确定是否使能自动预充电,使能后将在读脉冲串结束后对访问行进行预充电;失能后读访问的行将会保留打开状态供后续访问。
      读取的数据通过DQ数据线传输,根据字节选择DQM信号选择数据访问数据的有效部分,当DQM均无效时(均为高),两个时钟后所有DQ信号将处于高阻状态;根据高低字节DQM有效信号,DQ输出对应部分的有效信号。
  • 写命令
      WRITE命令用于对激活的行进行突发写访问。通过BA0~BA1选择对应的Bank,地址线选择起始读列地址。A10确定是否使能自动预充电,使能后将在写脉冲串结束后对访问行进行预充电;失能后写访问的行将会保留打开状态供后续访问。
      DQ数据信号线上的数据通过DQM信号线确定有效部分后将有效字节的数据写道存储阵列;如果DQM信号线均为无效(均为高电平),DQ信号线上的数据将被忽略,并不对该地址进行数据写入。
  • 预充电
      PRECHARGE命令用于关闭已选中Bank中选中的行或所有Bank中选中的行。在发出PRECHARGE命令经过指定的时间(tRP)后,Bank可用于后续访问。即SDRAM寻址具有独占性,在激活命令后,如果需要同一Bank的其他行进行寻址访问时,需要关闭原来选中的行,重新发送新要访问的地址。Bank关闭当前工作行,准备打开新行的操作就是预充电。Bank预充电后处于空闲状态,需要访问该Bank时必须先激活。
      预充电时Bank的选择由A10地址线、BS0~BS1确定。当A10有效时,将对所有Bank进行预充电,此时忽略BS信号;A10无效时,通过BS0~BS1信号线选择要预充电的Bank。

1.3 SDRAM特性

1. 刷新(Refresh)

  由于SDRAM作为动态存储器,数据存储原理是通过电容电荷的多与少来对数据进行保存,而电容会放电,因此在某个时间内需要对电容进行不断的充电来保证数据的准确性,此过程为刷新操作。区别于预充电,刷新具有固定的周期,依次对所有行进行操作。

  • 自动刷新(Auto Refresh):当SDRAM芯片与MCU进行时钟同步后,由MCU控制器负责对SDRAM进行刷新操作
  • 自刷新(Self Refresh):当芯片进入低功耗模式(空闲模式),为保证数据芯片自身也可以进行刷新操作
2.模式寄存器

  模式寄存器用于定义SDRAM的特定操作模式。该定义包括选择突发长度、突发类型、CAS延迟、操作模式和写入突发模式,如模式寄存器定义所示。模式寄存器通过LOAD MODE REGISTER(加载模式寄存器)命令进行编程,并保留存储的信息,直到再次编程或设备断电。

在这里插入图片描述
  模式寄存器位 M0:M2 指定突发长度,M3 指定突发类型(顺序或交错),M4:M6 指定CAS延迟,M7:M8 指定操作模式,M9 指定写入突发模式,M10:M11 保留供将来使用。当所有Banks空闲时,必须加载模式寄存器,并且控制器必须在启动后续操作之前等待指定的时间。违反这些要求之一将导致未指定的操作。

  • 突发及突发长度
      突发(Burst)是指对在同一行中相邻的存储单元连续进行数据传输,连续传输存储单元的数量就是突发长度。硬件上通过地址线读写寄存器内容。
      上文讲到的读/写操作,都是一次对一个存储单元进行寻址,如果要连续读/写就还要对当前存储单元的下一个单元进行寻址,也就是要不断的发送列地址与读写命令。虽然读/写延迟相同可以让数据的传输在I/O端是连续的,但它占用了大量的内存控制资源,在数据进行连续传输时无法输入新的命令,效率很低。为此,人们开发了突发传输技术,只有指定起始地址与突发长度,内存就会依次地自动对后面相应数量的存储单元进行读/写操作而不再需要控制器连续地提供列地址。这样,除了第一笔数据的传输需要若干个周期外,其后每个数据只需一个周期的即可获得。

  • 突发类型
      给定突发内的访问可以被编程为顺序的或交错的;这被称为突发类型,并通过位 M3 选择。

  • CAS Latency
      列地址选通延迟,CAS延迟是读命令发出与数据线输出数据可用时之间的延迟,以时钟周期为单位。延迟可以设置为两个或三个时钟周期。下图分别为华邦和芯城SDRAM手册上的CAS延时示意。
    在这里插入图片描述
      如果在第n个时钟边沿发出读命令,并且CAS延迟为m个时钟,则数据将在n+m时钟边沿时可用。DQ在第n+m-1时钟边沿驱动输出,如果满足相关访问时间,则数据在第n+m个时钟边沿有效。

3.初始化时序与加载模式寄存器

在这里插入图片描述
  SDRAM作为动态器件上电后不能像SRAM一般可以直接进行读写,还需要对SDRAM器件进行一定的初始化,以配置SDRAM的上述特性参数。上图为芯城和镁光公司SDRAM器件手册上的初始化时序图,从上电开始共有5个阶段。

  1. 对VDD和VDQ同时上电,待电源稳定后使能时钟输出,此过程至少100us
  2. 发送一个或多个COMMAND INHIBITNOP命令
  3. 发送PRECHARGE ALL命令,对所有Banks进行预充电后。至少等待TRP时间后,所有Banks将完成预充电,然后设备处于空闲状态
  4. 发出至少两个AUTO REFRESH命令并等待至少等待TRC时间
  5. 发送MODE REGISTER SET命令,根据实际应用配置寄存器对应字段。由于模式寄存器将在未知状态下上电,因此在发送其他操作命令之前,应加载模式寄存器。然后等待至少TMRD时间

二、STM32H7系列时钟命名约定

总线接口时钟:总线时钟与外设相连的总线接口时钟,可为APB,AHB,AXI时钟。

内核时钟:外设处理接口功能的专用时钟。可精确生成特定的主时钟频率,可更改总线时钟。

在这里插入图片描述
  待补充完整。

三、FMC外设

  可驱动SDRAM、SRAM、NAND/NOR Flash,FMC主要作为内部AXI总线桥数据与FMC外部连接器件的协议转换器,可以将AXI总线传递过来的信息(数据或命令)转换协议后送入外部器件。所有外部存储器共享地址、数据和控制信号,但有各自的片选信号。 FMC 一次只能访问一个外部器件。

3.1 FMC结构与SDRAM控制器

1. FMC内部时钟与数据接口

  AHB接口:内部CPU通过AHB总线配置FMC寄存器,而AHB时钟(fmc_hclk)是访问FMC寄存器的参考时钟。
  AXI接口:CPU或其他AXI总线主设备(如DMA)可通过AXI从设备接口访问外部存储器。AXI 事务会转换为外部器件协议。由于 AXI 数据总线为 64 位宽,因此 AXI 事务可根据数据大小访问拆分成几个连续的 32 位、 16 位或 8 位访问。

  CPU通过AHB总线根据总线接口时钟hclk访问配置FMC的寄存器,而FMC外设的独立运行由内核时钟ker_ck提供,AXI主设备通过AXI总线桥对FMC外部存储器的读写时序由FMC控制器进行协调。

2. FMC存储控制器

  针对于不同类型的存储设备,有不同的协议和时序要求,因此针对每一种类型的存储设备FMC都设计了专有的存储控制器。SDRAM区别于其他类型的控制器,为同步通信,需要控制器向存储设备提供时间参考以同步其他信号的采集和匹配;而SRAM,Nor FLash和NAND Flash均为异步通讯,其中两种Flash最大的区别是,Nand Flash依然采用并口通讯,但是是复用的地址数据线。
  针对于不同类型控制器,各自的协议不同,因此都有各自的信号线,如独立的片选信号,而针对于数据和地址线,则是通用的IO,其中,FMC一共有26位地址线 FMC_A[25:0],32位数据线 FMC_D[31:0]

3.2 FMC时钟解析

  根据上图所示,FMC外设的内核时钟源可来自于RCC的hclk3,PLL1的Q通道,PLL2的R通道或者per_ck

在这里插入图片描述

fmc_hclk:FMC总线接口时钟(pclk,hclk)
fmc_ker_ck:FMC内核时钟

  根据上面的时钟作用分析,配置FMC外设及其功能时,对于总线内核时钟配置如上图所示,系统时钟来源于PLL1的P1时钟,为480MHz,PLL1的时钟源自HSE,为25MHz。而FMC内核时钟为240MHz,源自HCLK3,HCLK3同样源自系统时钟sys_ck二分频。根据上述时钟配置,可以看到AXI外设总线接口时钟为240MHz,且FMC外设挂载在AXI总线上。

3.3 FMC地址映射

  通过阅读芯片参考手册,可以知道整个FMC的内存可拓展外置地址空间范围为1GB,而FMC将其划分为4个等大的存储区,分别为256MB,其中存储区1作为NOR/SRAM的拓展区域,存储区2作为SDRAM可选的地址映射范围,存储区3作为NAND Flash存储器的拓展区域,4作为保留区域FMC并未使用。而SDRAM的默认地址映射区域不在FMC的地址映射范围内。
  通过查阅Cortex-M7内核通用用户指南Memory Model章节的内核地址映射,可以更完整的了解整个Cortex-M7内核的内存分配。见下图,内核为代码区分配了0.5GB的地址空间,为片内SRAM也预留了0.5GB的内存区,为各种其他外设的寄存器预留了0.5GB的地址空间,而预留的外部RAM空间为1GB,在STM32H7系列中此处整个空间作为FMC的外存设备映射空间。但是把SDRAM的默认映射空间放在了外部设备映射地址范围的末端。其中,外部RAM区域为数据可执行区域,意味着此空间内的可存储执行代码。而外部设备存储区域则不支持此功能。

  1. External RAM

    • 可以保存运行程序,支持XIP功能
    • 数据可执行
  2. FMC存储区域地址映射修改
      由于SRAM的存储区域默认在外部设备区域,不支持XIP。为解决此问题,可通过 FMC_BCR1 寄存器中的 BMAP[1:0] 位修改FMC 存储区域映射。可交换 NOR/PSRAM 存储区域与 SDRAM 存储区域,或者重映射 SDRAM 存储区域 2 的配置,从而允许在两个不同的地址映射中访问 SDRAM 存储区域。

      根据上图所示,SDRAM1存储区域在默认映射下有两个地址,分别为0xC00000000x70000000。即同时支持上述地址的访问写入,在MCU内部的地址处理部分虽然为两个地址,实际对于外部SDRAM操作的均是内部的存储单元,其中SDRAM的实际片内访问地址为偏移地址,上述映射地址为访问基地址。
      在HAL库中,初始化SDRAM完成后,直接调用HAL_SetFMCMemorySwappingConfig()函数即可配置FMC的存储器地址映射。

XIP:Execute in place,即芯片内执行、就地执行,是指CPU直接从存储器中读取程序代码执行,而不用再读到内存中。应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。

  所谓片内执行,CPU可以直接从Nor Flash中取指令,供译码器和执行器使用。为实现该功能,Flash存储器需支持:

  • 需提供与RAM相似的访问接口
  • 接口能够满足足够快的读取速度,且支持随机访问
  • 程序不能修改已加载影像中的数据
  • 程序链接时需知道存储器的地址或地址与位置无关

四、FMC拓展SDRAM硬件设计

4.1 原理图

在这里插入图片描述
  上述为野火基于STM32H743XIB6 Pro核心开发板的SDRAM拓展部分原理图,其中SDRAM选型为华邦(Winbond)的W9825GKH-6,其内存规格为4M×4Banks×16 Bits = 32 Bytes,最大时钟频率支持166MHz。引脚分布如下图所示,一共有13位地址线A0~A12,其中行地址线13位,列地址线9位,数据通道宽度为16位DQ0~DQ15,并支持高低位字节选通读写。
  原理图中使用两片SDRAM芯片,共用一组地址线和时钟线,单片连接不同的数据线,共同组成32bit的外部SDRAM存储组合,其大小为2Pcs×4M×4Banks×16 Bits = 64 Bytes

4.2 引脚分配

  根据实际硬件设计和芯片手册数据,SDRAM与MCU的引脚连接复用对应如下:

	/** FMC GPIO Configuration
	|-------------------SDRAM_DQ0~31-------------------|
	PD14   ------> FMC_D0	|	PH8   ------> FMC_D16
	PD15   ------> FMC_D1	|	PH9   ------> FMC_D17
	PD0   ------> FMC_D2 	|	PH10   ------> FMC_D18
	PD1   ------> FMC_D3 	|	PH11   ------> FMC_D19
	PE7   ------> FMC_D4 	|	PH12   ------> FMC_D20
	PE8   ------> FMC_D5 	|	PH13   ------> FMC_D21
	PE9   ------> FMC_D6 	|	PH14   ------> FMC_D22
	PE10   ------> FMC_D7	|	PH15   ------> FMC_D23
	PE11   ------> FMC_D8   |	PI0   ------> FMC_D24
	PE12   ------> FMC_D9   |	PI1   ------> FMC_D25
	PE13   ------> FMC_D10  |	PI2   ------> FMC_D26
	PE14   ------> FMC_D11  |	PI3   ------> FMC_D27
	PE15   ------> FMC_D12  |	PI6   ------> FMC_D28
	PD8   ------> FMC_D13   |	PI7   ------> FMC_D29
	PD9   ------> FMC_D14   |	PI9   ------> FMC_D30
	PD10   ------> FMC_D15	|	PI10   ------> FMC_D31

	|--------------------SDRAM_A0~12--------------------|								
	PF0   ------> FMC_A0	|	PF13   ------> FMC_A7
	PF1   ------> FMC_A1	|	PF14   ------> FMC_A8
	PF2   ------> FMC_A2	|	PF15   ------> FMC_A9
	PF3   ------> FMC_A3	|	PG0   ------> FMC_A10
	PF4   ------> FMC_A4	|	PG1   ------> FMC_A11
	PF5   ------> FMC_A5	|	PG2   ------> FMC_A12
	PF12   ------> FMC_A6

	|--------------------SDRAM_Control------------------|
	PG4   ------> FMC_BA0	|	PG15   ------> FMC_SDNCAS
	PG5   ------> FMC_BA1	|	PF11   ------> FMC_SDNRAS
							|	PC0   ------> FMC_SDNWE
	PE0   ------> FMC_NBL0	|	PH6   ------> FMC_SDNE1
	PE1   ------> FMC_NBL1	|	PG8   ------> FMC_SDCLK
	PI4   ------> FMC_NBL2	|	PH7   ------> FMC_SDCKE1
	PI5   ------> FMC_NBL3
	**/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

  其中一共使用了32根数据线,13根地址线,12根控制线,分别为Bank选择、高低位4字节选择、行地址选通、列地址选通、写使能、片选、同步时钟与时钟使能信号。一共占用MCU 47个IO资源,因此并口通讯的速度高,但是硬件资源占用也很多。

五、工程配置与测试

5.1 引脚复用配置

  根据数据手册的引脚定义,配置相关引脚状态为复用功能推挽输出模式GPIO_MODE_AF_PP,无需上下拉,输出速度为最高等级,复用功能选择GPIO_AF12_FMC

GPIO_InitStruct.Pin = GPIO_PIN_6;//复用引脚号
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

5.2 FMC初始化

  HAL通过定义SDRAM_HandleTypeDef结构体对FMC所有寄存器的字段进行位定义,根据所需配置填充后通过HAL_SDRAM_Init()函数将填充的字段写入对应寄存器。SDRAM_HandleTypeDef->Instance成员就是选择FMC的Bank5_6作为SDRAM控制器,实质就是将指针指向SDRAM相关寄存器基地址,方便寄存器参数配置与初始化,让代码可读性大大提高。SDRAM_HandleTypeDef->Init成员结构体对FMC的SDRAM控制寄存器字段进行位定义,通过填充其内容配置寄存器。
  FMC在作为SDRAM控制器时,需要配置FMC有关SDRAM的控制寄存器、时序寄存器,然后初始化SDRAM器件后就可以读写数据。

1. 配置控制寄存器(FMC_SDCR2)

  FMC控制寄存器主要配置FMC与外部实际SDRAM器件通讯硬件上的基本参数,主要为列地址/行地址位数、存储器数据总线宽度、存储器内部的Bank数量、存储器CAS、FMC写保护使能、SDRAM时钟配置、突发读使能以及存储器读管道延时。

SDRAM_HandleTypeDef hsdram2;//定义控制寄存器参数配置结构体

hsdram2.Instance = FMC_SDRAM_DEVICE;//指向FMC的SDRAM寄存器基地址
/*选择FMC的SDRAM存储区2(即SDRAM的Bank2),实质就是将地址指向SDRAM2的寄存器地址*/
hsdram2.Init.SDBank = FMC_SDRAM_BANK2;
hsdram2.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
hsdram2.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
hsdram2.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32;
hsdram2.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram2.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
hsdram2.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram2.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
hsdram2.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
hsdram2.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_2;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意上述的SDClockPeriod选择为FMC_SDRAM_CLOCK_PERIOD_2,此处定义了两个SDRAM存储区域的SDRAM时钟周期为两个fmc_ker_ck周期,即SDRAM运行频率为120MHz。

  根据实际器件选型和硬件连接,选择SDRAM存储区域2作为SDRAM的拓展区域,行列地址线分别为13/9位,单片内部包括4个Banks,CAS延迟为3,失能写保护,SDRAM时钟2分频,使能突发读,读管道延时为2个时钟周期。

2. 配置时序寄存器(FMC_SDTR2)

  FMCSD时序寄存器配置FMC与SDRAM通讯的时序,由于不同厂商生产的器件时序存在差异,可灵活配置,以兼容不同型号器件的通讯时序要求。主要配置器件的加载模式寄存器到激活时间TMRD、退出自刷新延时TXSR、自刷新时间TRAS、行循环延时TRC、恢复延时TWR、行预充电延时TRCD。
  HAL库根据SDRAM时序寄存器FMC_SDTR2[31:0]定义了FMC_SDRAM_TimingTypeDef时序初始化结构体,将寄存器根据不同内容字段按结构体形式列出,将寄存器配置项参数通过宏定义进行声明定义,配置寄存器时,只需要定义对应的初始化结构体,然后根据宏定义,可以填鸭式的填充定义好的寄存器字段,填充完毕后,调用HAL_SDRAM_Init()将填充内容写入寄存器。

/**
  * @简要: FMC SDRAM 时序参数结构体定义
  */
typedef struct
{
	uint32_t LoadToActiveDelay;//加载模式寄存器到激活TMRD
	/*定义加载模式寄存器命令与激活或刷新命令之间的延迟,按存储器时钟周期数计。
	此参数应该介于Min_Data=1和Max_Data=16之间  */

	uint32_t ExitSelfRefreshDelay;//退出自刷新延迟TXSR[3:0]
    /*定义从发出自刷新命令到发出激活命令之间的延迟,按存储器时钟周期数计。
    此参数应该介于Min_Data=1和Max_Data=16之间  */

	uint32_t SelfRefreshTime;//自刷新时间TRAS[3:0]
    /*定义最短的自刷新周期,按存储器时钟周期数计。
	此参数应该介于Min_Data=1和Max_Data=16之间  */

    uint32_t RowCycleDelay;//行循环延迟TRC[3:0]
    /*定义刷新命令和激活命令之间的延迟,以及两个相邻刷新命令之间的延迟,以存储器时钟周期数表示。仅在	FMC_SDTR1寄存器中配置TRC时序。如果使用了两个SDRAM设备,则必须使用最慢设备的时序配置TRC。
    此参数应该介于Min_Data=1和Max_Data=16之间  */

	uint32_t WriteRecoveryTime;//恢复延迟TWR[3:0]
    /*定义写命令和预充电命令之间的延迟,按存储器时钟周期数计。
    此参数应该介于Min_Data=1和Max_Data=16之间  */

	uint32_t RPDelay;//行预充电延迟TRP[3:0]
    /*定义预充电命令与其它命令之间的延迟,按存储器时钟周期数计。仅在FMC_SDTR1寄存器中配置TRP时序。		如果使用了两个SDRAM设备,则必须使用最慢设备的时序配置TRP。
      此参数应该介于Min_Data=1和Max_Data=16之间  */

	uint32_t RCDDelay;//行到列延迟TRCD[3:0]
    /*定义激活命令与读/写命令之间的延迟,按存储器时钟周期数计。
    此参数应该介于Min_Data=1和Max_Data=16之间  */
} FMC_SDRAM_TimingTypeDef;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

  根据芯片参考手册,可知STM32H743支持同时扩展两片SDRAM,由于SDRAM两个存储区域可独立配置,每个存储区都有自己的时序寄存器FMC_SDTR1,FMC_SDTR2,但是行预充电延迟*TRP[3:0]和行循环延迟TRC[3:0]*共用一个寄存器FMC_SDTR1。假设我们硬件上在SDRAM存储区2扩展RAM,但是上述两个参数依然是在SDRAM存储区1寄存器配置的。此部分已经由HAL库内部初始化函数区分定义好。对于我们配置使用,并无影响。
  查询器件数据手册后,得到对应的参数为:

  • CAS Latency: 2 and 3
  • 8K Refresh Cycles/64 mS
  • tRC = 60 ns
  • tRAS = 42 ns
  • tRCD = 15 ns
  • tRP = 15 ns
  • tWR = 2 tck
  • tMRD = 2 tck
  • tXSR = 72 ns
  • tck = 6
  • tREF = 64 ms

  根据FMC存储器时钟周期(tck = 1/120M = 8.33ns)进行计算,tMRD 最小延时为两倍的 tckLoadToActiveDelay = 2;tRC 最小延时为60ns,对应具体配置值为RowCycleDelay = 60/8.33 ≈ 8;tXSR 最小值为72ns,ExitSelfRefreshDelay = 72/8.33 ≈ 9;tRC 最小为60ns,RowCycleDelay = 60/8.33 ≈ 8;tRP 最小为15 ns,RPDelay = 15/8.33 ≈ 2;同样tRCD 最小为15 ns,RCDDelay = 2

/* 定义FMC时序初始化结构体 */
FMC_SDRAM_TimingTypeDef SdramTiming = {0};	

SdramTiming.LoadToActiveDelay = 2;
SdramTiming.ExitSelfRefreshDelay = 9;
SdramTiming.SelfRefreshTime = 5;//华邦手册无此项
SdramTiming.RowCycleDelay = 8;
SdramTiming.WriteRecoveryTime = 4;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 2;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.初始化器件

  经过前三个阶段的初始化,分别配置引脚的复用功能,配置FMC的SDRAM控制器,完成通讯上的硬件参数基本配置;然后配置通讯过程中的时序协议,已经可以实现CPU和FMC之间的通讯了;但是,SDRAM作为动态的存储器件,建立通讯后,还必须对SDRAM存储器进行初始化,此初始化有严格的步骤和时间要求。
  SDRAM器件的配置通过命令下发实现,因此有专用的命令模式寄存器FMC_SDCMR,该寄存器包含访问 SDRAM 设备时所发出的命令。该寄存器用于初始化 SDRAM 设备、激活自刷新模式和掉电模式。
  同样地,HAL库根据SDRAM命令模式寄存器 FMC_SDCMR 定义了FMC_SDRAM_CommandTypeDef命令参数结构体,将寄存器根据不同内容字段按结构体形式列出,将寄存器配置项参数通过宏定义进行声明定义,配置寄存器时,只需要定义对应的初始化结构体,然后根据宏定义,填鸭式的填充定义好的寄存器字段,填充完毕后,调用HAL_SDRAM_SendCommand()函数将填充内容写入对应寄存器。

  1. 时钟输出使能:将 MODE 位置为“001”并配置 FMC_SDCMR 寄存器中的目标存储区域位( CTB1 和/或 CTB2)以开始为存储器提供时钟信号(SDCKE 驱动为高电平)
  2. 等待指定延时周期:延时至少100us(具体数据参见芯片数据手册)
  3. 给SDRAM所有Bank预充电:将 MODE 位置为“010”并配置 FMC_SDCMR 寄存器中的目标存储区域位( CTB1 和/或 CTB2)以发送“全部预充电”命令
  4. 配置自刷新及其命令个数:将 MODE 位置为“011”并配置 FMC_SDCMR 寄存器中的目标存储区位( CTB1 和/或CTB2)和连续自动刷新命令 (NRFS) 的数量。请参见 SDRAM 数据手册了解应发出的自动刷新命令个数。通常为 8 个。
  5. 配置SDRAM加载模式寄存器:配置 MRD 字段,将 MODE 位置为“100”并配置 FMC_SDCMR 寄存器中的目标存储区域位( CTB1 和/或 CTB2)以发送“加载模式寄存器”命令并对 SDRAM 设备进行编程。尤其突发长度 (BL) 必须置“1”且必须选择 CAS 延迟。
  6. 设置SDRAM刷新定时计数器:该寄存器通过配置刷新定时器计数值来设置刷新循环之间的刷新速率。刷新速率为64ms/8196行 = 7.81us,COUNT = 7.81us×120MHz-20 = 918。
FMC_SDRAM_CommandTypeDef	SDRAM_INIT_HANDLER;

//step1:时钟配使能
SDRAM_INIT_HANDLER.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
//此处SDRAM数据宽度为32bit,共用时钟线为FMC_SDCLK1,即SDRAM_BANK2的信号线
SDRAM_INIT_HANDLER.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
SDRAM_INIT_HANDLER.AutoRefreshNumber = 1;
SDRAM_INIT_HANDLER.ModeRegisterDefinition = 0x0;
HAL_SDRAM_SendCommand(&hsdram2, &SDRAM_INIT_HANDLER, 0xff);

//step2:延时100us以上
HAL_Delay(1);

//step3:给SDRAM所有Bank预充电
SDRAM_INIT_HANDLER.CommandMode = FMC_SDRAM_CMD_PALL;
SDRAM_INIT_HANDLER.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
SDRAM_INIT_HANDLER.AutoRefreshNumber = 1;
SDRAM_INIT_HANDLER.ModeRegisterDefinition = 0x0;
HAL_SDRAM_SendCommand(&hsdram2, &SDRAM_INIT_HANDLER, 0xff);

//step4:设置SDRAM自动刷新
SDRAM_INIT_HANDLER.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
SDRAM_INIT_HANDLER.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
SDRAM_INIT_HANDLER.AutoRefreshNumber = 8;//定义在自动刷新模式下发出的连续自动刷新命令的数量
SDRAM_INIT_HANDLER.ModeRegisterDefinition = 0x0;
HAL_SDRAM_SendCommand(&hsdram2, &SDRAM_INIT_HANDLER, 0xff);

//step5:设置SDRAM加载模式寄存器
SDRAM_INIT_HANDLER.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
SDRAM_INIT_HANDLER.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
SDRAM_INIT_HANDLER.AutoRefreshNumber = 1;//定义在自动刷新模式下发出的连续自动刷新命令的数量
SDRAM_INIT_HANDLER.ModeRegisterDefinition = 
				(uint32_t)SDRAM_MODEREG_BURST_LENGTH_1|
				SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |
				SDRAM_MODEREG_OPERATING_MODE_STANDARD |
				SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
HAL_SDRAM_SendCommand(&hsdram2, &SDRAM_INIT_HANDLER, 0xff);

//step6:设置SDRAM自动刷新速率
HAL_SDRAM_ProgramRefreshRate(&hsdram2, 918);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

5.3 数据读写

  当上述初始化过程完成以后,可以直接以指针方式对拓展区域的内存进行读写:

void SDRAM_Test(void)
{
    volatile uint32_t data_write_arr = 0xD0000000;
    for(uint32_t i = 0; i < 2*4*4*1024*1024/4; i++)
    {
        *(uint32_t *) data_write_arr = i;
        data_write_arr += 4;
    }
    data_write_arr = 0xD0000000;
    for(uint32_t i = 0; i < 2*4*4*1024*1024/4; i++)
    {
        if(*(uint32_t *) data_write_arr != i)
        {
            printf("数据读写有误\r\n");
            error_handler();
        }
    }
    printf("数据读写测试成功\r\n");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

  上述程序部分,将data_write_arr变量强制转换成32位指针变量,并依次填充数据i,填冲16M个数据,填充完毕后;依次读出来与i对比,如果不匹配,则数据读写不正确,实验失败。

六、驱动支持程序编写

  完整驱动程序编写如下:

/* USER CODE BEGIN Header */

/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "fmc.h"

/* USER CODE BEGIN 0 */
SDRAM_HandleTypeDef hsdram2;
/*
@intro:SDRAM存储器初始化
@param:no param
@retur:no return
*/
void SDRAM_Pre_Config(void)
{
	FMC_SDRAM_CommandTypeDef	SDRAM_INIT_HANDLER;
	
	//step1:时钟配使能
	SDRAM_INIT_HANDLER.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
	//此处SDRAM数据宽度为32bit,共用时钟线为FMC_SDCLK1,即SDRAM_BANK2的信号线
	SDRAM_INIT_HANDLER.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
	SDRAM_INIT_HANDLER.AutoRefreshNumber = 1;
	SDRAM_INIT_HANDLER.ModeRegisterDefinition = 0x0;
	HAL_SDRAM_SendCommand(&hsdram2, &SDRAM_INIT_HANDLER, 0xff);
	
	//step2:延时100us以上
	HAL_Delay(1);
	
	//step3:给SDRAM所有Bank预充电
	SDRAM_INIT_HANDLER.CommandMode = FMC_SDRAM_CMD_PALL;
	SDRAM_INIT_HANDLER.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
	SDRAM_INIT_HANDLER.AutoRefreshNumber = 1;
	SDRAM_INIT_HANDLER.ModeRegisterDefinition = 0x0;
	HAL_SDRAM_SendCommand(&hsdram2, &SDRAM_INIT_HANDLER, 0xff);
	
	//step4:设置SDRAM自动刷新
	SDRAM_INIT_HANDLER.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
	SDRAM_INIT_HANDLER.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
	SDRAM_INIT_HANDLER.AutoRefreshNumber = 8;
	SDRAM_INIT_HANDLER.ModeRegisterDefinition = 0x0;
	HAL_SDRAM_SendCommand(&hsdram2, &SDRAM_INIT_HANDLER, 0xff);
	
	//step5:设置SDRAM加载模式寄存器
	SDRAM_INIT_HANDLER.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
	SDRAM_INIT_HANDLER.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
	SDRAM_INIT_HANDLER.AutoRefreshNumber = 1;
	SDRAM_INIT_HANDLER.ModeRegisterDefinition = 
        (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1	|
            SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL	|
        	SDRAM_MODEREG_CAS_LATENCY_3			|
        	SDRAM_MODEREG_OPERATING_MODE_STANDARD|
           SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
	HAL_SDRAM_SendCommand(&hsdram2, &SDRAM_INIT_HANDLER, 0xff);
	
	//step6:设置SDRAM自动刷新速率
	HAL_SDRAM_ProgramRefreshRate(&hsdram2, 918);
}

uint8_t BSP_FMC_Sdram_Word_Write(uint32_t * write_data_buffer, uint32_t arr_write, uint32_t write_count)
{
	__IO uint32_t *arr = (uint32_t *)arr_write;
	if((*arr + EXT_SDRAM_START_ARR) > EXT_SDRAM_END_ARR || (arr_write < EXT_SDRAM_START_ARR))
	{
		return	0;
	}else{
		while(HAL_SDRAM_GetState(&hsdram2) != HAL_SDRAM_STATE_READY);
		for(; write_count > 0; write_count--)
		{
			*arr = *write_data_buffer++;
			arr += 4;
		
		}
	}
	return 1;
}

void BSP_FMC_Sdram_Word_Read(uint32_t * read_data_buffer, uint32_t arr_read, uint32_t read_count)
{
	__IO uint32_t *arr = (uint32_t *)arr_read;
	while(HAL_SDRAM_GetState(&hsdram2) != HAL_SDRAM_STATE_READY);
	for(; read_count > 0; read_count--)
	{
		*read_data_buffer++ = *arr;
		arr += 4;
	}

}
/* USER CODE END 0 */



/* 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 SDRAM2 memory initialization sequence
	*/
	hsdram2.Instance = FMC_SDRAM_DEVICE;
	/* hsdram2.Init */
	hsdram2.Init.SDBank = FMC_SDRAM_BANK2;
	hsdram2.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
	hsdram2.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
	hsdram2.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32;
	hsdram2.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
	hsdram2.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
	hsdram2.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
	hsdram2.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
	hsdram2.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
	hsdram2.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_2;
	/* SdramTiming */
	SdramTiming.LoadToActiveDelay = 2;
	SdramTiming.ExitSelfRefreshDelay = 9;
	SdramTiming.SelfRefreshTime = 5;
	SdramTiming.RowCycleDelay = 8;
	SdramTiming.WriteRecoveryTime = 4;
	SdramTiming.RPDelay = 2;
	SdramTiming.RCDDelay = 2;

	if (HAL_SDRAM_Init(&hsdram2, &SdramTiming) != RESET)
	{
		Error_Handler( );
	}

	/* USER CODE BEGIN FMC_Init 2 */
	SDRAM_Pre_Config();//初始化SDRAM设备
	/* USER CODE END FMC_Init 2 */
}

static uint32_t FMC_Initialized = 0;

static void HAL_FMC_MspInit(void){
	/* USER CODE BEGIN FMC_MspInit 0 */

	/* USER CODE END FMC_MspInit 0 */
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	if (FMC_Initialized) {
	return;
	}
	FMC_Initialized = 1;

	/* Peripheral clock enable */
	__HAL_RCC_FMC_CLK_ENABLE();

	/* GPIO_InitStruct */
	GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_1
				  |GPIO_PIN_0|GPIO_PIN_7|GPIO_PIN_2|GPIO_PIN_3
				  |GPIO_PIN_9|GPIO_PIN_10;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF12_FMC;

	HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);

	/* GPIO_InitStruct */
	GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_10|GPIO_PIN_9
				  |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_8
				  |GPIO_PIN_13|GPIO_PIN_7|GPIO_PIN_14;
	HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

	/* GPIO_InitStruct */
	GPIO_InitStruct.Pin = GPIO_PIN_15|GPIO_PIN_14|GPIO_PIN_13|GPIO_PIN_10
				  |GPIO_PIN_11|GPIO_PIN_9|GPIO_PIN_12|GPIO_PIN_6
				  |GPIO_PIN_8|GPIO_PIN_7;
	HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);

	/* GPIO_InitStruct */
	GPIO_InitStruct.Pin = GPIO_PIN_15|GPIO_PIN_8|GPIO_PIN_5|GPIO_PIN_4
				  |GPIO_PIN_2|GPIO_PIN_0|GPIO_PIN_1;
	HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

	/* GPIO_InitStruct */
	GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_15|GPIO_PIN_14
				  |GPIO_PIN_10|GPIO_PIN_9|GPIO_PIN_8;
	HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

	/* GPIO_InitStruct */
	GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_3
						  |GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_13|GPIO_PIN_14
						  |GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_11;
	HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

	/* GPIO_InitStruct */
	GPIO_InitStruct.Pin = GPIO_PIN_0;
	HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

	/* USER CODE BEGIN FMC_MspInit 1 */

	/* USER CODE END FMC_MspInit 1 */
}

void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef* sdramHandle){
  /* USER CODE BEGIN SDRAM_MspInit 0 */

  /* USER CODE END SDRAM_MspInit 0 */
  HAL_FMC_MspInit();
  /* USER CODE BEGIN SDRAM_MspInit 1 */

  /* USER CODE END SDRAM_MspInit 1 */
}

static uint32_t FMC_DeInitialized = 0;

static void HAL_FMC_MspDeInit(void){
  /* USER CODE BEGIN FMC_MspDeInit 0 */

  /* USER CODE END FMC_MspDeInit 0 */
  if (FMC_DeInitialized) {
    return;
  }
  FMC_DeInitialized = 1;
  /* Peripheral clock enable */
  __HAL_RCC_FMC_CLK_DISABLE();

  HAL_GPIO_DeInit(GPIOI, GPIO_PIN_6|GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_1
                          |GPIO_PIN_0|GPIO_PIN_7|GPIO_PIN_2|GPIO_PIN_3
                          |GPIO_PIN_9|GPIO_PIN_10);

  HAL_GPIO_DeInit(GPIOE, GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_10|GPIO_PIN_9
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_8
                          |GPIO_PIN_13|GPIO_PIN_7|GPIO_PIN_14);

  HAL_GPIO_DeInit(GPIOH, GPIO_PIN_15|GPIO_PIN_14|GPIO_PIN_13|GPIO_PIN_10
                          |GPIO_PIN_11|GPIO_PIN_9|GPIO_PIN_12|GPIO_PIN_6
                          |GPIO_PIN_8|GPIO_PIN_7);

  HAL_GPIO_DeInit(GPIOG, GPIO_PIN_15|GPIO_PIN_8|GPIO_PIN_5|GPIO_PIN_4
                          |GPIO_PIN_2|GPIO_PIN_0|GPIO_PIN_1);

  HAL_GPIO_DeInit(GPIOD, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_15|GPIO_PIN_14
                          |GPIO_PIN_10|GPIO_PIN_9|GPIO_PIN_8);

  HAL_GPIO_DeInit(GPIOF, GPIO_PIN_2|GPIO_PIN_1|GPIO_PIN_0|GPIO_PIN_3
                          |GPIO_PIN_5|GPIO_PIN_4|GPIO_PIN_13|GPIO_PIN_14
                          |GPIO_PIN_12|GPIO_PIN_15|GPIO_PIN_11);

  HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0);

  /* USER CODE BEGIN FMC_MspDeInit 1 */

  /* USER CODE END FMC_MspDeInit 1 */
}

void HAL_SDRAM_MspDeInit(SDRAM_HandleTypeDef* sdramHandle){
  /* USER CODE BEGIN SDRAM_MspDeInit 0 */

  /* USER CODE END SDRAM_MspDeInit 0 */
  HAL_FMC_MspDeInit();
  /* USER CODE BEGIN SDRAM_MspDeInit 1 */

  /* USER CODE END SDRAM_MspDeInit 1 */
}
/**
  * @}
  */

/**
  * @}
  */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269

  头文件定义:

/* USER CODE BEGIN Header */

/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __FMC_H
#define __FMC_H
#ifdef __cplusplus
 extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */
#define EXT_SDRAM_START_ARR		0xD0000000
#define EXT_SDRAM_TOTAL_SIZE	0x04000000//2*4*4*2*1024*1024 = 0x04000000
#define EXT_SDRAM_END_ARR		0xD4000000
/* USER CODE END Includes */

extern SDRAM_HandleTypeDef hsdram2;

/* USER CODE BEGIN Private defines */
/**
  * @brief  FMC SDRAM 模式配置的寄存器相关定义
  */
#define SDRAM_MODEREG_BURST_LENGTH_1             ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2             ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4             ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8             ((uint16_t)0x0004)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL      ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED     ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_2              ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3              ((uint16_t)0x0030)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD    ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE     ((uint16_t)0x0200)

/* USER CODE END Private defines */

void MX_FMC_Init(void);
void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef* hsdram);
void HAL_SDRAM_MspDeInit(SDRAM_HandleTypeDef* hsdram);

/* USER CODE BEGIN Prototypes */
void SDRAM_Pre_Config(void);
uint8_t BSP_FMC_Sdram_Word_Write(uint32_t * write_data_buffer, uint32_t arr_write, uint32_t write_count);
void BSP_FMC_Sdram_Word_Read(uint32_t * read_data_buffer, uint32_t arr_read, uint32_t read_count);
/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif
#endif /*__FMC_H */

/**
  * @}
  */

/**
  * @}
  */

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

参考资料

[1]: RM0433 STM32H7x3基于ARM内核的32位高级MCU
[2]: DS12110 32-bit Arm® Cortex®-M7 480MHz MCUs, up to 2MB Flash, up to 1MB RAM, 46 com.
[3]: Cortex_M7内核技术参考手册
[4]: MT48LC16M16A2P-6A数据手册
[5]: W9825G6KH-6数据手册
[6]: IS42S32800G数据手册
[7]: STM32 HAL库开发实战指南——基于野火F7与H7系列开发板
[8]: 安富莱STM32-V7 开发板用户手册

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/144439
推荐阅读
相关标签
  

闽ICP备14008679号