【正点原子STM32】FSMC_FMC——SDRAM实验(存储器类型、SDRAM操作命令、SDRAM工作流程(初始化、读和写)、FMC相关寄存器和HAL库驱动、SDRAM模块驱动步骤)

一、SDRAM介绍

二、FMC介绍

三、SDRAM模块驱动步骤
四、编程实战
五、总结

一、SDRAM介绍

在这里插入图片描述
你列出了常见的存储器类型,以下是每种类型的简要说明:

  1. RAM(随机存取存储器):

    • DRAM(动态随机存取存储器):包括SDRAM(同步动态随机存取存储器)和DDR(双倍数据率)等,是一种常见的内存类型,用于存储临时数据。需要定期刷新以保持数据的有效性。
    • SRAM(静态随机存取存储器):速度较快,无需刷新,用于需要快速访问的缓存和存储器。
  2. ROM(只读存储器):

    • PROM(可编程只读存储器):一种只能编程一次的存储器,通常用于存储固件和程序。
    • EEPROM(电可擦可编程只读存储器):可以通过电擦除和编程来重复写入数据,常用于存储小量数据和配置信息。
  3. FLASH(闪存):

    • NOR FLASH:与传统的并行 NOR Flash 存储器相比,具有较快的读取速度和较慢的擦除速度,适用于需要快速随机访问的应用。
    • NAND FLASH:适用于大容量存储,具有较高的写入速度和较慢的读取速度,常用于存储大型文件系统和嵌入式系统中的固件。

这些存储器类型在嵌入式系统和计算机系统中都有不同的应用和特点,根据具体的需求和场景选择合适的存储器类型非常重要。
在这里插入图片描述

1.1、SDRAM简介

在这里插入图片描述
SDRAM(同步动态随机存取存储器 Synchronous Dynamic Random Access Memory)是一种常见的内存类型,具有以下特点:

  1. 同步性:SDRAM内存的工作需要同步时钟。内部的命令发送和数据传输都以同步时钟为基准,这有助于协调内存控制器和其他系统组件之间的操作。

  2. 动态性:与静态RAM(SRAM)不同,SDRAM是一种动态存储器,存储阵列需要定期刷新来保持存储的数据不丢失。这种刷新是在内部自动进行的,无需外部干预。

  3. 随机访问:SDRAM具有随机访问的特性,意味着数据不是按照线性顺序存储的,而是可以通过任意地址进行读写操作,这提供了更灵活的数据访问方式。

  4. 优点

    • 容量大:SDRAM通常具有较大的存储容量,适用于存储大量数据或程序代码。
    • 读写速度快:相比许多其他存储器类型,SDRAM的读写速度较快,能够满足对数据访问速度要求较高的应用。
    • 价格相对便宜:相较于一些高性能存储器,SDRAM的价格相对较低,适合大多数应用的预算。
  5. 缺点

    • 控制逻辑复杂:SDRAM的控制逻辑相对较复杂,需要精确的时序和控制信号,因此在设计和实现SDRAM控制器时需要一定的技术和经验。

总的来说,SDRAM是一种常见的内存类型,在许多嵌入式系统和计算机系统中广泛应用,它提供了高速、大容量的数据存储和访问解决方案。

W9825G6KH 结构框图

在这里插入图片描述
SDRAM(同步动态随机存取存储器)的信号线包括:

  1. CLK:时钟信号,提供给SDRAM的工作时钟,用于同步SDRAM的内部操作。

  2. CKE:时钟使能信号,控制SDRAM的工作模式。在正常工作模式下,CKE为高电平;在时钟失能(自刷新)模式下,CKE为低电平。

  3. CS:片选信号,表示SDRAM是否被选中。CS为低电平时,表示SDRAM被选中,可以进行数据读写操作。

  4. RAS:行地址选通信号,表示行地址已经传送给SDRAM。RAS为低电平时,表示行地址有效。

  5. CAS:列地址选通信号,表示列地址已经传送给SDRAM。CAS为低电平时,表示列地址有效。

  6. WE:写使能信号,用于控制读写操作。当WE为低电平时,表示写操作;当WE为高电平时,表示读操作。

  7. A0~A12:地址线,用于传输行地址和列地址。

  8. BS0/BS1:BANK地址线,用于指示SDRAM的BANK地址。

  9. DQ0~15:数据线,用于传输数据。

  10. L/UDQM:数据掩码,表示数据线的有效部分。

如何定位存储单元?

在这里插入图片描述
在SDRAM中定位存储单元通常需要指定以下参数:

  1. Bank(BANK):SDRAM通常被划分为多个Bank,每个Bank都包含一定数量的存储单元。选择Bank是为了确定要访问的存储单元所在的Bank。

  2. 行地址(Row Address):一旦选择了Bank,就需要指定行地址。行地址用于确定要访问的存储单元所在的行。

  3. 列地址(Column Address):在指定了Bank和行地址之后,需要指定列地址。列地址用于确定要访问的存储单元所在的列。

通过指定Bank、行地址和列地址,可以准确定位到SDRAM中的存储单元。这样的寻址方式可以有效地管理SDRAM的存储空间,使得数据的读写操作能够高效地进行。

SDRAM寻址

在这里插入图片描述
SDRAM的寻址过程,可以总结为以下几个步骤:

  1. 选通行地址(RAS):通过将RAS信号置为低电平,选通行地址。

  2. 传输行地址:将要访问的行地址通过地址线A0~A12传输到行地址译码器,并被锁存。

  3. 确定BANK地址:通过BS0和BS1信号来确定要访问的BANK地址,并将其锁存。

  4. 选通列地址(CAS):通过将CAS信号置为低电平,选通列地址。

  5. 传输列地址:将要访问的列地址通过地址线A0~A12传输到列地址译码器,并被锁存。

通过以上步骤,可以完成对SDRAM中特定存储单元的定位,进而进行数据的读写操作。

SDRAM存储单元

在这里插入图片描述
SDRAM存储单元通过周期性的刷新来维持存储的数据。基本的操作包括读取和写入数据:

  1. 读取数据:当选通行地址和列地址后,存储单元中的电荷通过数据线导通到读取电路,从而可以读取存储单元中的数据。

  2. 写入数据:写入数据时,需要将选通行地址和列地址后,向存储单元施加相应的数据。这会导致存储单元中的电荷发生改变,从而写入新的数据。

