FSMC 简介
STM32F103 100 引脚以上系列芯片都带有 FSMC 接口,我们开发板上使用的是 STM32F103ZET6,因此也具有FSMC 接口。
FSMC(Flexible Static Memory Controller,灵活的静态存储控制器)是
STM32 系列采用一种新型的存储器扩展技术,能够连接同步、异步存储器和 16位 PC 存储卡。STM32通过FSMC可以与SRAM、 ROM、 PSRAM、 NOR Flash和NANDFlash等存储器的引脚直接相连。STM32F1的FSMC内部框图如下图所示:(大家也可以查看 《STM32F10x中文参考手册》-19 灵活的静态存储控制器(FSMC)-19.2 章节内容)
我们把 FSMC 结构框图分成3 个子模块,按照顺序依次进行简单介绍。
(1)标号 1:时钟输入
FSMC 的时钟来至时钟控制器HCLK,在前面我们讲解“存储器与寄存器”时,我们知道,AHB 区域内包含 FSMC 模块,所以如果要使用 FSMC,必须使能 AHB 总线时钟。
(2)标号 2:AHB 接口
CPU 和其它 AHB 总线主设备可通过该 AHB 从设备接口访问外部静态存储
器。FSMC 可通过一个寄存器组进行配置。有关 NOR Flash/PSRAM 控制寄存器的详细说明,请参《STM32F10x 中文参考手册》-19 灵活的静态存储控制器(FSMC)-19.5章节内容。
(3)FSMC 外部设备
STM32F1 的 FSMC 将外部设备分为 2 类:NOR/PSRAM 设备、NAND/PC 卡设备。他们共用地址数据总线等信号,但具有不同的 CS 以区分不同的设备。
本实验我们使用的是FSMC 的 NOR/PSRAM 存储器控制器部分,即把TFTLCD当成 SRAM 设备使用。为什么可以把 TFTLCD 当成 SRAM 设备用,这个首先要了解 NOR/PSRAM 存储器控制器的接口信号,其接口信号功能如下:
从上图中可以看出外部 SRAM 的控制一般有:地址线(如 A0~A25)、双向数据线(如 D0~D15)、写信号( NWE)、读信号( NOE)、片选信号(NE[x]),如果 SRAM 支持字节控制,那么还有 UB/LB 信号。而 TFTLCD 的信号我们在上一节有介绍,包括:RS、DB0-DB15、 WR、 RD、 CS、 RST 等,其中真正在操作 LCD 的时候需要用到的就只有:RS、 DB0-DB15、 WR、 RD、 CS。这样一来它们的操作接口信号完全类似,唯一不同就是 TFTLCD 有 RS 信号,但是没有地址信号。
TFTLCD 通过 RS 信号来决定传送的数据是数据还是命令,本质上可以理解
为一个地址信号,比如我们把 RS 接在 A0 上面,那么当 FSMC 控制器写地址 0的时候,会使得 A0 变为 0,对 TFTLCD 来说,就是写命令。而 FSMC 写地址 1的时候, A0 将会变为 1,对 TFTLCD 来说,就是写数据了。这样,就把数据和命令区分开了,他们其实就是对应 SRAM 操作的两个连续地址。当然 RS 也可以接在其他地址线上,我们STM32F1 开发板是把 RS 连接在 A10 上面的。
知道了可以将 TFTLCD 当做 SRAM 设备用,下面我们就来看下 FSMC 的外部设备地址映射,从 FSMC 的角度,外部存储器被划分为 4 个固定大小的存储区域(Bank),每个存储区域的大小为 256 MB,共 1GB 空间。如下图所示:
EEWORLDIMGTK2
本实验使用到的是Bank1,所以我们只讲解这块存储区域,其他的区域大家可参考《STM32F10x 中文参考手册》-19 灵活的静态存储控制器(FSMC)章节内容。
存储区域 1 可连接多达 4 个 NOR Flash 或 PSRAM 存储器器件。此存储区域被划分为 4 个 NOR/PSRAM 区域, 带 4 个专用片选信号。存储区域 2 和 3 用于连接 NAND Flash 器件(每个存储区域一个器件)。存储区域 4 用于连接 PC卡设备。对于每个存储区域, 所要使用的存储器类型由用户在配置寄存器中定义。
STM32F1 的 FSMC 各 Bank 配置寄存器如下图所示:
EEWORLDIMGTK3
STM32F1 的 FSMC 存储块 1 ( Bank1) 又被分为 4 个区, 每个区管理 64M 字节空间,每个区都有独立的寄存器对所连接的存储器进行配置。 Bank1 的 256M字节空间由 28 根地址线 (HADDR[27:0]) 寻址。 这里 HADDR 是内部 AHB 地址 总线, 其 中 HADDR[25:0] 来自外部存储器地 址 FSMC_A[25:0], 而 HADDR[26:27]对 4 个区进行寻址。如下图所示:
本实验我们使用的是 Bank1 的第 4 区,即起始地址为 0X6C000000。这里要特别注意 HADDR[25:0], HADDR[25:0]包含外部存储器地址。由于 HADDR 为字节地址,而存储器按字寻址,所以根据存储器数据宽度不同,实际向存储器发送的地址也将有所不同,如下图所示:
如果外部存储器的宽度为 8 位, FSMC 将使用内部的 HADDR[25:0] 地址来作为对外部存储器的寻址地址 FSMC_A[25:0]。
这里请大家特别留意,如果外部存储器的宽度为 16 位, FSMC 将使用内部的 HADDR[25:1] 地址来作为对外部存储器的寻址地址 FSMC_A[24:0],相当于右移了一位,在后面我们设置 A10 地址的时候就要使用到。无论外部存储器的宽度为 16 位还是 8 位, FSMC_A[0] 都应连接到外部存储器地址 A[0]。
另外,HADDR[27:26]的设置,是不需要我们干预的,比如:当你选择使用Bank1 的第三个区,即使用 FSMC_NE3 来连接外部设备的时候,即对应了
HADDR[27:26]=10,我们要做的就是配置对应第 3 区的寄存器组,来适应外部设备即可。FSMC 的各 Bank配置寄存器在上图 38.1.2.3 以列出。
对于 NOR FLASH 控制器,主要是通过 FSMC_BCRx、 FSMC_BTRx 和
FSMC_BWTRx 寄存器设置(其中 x=1~4,对应 4 个区)。通过这 3 个寄存器,可以设置 FSMC 访问外部存储器的时序参数, 拓宽了可选用的外部存储器的速度范围。
FSMC 的 NOR FLASH 控制器支持同步和异步突发两种访问方式。选用同步突发访问方式时, FSMC 将 HCLK(系统时钟)分频后,发送给外部存储器作为同步时钟信号 FSMC_CLK。此时需要的设置的时间参数有 2 个:
①HCLK 与 FSMC_CLK 的分频系数(CLKDIV),可以为 2~16 分频;
②同步突发访问中获得第 1 个数据所需要的等待延迟(DATLAT)。
对于异步突发访问方式, FSMC 主要设置 3 个时间参数:地址建立时间
(ADDSET)、 数据建立时间(DATAST)和地址保持时间(ADDHLD)。FSMC 综合了 SRAM/ROM、 PSRAM 和 NORFlash 产品的信号特点,定义了 4 种不同的异步时序模型。选用不同的时序模型时,需要设置不同的时序参数,如下图所列:
在实际扩展时,根据选用存储器的特征确定时序模型,从而确定各时间参数与存储器读/写周期参数指标之间的计算关系;利用该计算关系和存储芯片数据手册中给定的参数指标,可计算出 FSMC 所需要的各时间参数,从而对时间参数寄存器进行合理的配置。
本实验我们使用异步模式 A( ModeA)方式来控制 TFTLCD,模式 A 的读操作时序如下图所示:
模式 A 支持独立的读写时序控制, 这个对我们驱动 TFTLCD 来说非常有用,因为 TFTLCD 在读的时候,一般比较慢,而在写的时候可以比较快,如果读写用一样的时序,那么只能以读的时序为基准,从而导致写的速度变慢,或者在读数据的时候,重新配置 FSMC 的延时,在读操作完成的时候,再配置回写的时序,这样虽然也不会降低写的速度,但是频繁配置,比较麻烦。而如果有独立的读写时序控制,那么我们只要初始化的时候配置好,之后就不用再配置,既可以满足速度要求,又不需要频繁改配置。模式 A 的写操作时序如下图所示:
模式 A 读写时序中的 ADDSET 与 DATAST,是通过不同的寄存器设置的。由于篇幅限制, 本文并没有对 FSMC 相关寄存器进行介绍, 大家可以参考 《STM32F10x中文参考手册》-19 灵活的静态存储控制器(FSMC)章节寄存器内容,里面有详细的讲解。不过,这里还要给大家做下科普,在标准库的寄存器定义里面,并没有定义 FSMC_BCRx、 FSMC_BTRx、 FSMC_BWTRx 等这个单独的寄存器,而是将他们进行了一些组合。
FSMC_BCRx 和 FSMC_BTRx, 组合成 BTCR[8]寄存器组, 他们的对应关系如下:
BTCR[0]对应 FSMC_BCR1, BTCR[1]对应 FSMC_BTR1
BTCR[2]对应 FSMC_BCR2, BTCR[3]对应 FSMC_BTR2
BTCR[4]对应 FSMC_BCR3, BTCR[5]对应 FSMC_BTR3
BTCR[6]对应 FSMC_BCR4, BTCR[7]对应 FSMC_BTR4
FSMC_BWTRx 则组合成 BWTR[7],他们的对应关系如下:
BWTR[0]对应 FSMC_BWTR1, BWTR[2]对应 FSMC_BWTR2,
BWTR[4]对应 FSMC_BWTR3, BWTR[6]对应 FSMC_BWTR4,
BWTR[1]、 BWTR[3]和 BWTR[5]保留,没有用到。
FSMC 内部还是比较复杂的,如果看不懂的可以暂时放下,因为我们使用的是库函数开发,只需简单配置下即可使用。