【STM32H7教程】第47章 STM32H7的FMC总线基础知识和HAL库API

时间:2022-04-10 12:44:59

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第47章       STM32H7的FMC总线基础知识和HAL库API

本章节为大家讲解并行总线接口FMC(Flexible memory controller,灵活动态存储器),用到的地方比较多,比如V7开发板外接DM9000,SDRAM,OLED,AD7606,NAND,扩展IO等都有用到。

47.1 初学者重要提示

47.2 FMC基础知识

47.3 FMC的HAL库用法

47.4 源文件stm32h7xx_ll_fmc.c

47.5 总结

47.1 初学者重要提示

  1. F1和F407使用的是FSMC(Flexible static memory controller),跟F429和H7带的FMC区别是不支持SDRAM,也就是差在字母static,使用FMC可以动态刷新SDRAM,来保持电量。
  2. FMC控制SRAM型存储器和NAND型存储器是异步控制,而控制SDRAM属于同步控制。同步和异步的区别是同步方式需要一个专门的时钟控制引脚。
  3. FMC配置中未用到引脚均可以继续用作通用I/O模式或者其它复用功能,仅需不配置FMC复用即可。
  4. STM32H7驱动32位SDRAM的写速度狂飙376MB/S,读速度189MB/S。http://www.armbbs.cn/forum.php?mod=viewthread&tid=91481

47.2 FMC基础知识

FMC的几个关键知识点放在开头说:

  1. STM32H7的FMC总线是挂载64位带宽的AXI总线上,F1,F4和F7是挂在32位总线上。
  2. 使用FMC,可以用来外挂NOR/PSRAM型存储器,SRAM型存储器,NAND型存储器,SDRAM存储器等,从而可以用来驱动AD7606,OLED,DM9000等并行控制设备。
  3. 支持8位,16位和32位总线带宽控制。
  4. 每个片选下的存储器空间配置都是独立的,有专门的寄存器,互不影响。这点比较重要,要牢记。

47.2.1 FMC硬件框图

认识一个外设,最好的方式就是看他的框图,方便我们快速的了解FMC的基本功能,然后再看手册了解细节。框图如下所示:

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

通过这个框图,我们可以得到如下信息:

  • fmc_it to NVIC接口

用于FMC的NVIC配置,使能其中断通道。

  • fmc_hclk接口

FMC的接口时钟。

  • fmc_ker_ck接口

FMC的内核时钟。

  • SDRAM signals

驱动SDRAM独享的几个控制信号。

  • NAND signals

驱动NAND独享的几个控制信号。

  • NOR/PSRAM/SRAM shared signals

驱动NOR/PSRAM/SRAM的共享信号,主要是片选、读写控制和等待信号。

  • Shared signals

这个是驱动任何设备都要用到地址和数据线,地址线有A[0] – A[25],共计26根,能访问的地址空间是2^26 = 64MB。数据线是D[0] – D[31],共计32根,也就是说可以支持32位带宽的存储器访问模式。

  • NOR/SRAM shared signals

用于NOR/SRAM存储器的字节方式控制。

  • NOR/PSRAM signals

用于控制需要同步时钟的NOR/PSRAM类型存储器。

47.2.2 FMC时钟选择

使用FMC可以选择如下几种时钟源HCLK3,PLL1Q,PLL2R和PER_CK:

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

我们这里直接使用HCLK3,配置STM32H7的主频为400MHz的时候,HCLK3输出的200MHz,这个速度是FMC支持的最高时钟,正好用于这里:

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

47.2.3 FMC地址区分配

FMC总线可操作的地址范围0x60000000到0xDFFFFFFF,具体的框图如下:

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

根据上面这个地址映射图,下面几个知识点要了解到:

  与F1和F4不同,H7系列的FMC总线接口支持重映射,也就是可以设置这几块存储器的位置。

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

根据BMAP[0:1]设置的数值00,01或者10,支持三种分配方式。默认情况下,使用BMAP[0:1] = 00的分配方式即可,也就是上面截图中的分配方式。

  对于NOR/PSRAM/SRAM块区。

这个块区用到的地方最多,像NAND和SDRAM块区基本只能接NAND和SDRAM,而NOR/PSRAM/SRAM区就不同了,除了能接这几种类型的存储器,还可以外接DM9000,SDRAM,OLED,AD7606等总线外设。这个块区有4路片选,分别是FMC_NE1,FMC_NE2,FMC_NE3和FMC_NE4,这几个片选在芯片上都有对应的引脚,每个片选可以管理64MB的访问空间,这个是由FMC引出的26路地址线FMC_A[0:25]决定的,2^26 = 64MB。

  • FMC_NE1:首地址0x6000 0000,可以管理的地址范围0x6000 0000到0x63FF FFFF。
  • FMC_NE2:首地址0x6400 0000,可以管理的地址范围0x6400 0000到0x67FF FFFF。
  • FMC_NE3:首地址0x6800 0000,可以管理的地址范围0x6800 0000到0x6BFF FFFF。
  • FMC_NE4:首地址0x6C00 0000,可以管理的地址范围0x6C00 0000到0x6FFF FFFF。

这里特别注意一点,每个片选控制的地址空间都是可以独立配置的,与其它片选控制的空间互不影响。也许大家会问,只有这四路片选不够用啊,而且还有片选引脚被复用做其它功能的情况,这可以通过外接地址译码器解决,V7开发板就是通过这种方式外接了多个总线设备。

  使用NAND块区外接NAND的时候,务必要使用MPU将这块区域设置为Device模式。