为了保持存储单元中的数据,SDRAM需要定期进行刷新操作。在刷新期间,SDRAM会按照一定的规则,重新读取存储单元中的数据,并重新写入以维持电荷的状态,以防止数据丢失。

1.2、SDRAM操作命令介绍

在这里插入图片描述
在这里插入图片描述
NO-Operation(NOP)命令是一种空操作,用于在SDRAM中执行无操作的命令。主要目的是为了在执行其他命令之前,确保SDRAM已经被正确地选中,并且处于准备接收命令的状态。通过发送NOP命令,可以防止SDRAM接收到意外的、不正确的命令,从而确保后续的操作能够正常执行。

Active(激活)命令用于存储单元的寻址,必须在读写操作之前被发送,以设置所需的BANK和行地址。下面是发送Active命令的步骤:

  1. 通过指令线发送行有效命令,其中RAS线为低电平,以通知SDRAM将接收到的地址信息锁定为行地址。
  2. 通过BA线和A线来确定发送的BANK地址和行地址。BA线用于选择BANK地址,A线用于选择行地址。通常,BA线与地址线A01共同决定BANK地址,而地址线A012共同决定行地址。

发送Active命令后,SDRAM将在指定的BANK和行地址处准备好接收后续的读写操作。

在这里插入图片描述
Read / Write 读写操作通常在激活命令之后进行,以下是发送读/写命令的步骤:

  1. 通过指令线发送读/写命令。根据需要进行读或写操作,WE信号控制读(高)/写(低)命令,同时CAS线为低电平。
  2. 通过地址线A线来确定发送的列地址。使用到的地址线通常是A[8:0],它们共同决定了列地址。
  3. 根据需要,设置A10线来控制预充电。A10线用于控制预充电,高电平使能,低电平关闭。
  4. 在完成命令和地址的传输后,进行数据的读写操作。DQM线表示有效的DQ数据线,这些数据线用于传输数据。

通过这些步骤,SDRAM可以进行读写操作,并根据需要在指定的列地址处读取或写入数据。

在这里插入图片描述
Precharge 预充电指令用于关闭当前BANK中打开的行地址,以便进行下一次读写操作时可以重新选择行/列地址。预充电命令可以通过两种方式实现:

  1. 独立命令发送

    • 通过指令线发送预充电命令。
    • 通过A10线来控制操作区域:
      • 当A10为高电平时,所有BANK都预充电。
      • 当A10为低电平时,使用BA线选择要预充电的BANK。
  2. 自动预充电

    • 在发送读/写命令时,通过地址线A10来设置自动预充电。
    • 当A10为高电平时,所有BANK的预充电被启用。
    • 读/写完成后,自动进行预充电操作。

自动预充电的好处是,在下次读写操作之前,不需要再单独发送预充电命令,从而提高了读写速度,提升了性能。

在这里插入图片描述
Refresh 刷新指令用于刷新存储器中的数据,以防止数据丢失或损坏。在SDRAM中,有两种刷新方式,取决于时钟使能(CKE)引脚的状态:

  1. 自动刷新(Auto Refresh)

    • 当CKE引脚为高电平时,启用自动刷新。
    • 自动刷新依赖于刷新计数器(也称为行地址生成器),它会自动递增并生成要刷新的行地址。
    • 刷新周期是完成一次所有行的刷新所需的时间,通常为64ms。
    • 在自动刷新过程中,所有的BANK停止工作,这段时间内不能进行读写操作。
    • 刷新过程中,刷新时间一般为9个时钟周期(CLK),这期间SDRAM内部会自动执行刷新操作。
  2. 自我刷新(Self Refresh)

    • 当CKE引脚为低电平时,SDRAM进入自我刷新模式。
    • 自我刷新主要用于在低功耗状态下保持数据的完整性,通常在设备休眠时使用。
    • 进入自我刷新模式后,SDRAM不再依赖外部时钟,而是根据内部时钟自行进行刷新操作。
    • 当需要退出自我刷新模式并进入正常操作模式时,将CKE引脚置于高电平即可。

自动刷新和自我刷新是SDRAM保持数据有效性的重要机制,在不同的工作场景下起着不同的作用。

在这里插入图片描述
模式寄存器设置(Mode Register Set) 是用于配置SDRAM内部控制单元参数的命令,这些参数直接影响SDRAM的工作模式和性能。主要的模式参数包括:

  1. 突发长度(Burst Length)

    • 突发长度指定了在连续传输中涉及的存储单元数量,即每次访问中传输的数据量。
    • 可以选择的突发长度通常是4、8或者其他值,具体取决于SDRAM的型号和配置。
  2. 突发访问地址模式(Addressing Mode)

    • 突发访问地址模式用于确定在突发访问期间的地址增长模式。
    • 例如,当A3为低电平时,表示顺序访问模式;当A3为高电平时,表示交错访问模式。
  3. 列地址选通延迟(CAS Latency)

    • CAS延迟指定了在读命令发送后,需要等待几个时钟周期后才能从DQ数据线上读取到有效数据。
    • 典型的CAS延迟值为2或3个时钟周期。
  4. 写模式(Write Mode)

    • 写模式用于设置单次写入的方式,可以选择突发写入或单次写入。
    • 在突发写入模式下,可以一次性写入多个连续的存储单元;而在单次写入模式下,每次写入只操作一个存储单元。

通过配置模式寄存器,可以根据实际需求灵活调整SDRAM的工作模式,以最大程度地满足系统性能和功耗的要求。

1.3、SDRAM工作流程介绍

在这里插入图片描述
SDRAM初始化流程,下面是对每个步骤的简要说明:

  1. 上电

    • 给SDRAM供电并使能时钟,这是SDRAM正常工作的前提条件。
    • 在初始化阶段,通常会发送NOP(No Operation)命令,这是为了确保SDRAM处于稳定状态。
  2. 发送预充电命令

    • 预充电命令用于关闭所有Bank中已经打开的行地址,以便进行后续的初始化操作。
    • 通过发送预充电命令,可以确保SDRAM处于初始状态,准备接受后续的命令。
  3. 发送自动刷新命令

    • 在SDRAM初始化阶段,需要进行刷新操作以保持存储数据的稳定性。
    • 发送自动刷新命令可以依次对所有行进行刷新,确保存储数据不丢失。
  4. 设置模式寄存器

    • 模式寄存器包含了SDRAM的工作参数,包括突发长度、突发访问地址模式、CAS延迟等。
    • 通过设置模式寄存器,可以根据系统需求配置SDRAM的工作模式和性能参数。
  5. 完成

    • 完成以上步骤后,SDRAM初始化即完成,此时SDRAM已经处于正常工作状态,可以进行读写操作。

SDRAM的初始化过程是保证系统正常运行的关键步骤,确保了SDRAM在正常工作时能够稳定可靠地存储和读取数据。
在这里插入图片描述
对于 SDRAM的写操作流程,以下是每个步骤的简要说明:

  1. 发送激活命令

    • 首先,发送激活命令以激活目标存储单元。在激活命令中设置行地址和BANK地址,以确定要写入的存储单元位置。
  2. 发送写命令

    • 发送写命令时,同时设置列地址,完成对目标存储单元的寻址。这样,SDRAM就知道了数据应该写入哪个存储单元。
  3. 使能自动预充电

    • 在执行完一次数据写入后,为了准备下一次写入操作,需要进行自动预充电操作。通过拉高A10地址线,使能自动预充电功能。
  4. 执行预充电

    • 在发送激活命令后,根据预充电的时序要求(tRAS时间),SDRAM开始执行预充电操作。这个过程确保了下一次写入操作能够顺利进行。
  5. 完成一次数据写入

    • 当预充电完成后,可以执行下一次数据写入操作。这时发送第二次激活命令,启动下一次写入操作。这个过程可以循环执行,实现连续的数据写入操作。

以上步骤保证了数据能够正确地写入到SDRAM中,并且在写入过程中保持了正确的时序和顺序,确保了数据的准确性和可靠性。
在这里插入图片描述
对于 SDRAM读操作流程,以下是每个步骤的简要说明:

  1. 发送激活命令

    • 首先,发送激活命令以激活目标存储单元。在激活命令中设置行地址和BANK地址,以确定要读取的存储单元位置。
  2. 发送读命令

    • 发送读命令时,同时设置列地址,完成对目标存储单元的寻址。这样,SDRAM就知道了从哪个存储单元读取数据。
  3. 使能自动预充电

    • 在执行完一次数据读取后,为了准备下一次读取操作,需要进行自动预充电操作。通过拉高A10地址线,使能自动预充电功能。
  4. 执行预充电

    • 在发送激活命令后,根据预充电的时序要求(tRAS时间),SDRAM开始执行预充电操作。这个过程确保了下一次读取操作能够顺利进行。
  5. 完成一次数据读取

    • 当预充电完成后,可以执行下一次数据读取操作。这时发送第二次激活命令,启动下一次读取操作。这个过程可以循环执行,实现连续的数据读取操作。

以上步骤保证了数据能够正确地从SDRAM中读取出来,并且在读取过程中保持了正确的时序和顺序,确保了数据的准确性和可靠性。

二、FMC介绍

在这里插入图片描述
FMC(Flexible Memory Controller) 是STM32微控制器中的一种 灵活的存储控制器,具有驱动多种存储器的能力,包括SRAM、NOR/NAND Flash以及SDRAM等。下面是关于SDRAM控制器的特点:

  1. 两个SDRAM存储区域:FMC支持两个独立的SDRAM存储区域,并可以对它们进行独立的配置和管理。

  2. 支持多种数据总线宽度:FMC支持8位、16位和32位数据总线宽度,可以根据实际需求进行配置。

  3. 地址线位数:FMC支持13位行地址和11位列地址,这提供了足够的地址空间来寻址SDRAM中的存储单元。

  4. 内部存储区域:FMC支持4个内部存储区域的管理,可以根据需要对它们进行配置和操作。

  5. 支持不同访问方式:FMC支持字、半字和字节级别的访问操作,可以根据需要选择不同的访问方式。

  6. 自动行和存储区域边界管理:FMC具有自动管理行和存储区域边界的功能,简化了对SDRAM的管理和操作。

  7. 多存储区域乒乓访问:FMC支持多个存储区域之间的乒乓访问,可以实现更灵活的数据存取操作。

  8. 可编程时序参数:FMC的时序参数可以进行编程,以适应不同的SDRAM芯片和工作环境。

  9. 自动刷新操作:FMC支持自动刷新操作,并且可以编程刷新速率,确保SDRAM中的数据不丢失。

  10. 自刷新模式:FMC支持自刷新模式,可以在低功耗状态下保持数据,延长系统续航时间。

  11. 读FIFO缓存:FMC具有读FIFO缓存功能,可以提高数据读取的效率和性能。

综上所述,FMC作为STM32微控制器中的存储控制器,具有丰富的功能和灵活性,能够满足各种存储器的需求,并提供高效可靠的数据存取操作。

2.1、FMC-SDRAM 引脚说明介绍

在这里插入图片描述
FMC-SDRAM的引脚说明,以下是对每个引脚的简要解释:

  1. FMC_NBL:数据掩码信号,用于指示双向数据总线FMC_D[15:0]上的数据有效性。

  2. FMC_A:地址总线,用于传输地址信息,包括行地址和列地址。

  3. FMC_D:双向数据总线,用于传输数据,可用于读取或写入SDRAM存储器中的数据。

  4. FMC_SDCLK:同步时钟信号,提供给SDRAM存储器的时钟信号,用于同步数据传输。

  5. FMC_SDNWE:写使能信号,用于控制写操作的使能,告知SDRAM存储器当前操作为写入数据。

  6. FMC_SDCKE:SDRAM存储区域时钟使能信号,用于控制SDRAM存储器的时钟使能,使其进入正常工作模式。

  7. FMC_SDNE:SDRAM存储区域芯片使能信号,用于选中特定的SDRAM存储区域,使其处于操作状态。

  8. FMC_NRAS:行地址选通信号,用于指示行地址被选中,SDRAM存储器将执行与该行地址相关的操作。

  9. FMC_NCAS:列地址选通信号,用于指示列地址被选中,SDRAM存储器将执行与该列地址相关的操作。