47.2.4 NOR/PSRAM/SRAM区地址映射(重要)

FMC总线除了复用到具体引脚上的A[25:0],共计26路地址以外,还有两条内部总线ADDR[27:26]。通过这两路线才区分出了FMC_NE1,FMC_NE2,FMC_NE3和FMC_NE4。即0x60xx xxxx,0x64xx xxxx,0x68xx xxx和0x6Cxx xxxx。

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

这些地址都是按照字节进行访问的,如果控制8位宽度的存储设备比较方便,如果控制16位,32位宽度的存储设备,且不支持字节访问就比较麻烦了。比如16位宽度的NOR Flash,写入仅支持16位或者32位(通过写入两次16位),写入8位数据是不支持的。这个时候,为了方便操作,FMC在硬件设计上就提供了一种解决办法,将内部数据总线ADDR[25:0]措位后接到FMC_A地址引脚上。

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

配置外部存储器的宽度为16位, FMC将使用内部的ADDR[25:1]地址来作为对外部存储器的寻址地址FMC_A[24:0]。如果存储器宽度为32位, FMC将使用内部的ADDR[25:2]地址进行外部寻址。

但是不管存储器的宽度是多少, FMC_A[0]都应连接到外部存储器地址A[0]。

这里有一点要补充下,如果外接SDRAM/SRAM配置为16位或者32位带宽的时候,如何实现字节方式的读写。针对这个问题,SDRAM/SRAM都有专门的字节控制引脚,所以操作起来比较方便。

47.2.5 NOR/PSRAM/SRAM时序控制

F103和F407仅支持16位总线访问,等到F429,H7已经支持32位总线访问。以驱动SRAM为例,需要用到下面的数据,地址和控制引脚。配置完毕后,就可以像使用内部SRAM一样进行读写了,使用比较方便。

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

FMC支持以下四种时序模式:

/** @defgroup FMC_Access_Mode FMC Access Mode
* @{
*/
#define FMC_ACCESS_MODE_A ((uint32_t)0x00000000U)
#define FMC_ACCESS_MODE_B ((uint32_t)0x10000000U)
#define FMC_ACCESS_MODE_C ((uint32_t)0x20000000U)
#define FMC_ACCESS_MODE_D ((uint32_t)0x30000000)

每个模式的时序略有不同,我们这里以常用的模式A为例进行说明:

先来认识如下几个关键参数:

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

了解了这几个参数后再来看模式A的读时序:

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

通过读时序图,我们要了解到以下几点知识:

  • NBL[x:0]高低字节控制信号,NEx片选信号,NOE读使能信号和NWE写使能前面的字母N(Ngative)示这几个信号低电平有效。
  • 地址信号A[25:0],NBL[x:0]高低字节控制信号,要在NEx片选信号使能前准备就绪。整个读取过程中,NWE写使能信号是不起作用的,被设置为高电平。
  • NEx片选后,NOE要保持一段时间的高电平,这个时间就是ADDSET地址建立时间(通过寄存器FMC_BTRx可配置)。之后NOE变为低电平,读使能。低电平保持的时间由DATAST数据建立时间(通过寄存器FMC_BTRx可配置)决定。
  • 读取完毕数据后,NOE变成高电平,NEx变成高电平。

这里主要一点,A模式读时序没有用到地址保持时间(Address Hold)。

模式A的写时序:

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

通过写时序图,我们要了解到以下几点知识:

  • NBL[x:0]高低字节控制信号,NEx片选信号,NOE读使能信号和NWE写使能前面的字母N(Ngative)示这几个信号低电平有效。
  • 地址信号A[25:0],NBL[x:0]高低字节控制信号,要在NEx片选信号使能前准备就绪。整个写入过程中,NOE读使能信号是不起作用的,被设置为高电平。
  • NEx片选后,NWE要保持一段时间的高电平,这个时间就是ADDSET地址建立时间(通过寄存器FMC_BTRx可配置)。之后NWE变为低电平,写使能。低电平保持的时间由DATAST数据建立时间(通过寄存器FMC_BTRx可配置)决定。
  • 写入完毕数据后,等待NWE置高1个FMC时钟周期后,NOE变成低电平,NEx变成高电平。

这里主要一点,A模式写时序没有用到地址保持时间(Address Hold)。

47.2.6 NOR/PSRAM/SRAM的不同位宽通信问题

FMC是外挂在64位带宽的AXI总线上,可以8位,16位,32位或者64位方式操作。如果FMC外接设备是16位带宽,H7参考手册给出了不同位宽设置的支持情况:

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

根据上面表格,操作并口NOR Flash时,比如8位数据写入是不支持的,而读取是支持的,这是因为可以读取一个16位数据,放弃高字节或者低字节。

【STM32H7教程】第47章  STM32H7的FMC总线基础知识和HAL库API

根据上面表格,操作SRAM就比较方便了,各种数据位宽都可以正常操作SRAM,因为SRAM有专门的,NBL[x:0]高低字节控制引脚。

47.3 FMC的HAL库用法

FMC的HAL库用法其实就是几个结构体变量成员的配置和使用,然后配置GPIO、时钟,并根据需要配置NVIC、中断和DMA。下面我们逐一展开为大家做个说明。

47.3.1 NOR/PSRAM/SRAM寄存器结构体

FMC控制NOR/PSRAM/SRAM相关的寄存器是通过HAL库中的结构体FMC_NORSRAM_TypeDef和FMC_NORSRAM_EXTENDED_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义:

#define FMC_NORSRAM_TypeDef            FMC_Bank1_TypeDef
#define FMC_NORSRAM_EXTENDED_TypeDef FMC_Bank1E_TypeDef typedef struct
{
__IO uint32_t BTCR[];
} FMC_Bank1_TypeDef; typedef struct
{
__IO uint32_t BWTR[];
} FMC_Bank1E_TypeDef;

__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:

#define     __O     volatile             /*!< Defines 'write only' permissions */
#define __IO volatile /*!< Defines 'read / write' permissions */

FMC控制NOR/PSRAM/SRAM设备涉及到FMC_BCRx,FMC_BTRx和FMC_BWTRx这三个寄存器(x的范围是1到4),共计4路片选NE1 – NE4,每路对应一组FMC_BCRx,FMC_BTRx和FMC_BWTR。为了方便控制,HAL库是将4个寄存器FMC_BCRx和4个寄存器FMC_BTRx统一由结构体变量FMC_Bank1_TypeDef来定义,__IO uint32_t BTCR[8]正好8个32位变量。

而结构体变量FMC_Bank1E_TypeDef的__IO uint32_t BWTR[7]是用来定义4个FMC_BWTRx寄存器,也许大家会问为什么要用7个32位变量来定义,这是因为每个FMC_BWTRx寄存器的地址间隔8个字节。

下面再来看这几个寄存器的具体定义,在stm32h743xx.h文件。

#define PERIPH_BASE           ((uint32_t)0x40000000)
#define D1_AHB1PERIPH_BASE (PERIPH_BASE + 0x12000000)
#define FMC_R_BASE (D1_AHB1PERIPH_BASE + 0x4000) #define FMC_Bank1_R_BASE (FMC_R_BASE + 0x0000)
#define FMC_Bank1E_R_BASE (FMC_R_BASE + 0x0104)
#define FMC_Bank2_R_BASE (FMC_R_BASE + 0x0060)
#define FMC_Bank3_R_BASE (FMC_R_BASE + 0x0080)
#define FMC_Bank5_6_R_BASE (FMC_R_BASE + 0x0140) <-----展开下面的宏定义,(FMC_Bank1_TypeDef *) 0x52004000
#define FMC_Bank1 ((FMC_Bank1_TypeDef *) FMC_Bank1_R_BASE)
#define FMC_Bank1E ((FMC_Bank1E_TypeDef *) FMC_Bank1E_R_BASE)
#define FMC_Bank2 ((FMC_Bank2_TypeDef *) FMC_Bank2_R_BASE)
#define FMC_Bank3 ((FMC_Bank3_TypeDef *) FMC_Bank3_R_BASE)
#define FMC_Bank5_6 ((FMC_Bank5_6_TypeDef *) FMC_Bank5_6_R_BASE) #define FMC_NORSRAM_DEVICE FMC_Bank1
#define FMC_NORSRAM_EXTENDED_DEVICE FMC_Bank1E
#define FMC_NAND_DEVICE FMC_Bank3
#define FMC_SDRAM_DEVICE FMC_Bank5_6

我们访问FMC的BCR0寄存器可以采用这种形式:FMC->BCR[0] = 0。

47.3.2 NOR/PSRAM/SRAM参数配置结构体FMC_NORSRAM_InitTypeDef

这个结构体比较重要,FMC驱动NOR/PSRAM/SRAM设备的参数配置,全靠这个结构体实现,具体定义如下:

typedef struct
{
uint32_t NSBank;
uint32_t DataAddressMux;
uint32_t MemoryType;
uint32_t MemoryDataWidth;
uint32_t BurstAccessMode;
uint32_t WaitSignalPolarity;
uint32_t WaitSignalActive;
uint32_t WriteOperation;
uint32_t WaitSignal;
uint32_t ExtendedMode;
uint32_t AsynchronousWait;
uint32_t WriteBurst;
uint32_t ContinuousClock;
uint32_t WriteFifo;
uint32_t PageSize;
}FMC_NORSRAM_InitTypeDef;

下面将这几个成员逐一为大家做个介绍:

  •   uint32_t NSBank

用于设置使用的BANK,片选NE1对于BANK1,片选NE2对应BANK2,片选NE3对应BANK3,NE4对应BANK4,支持的参数类型如下:

#define FMC_NORSRAM_BANK1                       ((uint32_t)0x00000000U)
#define FMC_NORSRAM_BANK2 ((uint32_t)0x00000002U)
#define FMC_NORSRAM_BANK3 ((uint32_t)0x00000004U)
#define FMC_NORSRAM_BANK4 ((uint32_t)0x00000006U)
  •   uint32_t DataAddressMux

用于设置地址线和数据线复用,都使用的FMC_D[31:0],可以选择使能或者禁止:

#define FMC_DATA_ADDRESS_MUX_DISABLE            ((uint32_t)0x00000000U)
#define FMC_DATA_ADDRESS_MUX_ENABLE ((uint32_t)0x00000002U)
  •   uint32_t MemoryType

用于设置使用的存储器类型,具体支持的参数如下:

#define FMC_MEMORY_TYPE_SRAM                    ((uint32_t)0x00000000U)
#define FMC_MEMORY_TYPE_PSRAM ((uint32_t)0x00000004U)
#define FMC_MEMORY_TYPE_NOR ((uint32_t)0x00000008U)
  •   uint32_t MemoryDataWidth

用于设置外接的存储器位宽,具体支持的参数如下:

#define FMC_NORSRAM_MEM_BUS_WIDTH_8             ((uint32_t)0x00000000U)
#define FMC_NORSRAM_MEM_BUS_WIDTH_16 ((uint32_t)0x00000010U)
#define FMC_NORSRAM_MEM_BUS_WIDTH_32 ((uint32_t)0x00000020U)
  •   uint32_t BurstAccessMode

用于使能或者禁止突发模式,仅用于支持同步突发的存储器,具体支持的参数如下:

#define FMC_BURST_ACCESS_MODE_DISABLE           ((uint32_t)0x00000000U)
#define FMC_BURST_ACCESS_MODE_ENABLE ((uint32_t)0x00000100U)
  •   uint32_t WaitSignalPolarity

用于设置等待信号的极性,仅当使能突发模式时有效,具体支持的参数如下:

#define FMC_WAIT_SIGNAL_POLARITY_LOW            ((uint32_t)0x00000000U)
#define FMC_WAIT_SIGNAL_POLARITY_HIGH ((uint32_t)0x00000200U)
  •   uint32_t WaitSignalActive

用于在等待状态之前或等待状态期间,存储器是否在一个时钟周期内置位等待信号,仅当使能突发模式时有效,具体支持的参数如下:

#define FMC_WAIT_TIMING_BEFORE_WS               ((uint32_t)0x00000000U)
#define FMC_WAIT_TIMING_DURING_WS ((uint32_t)0x00000800U)
  •   uint32_t WriteOperation

用于使能或者禁止写保护,具体支持的参数如下:

#define FMC_WRITE_OPERATION_DISABLE             ((uint32_t)0x00000000U)
#define FMC_WRITE_OPERATION_ENABLE ((uint32_t)0x00001000U)
  •   uint32_t WaitSignal

用于使能或者禁止通过等待信号来插入等待状态,仅当使能突发模式时有效,具体支持的参数如下:

#define FMC_WAIT_SIGNAL_DISABLE                 ((uint32_t)0x00000000U)
#define FMC_WAIT_SIGNAL_ENABLE ((uint32_t)0x00002000U)
  •   uint32_t ExtendedMode

用于使能或者禁止扩展模式,具体支持的参数如下:

#define FMC_EXTENDED_MODE_DISABLE               ((uint32_t)0x00000000U)
#define FMC_EXTENDED_MODE_ENABLE ((uint32_t)0x00004000U)
  •   uint32_t AsynchronousWait

用于异步传输期间,使能或者禁止等待信号,仅操作异步存储器有效,具体支持的参数如下:

#define FMC_ASYNCHRONOUS_WAIT_DISABLE           ((uint32_t)0x00000000U)
#define FMC_ASYNCHRONOUS_WAIT_ENABLE ((uint32_t)0x00008000U)
  •   uint32_t WriteBurst

用于使能或者禁止异步的写突发操作:

#define FMC_WRITE_BURST_DISABLE                 ((uint32_t)0x00000000U)
#define FMC_WRITE_BURST_ENABLE ((uint32_t)0x00080000U)
  •   uint32_t ContinuousClock

用于使能或者禁止FMC同步异步模式的时钟信号输出,此参数仅通过FMC_BCR1使能,与FMC_BCR2,3,4无关,具体支持的参数如下:

#define FMC_CONTINUOUS_CLOCK_SYNC_ONLY          ((uint32_t)0x00000000U) /* 同步模式时钟输出 */
#define FMC_CONTINUOUS_CLOCK_SYNC_ASYNC ((uint32_t)0x00100000U) /* 同步和异步模式均时钟输出 */
  •   uint32_t WriteFifo

用于使能或者禁止写FIFO,此参数仅通过FMC_BCR1使能,与FMC_BCR2,3,4无关,具体支持的参数如下:

#define FMC_WRITE_FIFO_DISABLE           ((uint32_t)FMC_BCR1_WFDIS)
#define FMC_WRITE_FIFO_ENABLE ((uint32_t)0x00000000U)
  •   uint32_t PageSize

用于设置页大小,FMC操作器件Cellular RAM 1.5时要用到,具体支持的参数如下:

#define FMC_PAGE_SIZE_NONE           ((uint32_t)0x00000000U)
#define FMC_PAGE_SIZE_128 ((uint32_t)FMC_BCR1_CPSIZE_0)
#define FMC_PAGE_SIZE_256 ((uint32_t)FMC_BCR1_CPSIZE_1)
#define FMC_PAGE_SIZE_512 ((uint32_t)(FMC_BCR1_CPSIZE_0 | FMC_BCR1_CPSIZE_1))
#define FMC_PAGE_SIZE_1024 ((uint32_t)FMC_BCR1_CPSIZE_2)

47.3.3 NOR/PSARM/SRAM时序配置结构体FMC_NORSRAM_TimingTypeDef

此结构体对于配置NOR/PSRAM/SRAM器件的时序参数比较重要,具体定义如下:

typedef struct
{
uint32_t AddressSetupTime;
uint32_t AddressHoldTime;
uint32_t DataSetupTime;
uint32_t BusTurnAroundDuration;
uint32_t CLKDivision;
uint32_t DataLatency;
uint32_t AccessMode;
}FMC_NORSRAM_TimingTypeDef;

下面将这几个参数逐一为大家做个说明(默认情况下FMC用的HCLK3,主频200MHz)

  •   uint32_t AddressSetupTime

此参数用于设置地址建立时间,单位FMC时钟周期个数,范围0 -15。同步NOR Flash用不到此参数。

  •   uint32_t AddressHoldTime

此参数用于设置地址持续时间,单位FMC时钟周期个数,范围1 -15。同步NOR Flash用不到此参数。

  •   uint32_t DataSetupTime