这些引脚共同组成了FMC接口与SDRAM存储器之间的通信和控制链路,实现了对SDRAM存储器的读写操作。
在这里插入图片描述

2.2、外部器件地址映射

在这里插入图片描述
使用FMC外接存储器时,其存储单元会映射到STM32的内部寻址空间,这样MCU就可以通过内部地址访问外部存储器。通常情况下,外部存储器的地址范围会映射到STM32的特定地址范围,以便MCU能够方便地访问外部存储器中的数据。

具体的地址映射关系取决于外部存储器的类型、大小以及连接方式。在配置FMC时,需要指定外部存储器的基地址和大小,以及存储器接口的各种参数。然后,MCU会将这些参数用于确定外部存储器的地址范围,并将其映射到内部地址空间中。

一般来说,可以通过定义宏或配置寄存器来指定外部存储器的基地址,并根据存储器的大小确定其地址范围。然后,MCU在访问外部存储器时,会将内部地址转换为外部存储器的物理地址,从而实现对外部存储器的访问。

总的来说,FMC外部器件地址映射是通过配置FMC参数和寄存器来实现的,确保外部存储器的地址范围能够被正确地映射到STM32的内部地址空间中。

外部SDRAM具体地址怎么确定?

在这里插入图片描述
确定外部SDRAM的具体地址需要考虑以下几个因素,并根据这些因素配置SDRAM控制器:

  1. 内部存储区域数量: 确定SDRAM控制器中的存储区域数量,通常可以选择两个或四个。每个存储区域都会有自己的地址范围。

  2. 数据大小: 确定每个存储区域的数据总线宽度,可以选择8位、16位或32位。数据总线宽度会影响每次数据传输的大小。

  3. 行大小和列大小: 确定每个存储区域的行地址和列地址的位数。行大小通常是11位、12位或13位,而列大小通常是8位、9位、10位或11位。行地址和列地址的位数决定了存储器的寻址范围。

配置SDRAM控制器时,需要根据以上参数设置FMC的控制寄存器,包括存储器类型、数据宽度、行地址位数、列地址位数等。在设置这些参数时,要确保与外部SDRAM的规格相匹配,以确保正确的地址映射和数据传输。

一旦配置了SDRAM控制器,就可以根据其内部存储区域数量和每个存储区域的地址范围确定外部SDRAM的具体地址。通常情况下,可以通过指定基地址和存储器大小来确定外部SDRAM的地址范围,并将其映射到MCU的内部地址空间中,以便MCU可以直接访问外部SDRAM的数据。

2.3、SDRAM相关寄存器

在这里插入图片描述
对于STM32中的外部SDRAM控制器,通常会涉及以下几种重要的寄存器:

  1. 控制寄存器(FMC_SDCRx): 这些寄存器用于配置和控制每个SDRAM存储区域的参数,例如存储器类型、数据宽度、行地址位数、列地址位数等。每个存储区域都有对应的控制寄存器。

  2. 时序寄存器(FMC_SDTRx): 时序寄存器用于配置读写操作的时序参数,例如读写延迟、行地址选通延迟、列地址选通延迟等。这些参数需要根据外部SDRAM的规格来设置,以确保正确的数据传输。

  3. 命令模式寄存器(FMC_SDCMR): 命令模式寄存器用于发送各种SDRAM操作命令,例如激活命令、自动刷新命令、预充电命令等。通过配置命令模式寄存器,可以向外部SDRAM发送相应的命令,从而执行读写操作或者控制SDRAM的状态。

  4. 刷新定时器寄存器(FMC_SDRTR): 刷新定时器寄存器用于配置SDRAM的自动刷新参数,例如自动刷新周期。外部SDRAM需要定期进行刷新操作,以保持数据的有效性,刷新定时器寄存器用于设置自动刷新的时间间隔。

通过配置这些寄存器,可以有效地控制和管理外部SDRAM的操作,从而实现对SDRAM的读写访问以及刷新操作等功能。在配置这些寄存器时,需要根据外部SDRAM的规格和要求进行相应的设置,以确保系统能够正常地与外部SDRAM进行通信。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.4、FMC SDRAM 相关HAL库驱动

在这里插入图片描述
这些是关于FMC SDRAM 相关的HAL库函数的简要说明:

  1. HAL_SDRAM_Init(…): 这个函数用于初始化 FMC SDRAM 控制器,并设置相关的控制参数和时间参数。通过这个函数,可以启用外部 SDRAM 的控制。

  2. HAL_SDRAM_MspInit(…): 这是一个初始化回调函数,用于配置 FMC SDRAM 控制器的底层硬件资源,例如时钟使能和IO口配置等。在这个函数中,你可以完成与具体硬件平台相关的初始化工作。

  3. FMC_SDRAM_Init(…): 这个函数用于配置 FMC 控制器的 SDRAM 参数,例如地址线宽度、CAS 延迟以及 SDRAM 时钟等。它设置了 FMC 控制器的 SDCR1 寄存器。

  4. FMC_SDRAM_Timing_Init(…): 这个函数用于配置 FMC 控制器的 SDRAM 时序参数,例如自刷新时间、恢复延迟、预充电延迟等。它设置了 FMC 控制器的 SDTR1 寄存器。

  5. HAL_SDRAM_SendCommand(…): 这个函数用于向外部 SDRAM 发送命令,例如激活命令、自动刷新命令、预充电命令等。通过这个函数,可以实现对外部 SDRAM 的控制操作。

  6. HAL_SDRAM_ProgramRefreshRate(…): 这个函数用于设置外部 SDRAM 的刷新频率,即自动刷新的频率。通过这个函数,可以调整外部 SDRAM 的刷新频率,以满足系统的要求。

这些函数提供了在使用 FMC 控制器驱动外部 SDRAM 时所需的基本功能,可以帮助你方便地配置和控制外部 SDRAM,并实现对其的读写访问和刷新操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、SDRAM模块驱动步骤

在这里插入图片描述
SDRAM模块驱动步骤。下面是详细的步骤:

  1. 配置FMC:

    • 首先,需要启用 FMC 时钟,并配置相关的 IO 接口。这包括配置引脚为复用输出模式,并启用这些引脚的时钟。
    • 这一步可以通过 HAL 库提供的函数来完成,例如 HAL_GPIO_Init() 和 HAL_RCC_ClockConfig()。
  2. 设置SDRAM相关参数:

    • 使用 HAL 库提供的 HAL_SDRAM_Init() 函数初始化 FMC SDRAM 控制器。通过这个函数,可以设置 SDRAM 的控制参数和时序参数。
  3. 发送SDRAM初始化序列:

    • 在初始化完成后,需要发送 SDRAM 初始化序列以配置 SDRAM。这个序列通常包括时钟配置使能、所有存储区预充电、设置自刷新次数、设置模式寄存器等操作。
    • 可以使用 HAL_SDRAM_SendCommand() 函数来发送这些命令。
  4. 设置刷新频率:

    • 最后,需要设置 SDRAM 的刷新频率,以确保 SDRAM 在使用过程中能够按时刷新。通过调用 HAL_SDRAM_ProgramRefreshRate() 函数,可以设置 SDRAM 的刷新频率。

在完成这些步骤后,SDRAM 就可以被正确地配置和初始化,可以安全地使用了。

四、编程实战

SDRAM 工程

sdram.c

#include "./BSP/SDRAM/sdram.h"
#include "./SYSTEM/delay/delay.h"

SDRAM_HandleTypeDef g_sdram_handle;     /* SDRAM句柄 */

void sdram_init(void)
{
    FMC_SDRAM_TimingTypeDef sdram_timing;
    
    g_sdram_handle.Instance                 = FMC_SDRAM_DEVICE;                     /* SDRAM存储器设备 */
    
    g_sdram_handle.Init.SDBank              = FMC_SDRAM_BANK1;                      /* SDRAM BANK1 */
    g_sdram_handle.Init.ColumnBitsNumber    = FMC_SDRAM_COLUMN_BITS_NUM_9;          /* 列数量 9 */
    g_sdram_handle.Init.RowBitsNumber       = FMC_SDRAM_ROW_BITS_NUM_13;            /* 行数量 13 */
    g_sdram_handle.Init.MemoryDataWidth     = FMC_SDRAM_MEM_BUS_WIDTH_16;           /* 数据宽度 16 */
    g_sdram_handle.Init.InternalBankNumber  = FMC_SDRAM_INTERN_BANKS_NUM_4;         /* SDRAM BANK数量 4 */
    g_sdram_handle.Init.CASLatency          = FMC_SDRAM_CAS_LATENCY_3;              /* CAS潜伏期时间 3 */
    g_sdram_handle.Init.WriteProtection     = FMC_SDRAM_WRITE_PROTECTION_DISABLE;   /* 失能写保护 */
    g_sdram_handle.Init.SDClockPeriod       = FMC_SDRAM_CLOCK_PERIOD_2;             /* SDRAM时钟 HCLK/2 = 90MHz */ 
    g_sdram_handle.Init.ReadBurst           = FMC_SDRAM_RBURST_ENABLE;              /* 突发使能 */
    g_sdram_handle.Init.ReadPipeDelay       = FMC_SDRAM_RPIPE_DELAY_1;              /* 读通道延时 */
    
    sdram_timing.LoadToActiveDelay      = 2;    /* 加载模式寄存器的命令与激活命令或刷新命令之间的延迟 */
    sdram_timing.ExitSelfRefreshDelay   = 8;    /* 发出刷新命令到发出激活命令之间的延迟 */
    sdram_timing.SelfRefreshTime        = 6;    /* 自刷新周期 */
    sdram_timing.RowCycleDelay          = 6;    /* 刷新命令和激活命令之间的延迟 */
    sdram_timing.WriteRecoveryTime      = 2;    /* 写命令和预充电命令之间的延迟 */
    sdram_timing.RPDelay                = 2;    /* 预充电命令与其他命令之间的延迟 */
    sdram_timing.RCDDelay               = 2;    /* 激活命令与读/写命令之间的延迟 */
    
    HAL_SDRAM_Init(&g_sdram_handle, &sdram_timing);     /* 初始化SDRAM */
    
    /* 发送SDRAM初始化序列 */
    sdram_initialization_sequence();
    
    /* 设置刷新频率 */
    HAL_SDRAM_ProgramRefreshRate(&g_sdram_handle, 683);
}

void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef *hsdram)
{
    GPIO_InitTypeDef gpio_init_struct;

    /* 使能相关时钟 */
    __HAL_RCC_FMC_CLK_ENABLE();     /* 使能FMC时钟 */
    __HAL_RCC_GPIOC_CLK_ENABLE();   /* 使能GPIOC时钟 */
    __HAL_RCC_GPIOD_CLK_ENABLE();   /* 使能GPIOD时钟 */
    __HAL_RCC_GPIOE_CLK_ENABLE();   /* 使能GPIOE时钟 */
    __HAL_RCC_GPIOF_CLK_ENABLE();   /* 使能GPIOF时钟 */
    __HAL_RCC_GPIOG_CLK_ENABLE();   /* 使能GPIOG时钟 */
    
    /* 相关GPIO口初始化 */
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3;
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;        /* 推挽复用 */
    gpio_init_struct.Pull = GPIO_PULLUP;            /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;       /* 高速 */
    gpio_init_struct.Alternate = GPIO_AF12_FMC;     /* 复用为FMC */
    HAL_GPIO_Init(GPIOC, &gpio_init_struct);        /* 初始化PC0,2,3 */
    
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15;
    HAL_GPIO_Init(GPIOD, &gpio_init_struct);        /* 初始化PD0,1,8,9,10,14,15 */
    
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;              
    HAL_GPIO_Init(GPIOE, &gpio_init_struct);        /* 初始化PE0,1,7,8,9,10,11,12,13,14,15 */
    
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;              
    HAL_GPIO_Init(GPIOF, &gpio_init_struct);        /* 初始化PF0,1,2,3,4,5,11,12,13,14,15 */
    
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15;              
    HAL_GPIO_Init(GPIOG, &gpio_init_struct);        /* 初始化PG0,1,2,4,5,8,15 */
}