此参数用于设置数据建立时间,单位FMC时钟周期个数,范围1 -255。用于SRAM,异步多路复用NOR Flash。

  •   uint32_t BusTurnAroundDuration

此参数用于设置总线TurnAround(总线周转阶段)持续时间,单位FMC时钟周期个数,范围0 -15。

仅用于多路复用NOR Flash。

  •   uint32_t CLKDivision

此参数用于设置时钟分频,范围2 -16,仅用于同步器件。

  •   uint32_t DataLatency

对于使能了读/写突发模式的同步访问,此参数定义了读写首个数据前要发送给存储器的时钟周期个数。

    • 操作CRAM,此参数必须为0。
    • 异步NOR/PSRAM/SRAM器件用不到此参数。
    • 使能了同步突发模式的NOR Flash,此参数的范围是2 – 17,单位FMC时钟周期个数。
  •   uint32_t AccessMode

用于设置FMC的访问模式,支持如下四种:

#define FMC_ACCESS_MODE_A                        ((uint32_t)0x00000000U)
#define FMC_ACCESS_MODE_B ((uint32_t)0x10000000U)
#define FMC_ACCESS_MODE_C ((uint32_t)0x20000000U)
#define FMC_ACCESS_MODE_D ((uint32_t)0x30000000)

47.3.4 SRAM句柄结构体SRAM_HandleTypeDef

HAL库在FMC_NORSRAM_TypeDef和FMC_NORSRAM_EXTENDED_TypeDef的基础上封装了一个结构体SRAM_HandleTypeDef,定义如下:

typedef struct
{
FMC_NORSRAM_TypeDef *Instance;
FMC_NORSRAM_EXTENDED_TypeDef *Extended;
FMC_NORSRAM_InitTypeDef Init;
HAL_LockTypeDef Lock;
__IO HAL_SRAM_StateTypeDef State;
MDMA_HandleTypeDef *hmdma;
}SRAM_HandleTypeDef;

下面将这几个参数逐一为大家做个说明。

  •   FMC_NORSRAM_TypeDef  *Instance

用于驱动SRAM设备时的例化,主要用于寄存器FMC_BCRx和FMC_BTRx的配置。

  •   FMC_NORSRAM_EXTENDED_TypeDef  *Extended;

用于驱动SRAM设备时的扩展配置,通过设置寄存器FMC_BWTRx实现。

  •   FMC_NORSRAM_InitTypeDef    Init 

这个参数是用户接触最多的,用于配置FMC外接SRAM或者相同时序设备时的基本参数,像使用的BANK号,存储器位宽,是否使用地址数据复用等。结构体FMC_NORSRAM_InitTypeDef的详细介绍见本章3.3小节。

  •   HAL_LockTypeDef   Lock
  • __IO HAL_SRAM_StateTypeDef  State

这两个变量主要供函数内部使用。Lock用于设置锁状态,而State用于设置FMC状态。

  •   MDMA_HandleTypeDef      *hmdma       

用于关联MDMA句柄。

47.3.5 SDRAM寄存器结构体

FMC控制SDRAM相关的寄存器是通过HAL库中的结构体FMC_SDRAM_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义:

#define FMC_SDRAM_TypeDef     FMC_Bank5_6_TypeDef

typedef struct
{
__IO uint32_t SDCR[]; /*!< SDRAM Control registers , Address offset: 0x140-0x144 */
__IO uint32_t SDTR[]; /*!< SDRAM Timing registers , Address offset: 0x148-0x14C */
__IO uint32_t SDCMR; /*!< SDRAM Command Mode register, Address offset: 0x150 */
__IO uint32_t SDRTR; /*!< SDRAM Refresh Timer register, Address offset: 0x154 */
__IO uint32_t SDSR; /*!< SDRAM Status register, Address offset: 0x158 */
} FMC_Bank5_6_TypeDef;

下面再来看这几个寄存器的具体定义,在stm32h743xx.h文件。

#define PERIPH_BASE           ((uint32_t)0x40000000)
#define D1_AHB1PERIPH_BASE (PERIPH_BASE + 0x12000000)
#define FMC_R_BASE (D1_AHB1PERIPH_BASE + 0x4000) #define FMC_Bank1_R_BASE (FMC_R_BASE + 0x0000)
#define FMC_Bank1E_R_BASE (FMC_R_BASE + 0x0104)
#define FMC_Bank2_R_BASE (FMC_R_BASE + 0x0060)
#define FMC_Bank3_R_BASE (FMC_R_BASE + 0x0080)
#define FMC_Bank5_6_R_BASE (FMC_R_BASE + 0x0140) #define FMC_Bank1 ((FMC_Bank1_TypeDef *) FMC_Bank1_R_BASE)
#define FMC_Bank1E ((FMC_Bank1E_TypeDef *) FMC_Bank1E_R_BASE)
#define FMC_Bank2 ((FMC_Bank2_TypeDef *) FMC_Bank2_R_BASE)
#define FMC_Bank3 ((FMC_Bank3_TypeDef *) FMC_Bank3_R_BASE)
<-----展开下面的宏定义,(FMC_Bank1_TypeDef *) 0x52004140
#define FMC_Bank5_6 ((FMC_Bank5_6_TypeDef *) FMC_Bank5_6_R_BASE) #define FMC_NORSRAM_DEVICE FMC_Bank1
#define FMC_NORSRAM_EXTENDED_DEVICE FMC_Bank1E
#define FMC_NAND_DEVICE FMC_Bank3
#define FMC_SDRAM_DEVICE FMC_Bank5_6