/**
 * @brief       向SDRAM发送命令
 * @param       bankx:0,向BANK5上面的SDRAM发送指令
 * @param             1,向BANK6上面的SDRAM发送指令
 * @param       cmd:指令(0,正常模式/1,时钟配置使能/2,预充电所有存储区/3,自动刷新/4,加载模式寄存器/5,自刷新/6,掉电)
 * @param       refresh:自刷新次数
 * @param       regval:对模式寄存器配置值
 * @retval      返回值:0,正常;1,失败.
 */
uint8_t sdram_send_cmd(uint8_t bankx, uint8_t cmd, uint8_t refresh, uint16_t regval)
{
    uint32_t target_bank = 0;
    
    FMC_SDRAM_CommandTypeDef command;
    
    if (bankx)
    {
        target_bank = FMC_SDRAM_CMD_TARGET_BANK2;
    }
    else
    {
        target_bank = FMC_SDRAM_CMD_TARGET_BANK1;
    }

    command.CommandMode             = cmd;          /* 命令 */
    command.CommandTarget           = target_bank;  /* 目标SDRAM存储区域 */
    command.AutoRefreshNumber       = refresh;      /* 自刷新次数 */
    command.ModeRegisterDefinition  = regval;       /* 要写入模式寄存器的值 */
    
    if (HAL_SDRAM_SendCommand(&g_sdram_handle, &command, 0x1000) == HAL_OK)     /* 向SDRAM发送命令 */
    {
        return 0;
    }
    return 1;
}

/* 发送SDRAM初始化序列 */
void sdram_initialization_sequence(void)
{
    uint32_t mode_reg = 0;
    
    /* 时钟配置使能 */
    sdram_send_cmd(0, FMC_SDRAM_CMD_CLK_ENABLE, 1, 0);
    
    /* 延迟,至少200us */
    delay_us(500);
    
    /* 所有BANK预充电 */
    sdram_send_cmd(0, FMC_SDRAM_CMD_AUTOREFRESH_MODE, 1, 0);

    /* 设置自刷新次数 */
    sdram_send_cmd(0, FMC_SDRAM_CMD_SELFREFRESH_MODE, 8, 0);

    /* 设置模式寄存器 */
    mode_reg = (uint32_t) SDRAM_MODEREG_BURST_LENGTH_1          |   /* 设置突发长度:1(可以是1/2/4/8) */
                          SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |   /* 设置突发类型:连续(可以是连续/交错) */
                          SDRAM_MODEREG_CAS_LATENCY_3           |   /* 设置CAS值:3(可以是2/3) */
                          SDRAM_MODEREG_OPERATING_MODE_STANDARD |   /* 设置操作模式:0,标准模式 */
                          SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;     /* 设置突发写模式:1,单点访问 */
    sdram_send_cmd(0, FMC_SDRAM_CMD_LOAD_MODE, 1, mode_reg);
}

sdram.h

#ifndef __SDRAM_H
#define __SDRAM_H

#include "./SYSTEM/sys/sys.h"

extern SDRAM_HandleTypeDef g_sram_handle;               /* SDRAM句柄 */
#define SDRAM_BASE_ADDR    ((uint32_t)(0XC0000000))     /* SDRAM开始地址 */

/* 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)

/* 函数声明 */
void sdram_init(void);
void sdram_initialization_sequence(void);
uint8_t sdram_send_cmd(uint8_t bankx, uint8_t cmd, uint8_t refresh, uint16_t regval);

#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SDRAM/sdram.h"

int main(void)
{
    uint8_t key_value = 0;
    uint16_t tmp_value = 0;
    
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(360, 25, 2, 8);    /* 设置时钟,180Mhz */
    delay_init(180);                        /* 延时初始化 */
    usart_init(115200);                     /* 初始化USART */
    led_init();                             /* 初始化LED */
    key_init();                             /* 初始化按键 */
    lcd_init();                             /* 初始化LCD */
    sdram_init();                           /* 初始化SDRAM */
    printf("sdram project \r\n");
    
    while(1)
    {
        key_value = key_scan(0);
        
        if (key_value == KEY1_PRES)
        {
            *(uint16_t *)(SDRAM_BASE_ADDR + 0x1000) = 0x8888;
            printf("Write Data succeed \r\n");
        }
        
        if (key_value == KEY0_PRES)
        {
            tmp_value = *(uint16_t *)(SDRAM_BASE_ADDR + 0x1000);
            printf("tmp_value %#x \r\n", tmp_value);
        }
        
        LED0_TOGGLE(); /* 红灯闪烁 */
        delay_ms(200);
    }
}

在这里插入图片描述
在这里插入图片描述

F429 SDRAM实验

sdram.c

#include "./BSP/SDRAM/sdram.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/sys/sys.h"
#include "./BSP/LCD/lcd.h"

SDRAM_HandleTypeDef g_sdram_handle;   /* SDRAM句柄 */

/**
 * @brief       初始化SDRAM
 * @param       无
 * @retval      无
 */
void sdram_init(void)
{
    FMC_SDRAM_TimingTypeDef sdram_timing;

    g_sdram_handle.Instance = FMC_SDRAM_DEVICE;                              /* SDRAM在BANK5,6 */
    g_sdram_handle.Init.SDBank = FMC_SDRAM_BANK1;                            /* 第一个SDRAM BANK */
    g_sdram_handle.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;      /* 列数量 */
    g_sdram_handle.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;           /* 行数量 */
    g_sdram_handle.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;        /* 数据宽度为16位 */
    g_sdram_handle.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;   /* 一共4个BANK */
    g_sdram_handle.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;                /* CAS为3 */
    g_sdram_handle.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;/* 失能写保护 */
    g_sdram_handle.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;            /* SDRAM时钟为HCLK/2=180M/2=90M=11.1ns */
    g_sdram_handle.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;                 /* 使能突发 */
    g_sdram_handle.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1;             /* 读通道延时 */
    
    sdram_timing.LoadToActiveDelay = 2;             /* 加载模式寄存器到激活时间的延迟为2个时钟周期 */
    sdram_timing.ExitSelfRefreshDelay = 8;          /* 退出自刷新延迟为8个时钟周期 */
    sdram_timing.SelfRefreshTime = 6;               /* 自刷新时间为6个时钟周期 */
    sdram_timing.RowCycleDelay = 6;                 /* 行循环延迟为6个时钟周期 */
    sdram_timing.WriteRecoveryTime = 2;             /* 恢复延迟为2个时钟周期 */
    sdram_timing.RPDelay = 2;                       /* 行预充电延迟为2个时钟周期 */
    sdram_timing.RCDDelay = 2;                      /* 行到列延迟为2个时钟周期 */
    HAL_SDRAM_Init(&g_sdram_handle, &sdram_timing); /* 初始化SDRAM */

    sdram_initialization_sequence(&g_sdram_handle); /* 发送SDRAM初始化序列 */

    /* 刷新频率计数器(以SDCLK频率计数),计算方法:
     * COUNT=SDRAM刷新周期/行数-20=SDRAM刷新周期(us)*SDCLK频率(Mhz)/行数
     * 我们使用的SDRAM刷新周期为64ms,SDCLK=180/2=90Mhz,行数为8192(2^13).
     * 所以,COUNT=64*1000*90/8192-20=683 */
    HAL_SDRAM_ProgramRefreshRate(&g_sdram_handle, 683);     /* 设置刷新频率 */
}

/**
 * @brief       发送SDRAM初始化序列
 * @param       无
 * @retval      无
 */
void sdram_initialization_sequence(SDRAM_HandleTypeDef *hsdram)
{
    uint32_t temp = 0;

    /* SDRAM控制器初始化完成以后还需要按照如下顺序初始化SDRAM */
    sdram_send_cmd(0, FMC_SDRAM_CMD_CLK_ENABLE, 1, 0);          /* 时钟配置使能 */
    delay_us(500);                                              /* 至少延时500us */
    sdram_send_cmd(0, FMC_SDRAM_CMD_PALL, 1, 0);                /* 对所有存储区预充电 */
    sdram_send_cmd(0, FMC_SDRAM_CMD_AUTOREFRESH_MODE, 8, 0);    /* 设置自刷新次数 */

    /* 配置模式寄存器,SDRAM的bit0~bit2为指定突发访问的长度,
     * bit3为指定突发访问的类型,bit4~bit6为CAS值,bit7和bit8为运行模式
     * bit9为指定的写突发模式,bit10和bit11位保留位 */
    temp = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1  |    /* 设置突发长度:1(可以是1/2/4/8) */
              SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL  |    /* 设置突发类型:连续(可以是连续/交错) */
              SDRAM_MODEREG_CAS_LATENCY_3          |    /* 设置CAS值:3(可以是2/3) */
              SDRAM_MODEREG_OPERATING_MODE_STANDARD|    /* 设置操作模式:0,标准模式 */
              SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;     /* 设置突发写模式:1,单点访问 */
    sdram_send_cmd(0, FMC_SDRAM_CMD_LOAD_MODE, 1, temp);        /* 设置SDRAM的模式寄存器 */
}

/**
 * @brief       SDRAM底层驱动,引脚配置,时钟使能
 * @note        此函数会被HAL_SDRAM_Init()调用
 * @param       hsdram:SDRAM句柄
 * @retval      
 */
void HAL_SDRAM_MspInit(SDRAM_HandleTypeDef *hsdram)
{
    GPIO_InitTypeDef gpio_init_struct;

    __HAL_RCC_FMC_CLK_ENABLE();     /* 使能FMC时钟 */
    __HAL_RCC_GPIOC_CLK_ENABLE();   /* 使能GPIOC时钟 */
    __HAL_RCC_GPIOD_CLK_ENABLE();   /* 使能GPIOD时钟 */
    __HAL_RCC_GPIOE_CLK_ENABLE();   /* 使能GPIOE时钟 */
    __HAL_RCC_GPIOF_CLK_ENABLE();   /* 使能GPIOF时钟 */
    __HAL_RCC_GPIOG_CLK_ENABLE();   /* 使能GPIOG时钟 */
    
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3;  
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;        /* 推挽复用 */
    gpio_init_struct.Pull = GPIO_PULLUP;            /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;       /* 高速 */
    gpio_init_struct.Alternate = GPIO_AF12_FMC;     /* 复用为FMC */
    HAL_GPIO_Init(GPIOC, &gpio_init_struct);        /* 初始化PC0,2,3 */
    
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15;
    HAL_GPIO_Init(GPIOD, &gpio_init_struct);        /* 初始化PD0,1,8,9,10,14,15 */
    
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;              
    HAL_GPIO_Init(GPIOE, &gpio_init_struct);        /* 初始化PE0,1,7,8,9,10,11,12,13,14,15 */
    
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;              
    HAL_GPIO_Init(GPIOF, &gpio_init_struct);        /* 初始化PF0,1,2,3,4,5,11,12,13,14,15 */
    
    gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15;              
    HAL_GPIO_Init(GPIOG, &gpio_init_struct);        /* 初始化PG0,1,2,4,5,8,15 */
}

/**
 * @brief       向SDRAM发送命令
 * @param       bankx:0,向BANK5上面的SDRAM发送指令
 * @param             1,向BANK6上面的SDRAM发送指令
 * @param       cmd:指令(0,正常模式/1,时钟配置使能/2,预充电所有存储区/3,自动刷新/4,加载模式寄存器/5,自刷新/6,掉电)
 * @param       refresh:自刷新次数
 * @param       regval:对模式寄存器配置值
 * @retval      返回值:0,正常;1,失败.
 */