我们访问FMC的SDRTR寄存器可以采用这种形式:FMC->SDRTR= 0。

47.3.6 SDRAM参数配置结构体FMC_SDRAM_InitTypeDef

这个结构体比较重要,FMC驱动SDRAM设备的参数配置,全靠这个结构体实现,具体定义如下:

typedef struct
{
uint32_t SDBank;
uint32_t ColumnBitsNumber;
uint32_t RowBitsNumber;
uint32_t MemoryDataWidth;
uint32_t InternalBankNumber;
uint32_t CASLatency;
uint32_t WriteProtection;
uint32_t SDClockPeriod;
uint32_t ReadBurst;
uint32_t ReadPipeDelay;
}FMC_SDRAM_InitTypeDef;

下面将这几个成员逐一为大家做个介绍:

  •   uint32_t NSBank

用于配置使用的SDRAM BANK,具体支持的参数如下:

#define FMC_SDRAM_BANK1                       ((uint32_t)0x00000000U)
#define FMC_SDRAM_BANK2 ((uint32_t)0x00000001U)
  •   uint32_t ColumnBitsNumber

用于设置列地址的位数,具体支持的参数如下:

#define FMC_SDRAM_COLUMN_BITS_NUM_8           ((uint32_t)0x00000000U)
#define FMC_SDRAM_COLUMN_BITS_NUM_9 ((uint32_t)0x00000001U)
#define FMC_SDRAM_COLUMN_BITS_NUM_10 ((uint32_t)0x00000002U)
#define FMC_SDRAM_COLUMN_BITS_NUM_11 ((uint32_t)0x00000003U)
  •   uint32_t RowBitsNumber

用于设置行地址的位数,具体支持的参数如下:

#define FMC_SDRAM_ROW_BITS_NUM_11             ((uint32_t)0x00000000U)
#define FMC_SDRAM_ROW_BITS_NUM_12 ((uint32_t)0x00000004U)
#define FMC_SDRAM_ROW_BITS_NUM_13 ((uint32_t)0x00000008U)
  •   uint32_t MemoryDataWidth

用于设置SDRAM的数据带宽,具体支持的参数如下:

#define FMC_SDRAM_MEM_BUS_WIDTH_8             ((uint32_t)0x00000000U)
#define FMC_SDRAM_MEM_BUS_WIDTH_16 ((uint32_t)0x00000010U)
#define FMC_SDRAM_MEM_BUS_WIDTH_32 ((uint32_t)0x00000020U)
  •   uint32_t InternalBankNumber

用于设置SDRAM的内部BANK个数,具体支持的参数如下:

#define FMC_SDRAM_INTERN_BANKS_NUM_2          ((uint32_t)0x00000000U)
#define FMC_SDRAM_INTERN_BANKS_NUM_4 ((uint32_t)0x00000040U)
  •   uint32_t CASLatency

用于设置SDRAM的CAS延迟,具体支持的参数如下:

#define FMC_SDRAM_CAS_LATENCY_1               ((uint32_t)0x00000080U)
#define FMC_SDRAM_CAS_LATENCY_2 ((uint32_t)0x00000100U)
#define FMC_SDRAM_CAS_LATENCY_3 ((uint32_t)0x00000180)
  •   uint32_t WriteProtection

用于使能或者禁止SDRAM的写保护,具体支持的参数如下:

#define FMC_SDRAM_WRITE_PROTECTION_DISABLE    ((uint32_t)0x00000000U)
#define FMC_SDRAM_WRITE_PROTECTION_ENABLE ((uint32_t)0x00000200U)
  •   uint32_t SDClockPeriod

用于设置SDRAM的时钟频率,可以选择主频的2分频或者3分频。由于FMC最高支持200MHz,所有SDRAM最高100MHz。此成员具体支持的参数如下:

#define FMC_SDRAM_CLOCK_DISABLE               ((uint32_t)0x00000000U)
#define FMC_SDRAM_CLOCK_PERIOD_2 ((uint32_t)0x00000800U)
#define FMC_SDRAM_CLOCK_PERIOD_3 ((uint32_t)0x00000C00)
  •   uint32_t ReadBurst

此函数用于使能读突发,借助读突发,SDRAM 可以在 CAS 延迟期间接受下一个读命令并将数据存储在读FIFO 中。此成员具体支持的参数如下:

#define FMC_SDRAM_RBURST_DISABLE              ((uint32_t)0x00000000U)
#define FMC_SDRAM_RBURST_ENABLE ((uint32_t)0x00001000U)
  •   uint32_t ReadPipeDela

此成员用于定义在 CAS 延时后多少个SDRAM时钟周期读取数据,具体支持的参数如下:

#define FMC_SDRAM_RPIPE_DELAY_0               ((uint32_t)0x00000000U)
#define FMC_SDRAM_RPIPE_DELAY_1 ((uint32_t)0x00002000U)
#define FMC_SDRAM_RPIPE_DELAY_2 ((uint32_t)0x00004000U)

47.3.7 SDRAM时序配置结构体FMC_SDRAM_TimingTypeDef

此结构体对于配置SDRAM的时序参数,比较重要,具体定义如下:

typedef struct
{
uint32_t LoadToActiveDelay;
uint32_t ExitSelfRefreshDelay;
uint32_t SelfRefreshTime;
uint32_t RowCycleDelay;
uint32_t WriteRecoveryTime;
uint32_t RPDelay;
uint32_t RCDDelay;
}FMC_SDRAM_TimingTypeDef;