uint8_t sdram_send_cmd(uint8_t bankx, uint8_t cmd, uint8_t refresh, uint16_t regval)
{
    uint32_t target_bank = 0;
    FMC_SDRAM_CommandTypeDef command;
    
    if (bankx == 0)
    {
        target_bank = FMC_SDRAM_CMD_TARGET_BANK1;
    }
    else if (bankx == 1)
    {
        target_bank = FMC_SDRAM_CMD_TARGET_BANK2;
    }

    command.CommandMode = cmd;                /* 命令 */
    command.CommandTarget = target_bank;      /* 目标SDRAM存储区域 */
    command.AutoRefreshNumber = refresh;      /* 自刷新次数 */
    command.ModeRegisterDefinition = regval;  /* 要写入模式寄存器的值 */

    if (HAL_SDRAM_SendCommand(&g_sdram_handle, &command, 0X1000) == HAL_OK) /* 向SDRAM发送命令 */
    {
        return 0;
    }
    else
    {
        return 1;
    }
}

/**
 * @brief       在指定地址(WriteAddr+Bank5_SDRAM_ADDR)开始,连续写入n个字节
 * @param       pBuffer:字节指针
 * @param       WriteAddr:要写入的地址
 * @param       n:要写入的字节数
 * @retval      无
*/
void fmc_sdram_write_buffer(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t n)
{
    for (; n != 0; n--)
    {
        *(__IO uint8_t*)(Bank5_SDRAM_ADDR + WriteAddr) = *pBuffer;
        WriteAddr++;
        pBuffer++;
    }
}

/**
 * @brief       在指定地址((WriteAddr+Bank5_SDRAM_ADDR))开始,连续读出n个字节.
 * @param       pBuffer:字节指针
 * @param       ReadAddr:要读出的起始地址
 * @param       n:要写入的字节数
 * @retval      无
*/
void fmc_sdram_read_buffer(uint8_t *pBuffer, uint32_t ReadAddr, uint32_t n)
{
    for (; n != 0; n--)
    {
        *pBuffer++ = *(__IO uint8_t*)(Bank5_SDRAM_ADDR + ReadAddr);
        ReadAddr++;
    }
}

sdram.h

#ifndef _SDRAM_H
#define _SDRAM_H

#include "./SYSTEM/sys/sys.h"

extern SDRAM_HandleTypeDef g_sram_handle;               /* SDRAM句柄 */
#define Bank5_SDRAM_ADDR    ((uint32_t)(0XC0000000))    /* SDRAM开始地址 */

/* 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)

/* 函数声明 */
void sdram_init(void);
uint8_t sdram_send_cmd(uint8_t bankx, uint8_t cmd, uint8_t refresh, uint16_t regval);
void sdram_initialization_sequence(SDRAM_HandleTypeDef *hsdram);
void fmc_sdram_write_buffer(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t n);
void fmc_sdram_read_buffer(uint8_t *pBuffer, uint32_t ReadAddr, uint32_t n);


#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SDRAM/sdram.h"


#if !(__ARMCC_VERSION >= 6010050)   /* 不是AC6编译器,即使用AC5编译器时 */

uint16_t testsdram[250000] __attribute__((at(0XC0000000)));   /* 测试用数组 */

#else      /* 使用AC6编译器时 */

uint16_t testsdram[250000] __attribute__((section(".bss.ARM.__at_0xC0000000")));

#endif

/**
 * @brief       SDRAM内存测试
 * @param       x,y:起点坐标
 * @retval      无
*/
void fmc_sdram_test(uint16_t x,uint16_t y)
{  
    uint32_t i = 0;
    uint32_t temp = 0;
    uint32_t sval = 0;    /* 在地址0读到的数据 */
    lcd_show_string(x, y, 180, y + 16, 16, "Ex Memory Test:    0KB ", RED);

    /* 每隔16K字节,写入一个数据,总共写入2048个数据,刚好是32M字节 */
    for (i = 0; i < 32 * 1024 * 1024; i += 16 * 1024)
    {
        *(__IO uint32_t*)(Bank5_SDRAM_ADDR + i) = temp; 
        temp++;
    }

    /* 依次读出之前写入的数据,进行校验 */
    for (i = 0; i < 32 * 1024 * 1024; i += 16 * 1024) 
    {
        temp = *(__IO uint32_t*)(Bank5_SDRAM_ADDR + i);

        if (i == 0)
        {
            sval = temp;
        }
        else if (temp <= sval)
        {
            break;     /* 后面读出的数据一定要比第一次读到的数据大 */
        }
        
        lcd_show_num(x + 15 * 8, y, (uint16_t)(temp - sval + 1) * 16, 5, 16, 0);    /* 显示内存容量 */
        printf("SDRAM Capacity:%dKB\r\n",(uint16_t)(temp-sval + 1) * 16);           /* 打印SDRAM容量 */
    }
}

int main(void)
{
    uint8_t key;
    uint8_t i = 0;
    uint32_t ts = 0;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(360, 25, 2, 8);    /* 设置时钟,180Mhz */
    delay_init(180);                        /* 延时初始化 */
    usart_init(115200);                     /* 初始化USART */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    key_init();                             /* 初始化KEY */
    sdram_init();                           /* 初始化SDRAM */

    lcd_show_string(30, 50, 200, 16, 16,"STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16,"SDRAM TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16,"ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16,"KEY0:Test Sram", RED);
    lcd_show_string(30, 130, 200, 16, 16,"KEY1:TEST Data", RED);

    for(ts = 0; ts < 250000; ts++)
    {
        testsdram[ts]= ts;       /* 预存测试数据 */
    }
    
    while (1)
    {
        key = key_scan(0);      /* 不支持连按 */

        if (key == KEY0_PRES)
        {
            fmc_sdram_test(30, 170);   /* 测试SDRAM容量 */
        }
        else if(key == KEY1_PRES)       /* 打印预存测试数据 */
        {
            for(ts = 0; ts < 250000; ts++)
            {
                lcd_show_num(30, 190,testsdram[ts], 6, 16, RED);     /* 显示测试数据 */
                printf("testsram[%d]:%d\r\n", ts, testsdram[ts]);
            }
        }
        else 
        {
            delay_ms(10);
        }
        
        i++;

        if (i == 20)
            i = 0;

        LED0_TOGGLE();       /* DS0闪烁 */
    }
}

在这里插入图片描述

五、总结

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖喱年糕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值