下面将这几个参数逐一为大家做个说明(默认情况下FMC用的HCLKx要修改,主频200MHz)

  •   uint32_t LoadToActiveDelay

此成员用于定义加载模式寄存器的命令与激活命令或刷新命令之间的延迟,单位SDRAM时钟周期个数,范围1 – 16。

  •   uint32_t ExitSelfRefreshDelay

此成员用于定义从发出自刷新命令到发出激活命令之间的延迟,单位SDRAM时钟周期个数,范围1 – 16。

  •   uint32_t SelfRefreshTime

此成员用于定义最短的自刷新周期,单位SDRAM时钟周期个数,范围1 – 16。

  •   uint32_t RowCycleDelay

此成员用于定义刷新命令和激活命令之间的延迟,以及两个相邻刷新命令之间的延迟,单位SDRAM时钟周期个数,范围1 – 16。

  •   uint32_t WriteRecoveryTime

此成员用于定义在写命令和预充电命令之间的延迟,单位SDRAM时钟周期个数,范围1 – 16。

  •   uint32_t RPDelay

此成员用于定义预充电命令与其它命令之间的延迟,单位SDRAM时钟周期个数,范围1 – 16。

  •   uint32_t RCDDelay

此成员用于定义激活命令与读/写命令之间的延迟,单位SDRAM时钟周期个数,范围1 – 16。

47.3.8 SDRAM句柄结构体SDRAM_HandleTypeDef

HAL库在FMC_SDRAM_TypeDef和FMC_SDRAM_InitTypeDef的基础上封装了一个结构体SDRAM_HandleTypeDef,定义如下:

typedef struct
{
FMC_SDRAM_TypeDef *Instance;
FMC_SDRAM_InitTypeDef Init;
__IO HAL_SDRAM_StateTypeDef State;
HAL_LockTypeDef Lock;
MDMA_HandleTypeDef *hmdma;
}SDRAM_HandleTypeDef;

下面将这几个参数逐一为大家做个说明。

  •   FMC_SDRAM_TypeDef   *Instance

用于驱动SDRAM设备时的例化,主要用于寄存器配置。

  •   FMC_SDRAM_InitTypeDef  Init;

这个参数是用户接触最多的,用于配置FMC外接SDRAM时的基本参数,像使用的BANK号,存储器位宽,行列个数等。结构体FMC_NORSRAM_InitTypeDef的详细介绍见本章3.6小节。

  •   HAL_LockTypeDef   Lock
  • __IO HAL_SRAM_StateTypeDef  State

这两个变量主要供函数内部使用。Lock用于设置锁状态,而State用于设置FMC状态。

  •   MDMA_HandleTypeDef      *hmdma       

用于关联MDMA句柄。

47.4 源文件stm32h7xx_ll_fmc.c

SRAM类设备的API在源文件stm32h7xx_hal_sram.c文件里面,而SDRAM类设备的API在源文件stm32h7xx_hal_sdram.c文件里面。

这里把我们把如下两个常用到的函数做个说明:

  • HAL_SRAM_Init
  • HAL_SDRAM_Init

47.4.1 函数HAL_SRAM_Init

函数原型:

HAL_StatusTypeDef HAL_SRAM_Init(SRAM_HandleTypeDef *hsram, FMC_NORSRAM_TimingTypeDef *Timing, FMC_NORSRAM_TimingTypeDef *ExtTiming)

函数描述:

此函数用于初始化SRAM类设置,不限制必须是SRAM。只要时序类似,均可使用。

函数参数:

  • 第1个参数是SRAM_HandleTypeDef类型结构体指针变量,用于配置要初始化的参数。结构体变量成员的详细介绍看本章3.4小节。
  • 第2个参数是FMC_NORSRAM_TimingTypeDef类型结构体指针变量,用于配置FMC时序参数。结构体变量成员的详细介绍看本章3.3小节。
  • 第3个参数类型同第2个,用于扩展模式。
  • 返回值,返回HAL_ERROR表示配置失败,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示时间溢出。

使用举例:

/*
*********************************************************************************************************
* 函 数 名: HC574_ConfigFMC
* 功能说明: 配置FMC并口访问时序
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void HC574_ConfigFMC(void)
{
SRAM_HandleTypeDef hsram = {};
FMC_NORSRAM_TimingTypeDef SRAM_Timing = {}; hsram.Instance = FMC_NORSRAM_DEVICE;
hsram.Extended = FMC_NORSRAM_EXTENDED_DEVICE; /* FMC使用的HCLK3,主频200MHz,1个FMC时钟周期就是5ns */
/* SRAM 总线时序配置 4-1-2-1-2-2 不稳定,5-2-2-1-2-2 稳定 */
SRAM_Timing.AddressSetupTime = ; /* 5*5ns=25ns,地址建立时间,范围0 -15个FMC时钟周期个数 */
SRAM_Timing.AddressHoldTime = ; /* 地址保持时间,配置为模式A时,
用不到此参数 范围1 -15个时钟周期个数 */
SRAM_Timing.DataSetupTime = ; /* 2*5ns=10ns,数据保持时间,范围1 -255个时钟周期个数 */
SRAM_Timing.BusTurnAroundDuration = ; /* 此配置用不到这个参数 */
SRAM_Timing.CLKDivision = ; /* 此配置用不到这个参数 */
SRAM_Timing.DataLatency = ; /* 此配置用不到这个参数 */
SRAM_Timing.AccessMode = FMC_ACCESS_MODE_A; /* 配置为模式A */ hsram.Init.NSBank = FMC_NORSRAM_BANK1; /* 使用的BANK1,即使用的片选FMC_NE1 */
hsram.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE; /* 禁止地址数据复用 */
hsram.Init.MemoryType = FMC_MEMORY_TYPE_SRAM; /* 存储器类型SRAM */
hsram.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_32; /* 32位总线宽度 */
hsram.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE; /* 关闭突发模式 */
hsram.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW; /* 用于设置等待信号的极性,关闭突发模式,
此参数无效 */
hsram.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS; /* 关闭突发模式,此参数无效 */
hsram.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE; /* 用于使能或者禁止写保护 */
hsram.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE; /* 关闭突发模式,此参数无效 */
hsram.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE; /* 禁止扩展模式 */
hsram.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE; /* 用于异步传输期间,使能或者禁止等待信
号,这里选择关闭 */
hsram.Init.WriteBurst = FMC_WRITE_BURST_DISABLE; /* 禁止写突发 */
hsram.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY; /* 仅同步模式才做时钟输出 */
hsram.Init.WriteFifo = FMC_WRITE_FIFO_ENABLE; /* 使能写FIFO */ /* 初始化SRAM控制器 */
if (HAL_SRAM_Init(&hsram, &SRAM_Timing, &SRAM_Timing) != HAL_OK)
{
/* 初始化错误 */
Error_Handler(__FILE__, __LINE__);
}
}

47.4.2 函数HAL_SDRAM_Init

函数原型:

HAL_StatusTypeDef HAL_SDRAM_Init(SDRAM_HandleTypeDef *hsdram, FMC_SDRAM_TimingTypeDef *Timing)

函数描述:

此函数主要用于初始化SDRAM。

函数参数:

  • 第1个参数是SDRAM句柄,具体每个成员的介绍看本章3.8小节
  • 第2个参数是FMC_SDRAM_TimingTypeDef类型结构体指针变量,用于配置SDRAM时序参数。结构体变量成员的详细介绍看本章3.7小节
  • 返回值,返回HAL_ERROR表示配置失败,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示时间溢出。

使用举例:

/*
*********************************************************************************************************
* 函 数 名: vTaskLED
* 功能说明: LED闪烁
* 形 参: pvParameters 是在创建该任务时传递的形参
* 返 回 值: 无
* 优 先 级: 2
*********************************************************************************************************
*/
static void vTaskLED(void *pvParameters)
{ /*
*********************************************************************************************************
* 函 数 名: bsp_InitExtSDRAM
* 功能说明: 配置连接外部SDRAM的GPIO和FMC
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitExtSDRAM(void)
{
SDRAM_HandleTypeDef hsdram = {};
FMC_SDRAM_TimingTypeDef SDRAM_Timing = {};
FMC_SDRAM_CommandTypeDef command = {}; /* FMC SDRAM所涉及到GPIO配置 */
SDRAM_GPIOConfig(); /* SDRAM配置 */
hsdram.Instance = FMC_SDRAM_DEVICE; /*
FMC使用的HCLK3时钟,200MHz,用于SDRAM的话,至少2分频,也就是100MHz,即1个SDRAM时钟周期是10ns
下面参数单位均为10ns。
*/
/* 20ns, TMRD定义加载模式寄存器的命令与激活命令或刷新命令之间的延迟 */
SDRAM_Timing.LoadToActiveDelay = ;
SDRAM_Timing.ExitSelfRefreshDelay = ; /* 70ns, TXSR定义从发出自刷新命令到发出激活命令之间的延迟 */
SDRAM_Timing.SelfRefreshTime = ; /* 50ns, TRAS定义最短的自刷新周期 */
SDRAM_Timing.RowCycleDelay = ; /* 70ns, TRC定义刷新命令和激活命令之间的延迟 */
SDRAM_Timing.WriteRecoveryTime = ; /* 20ns, TWR定义在写命令和预充电命令之间的延迟 */
SDRAM_Timing.RPDelay = ; /* 20ns, TRP定义预充电命令与其它命令之间的延迟 */
SDRAM_Timing.RCDDelay = ; /* 20ns, TRCD定义激活命令与读/写命令之间的延迟 */ hsdram.Init.SDBank = FMC_SDRAM_BANK1; /* 硬件设计上用的BANK1 */
hsdram.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9; /* 9列 */
hsdram.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12; /* 12行 */
hsdram.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32; /* 32位带宽 */
hsdram.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4; /* SDRAM有4个BANK */
/* CAS Latency可以设置Latency1,2和3,实际测试Latency3稳定 */
hsdram.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
hsdram.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE; /* 禁止写保护 */
/* FMC时钟200MHz,2分频后给SDRAM,即100MHz */
hsdram.Init.SDClockPeriod = SDCLOCK_PERIOD;
hsdram.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE; /* 使能读突发 */
/* 此位定CAS延时后延后多少个SDRAM时钟周期读取数据,实际测此位可以设置无需延迟 */
hsdram.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0; /* 配置SDRAM控制器基本参数 */
if(HAL_SDRAM_Init(&hsdram, &SDRAM_Timing) != HAL_OK)
{
/* Initialization Error */
Error_Handler(__FILE__, __LINE__);
} /* 完成SDRAM序列初始化 */
SDRAM_Initialization_Sequence(&hsdram, &command);
}

47.5 总结

本章节就为大家讲解这么多,FMC涉及到的知识点比较多,而且实际项目中用到的地方也比较多,望初学者熟练运用。