STM32 LCD与FSMC学习

STM32 LCD屏幕学习

写于2024/8/25日中午 该文章仅供自己学习参考


image-20240825133947556

1. OLED简介

显示器举例优点缺点
断码屏数码管、计算器、遥控器成本低,驱动简单,稳定色彩单一,显示内容少
点阵屏户外广告屏任意尺寸,亮度高贵,耗电,体积大
LCD屏显示器、电视屏、手机屏成本低,色彩好,薄,寿命长全彩稍差,漏光,拖影
OLED屏显示器、电视屏、手机屏自发光,色彩最好,超薄,功耗低比较贵,寿命短

1.1 LCD基本组成

  • 玻璃基板
  • 背光
  • 驱动IC

image-20240825134536909

1.2 LCD分类

image-20240825135005591

如下:

  • 按信号类型分为 TTL/LVDS/EDP/MIPI 几大类别
  • 按材质分类分为(针对 TFT-LCD) TFT-TN/TFT-IPS/TFT-VA。
  • 接口类型分为:RGB 模式、SPI 模式、MDDI 模式、VSYNC 模式、DSI 模式、MCU 模式等
分类类别说明
信号类型TTL(RGB)TTL电平信号,属于并行接口,包含RGB信号、时钟和控制信号
信号类型LVDSLow-Voltage Differential Signaling,低压差分信号技术
信号类型EDPEmbedded DisplayPort,适用于平板、笔记本电脑、一体机、未来高分辨率手机,传输速率比LVDS快
信号类型MIPIMobile Industry Processor Interface,移动产业处理接口,差分传输
材质TFT-TN液晶分子排列和通电扭转方式不同,TN采用正性液晶
材质TFT-IPS同等级IPS和VA材质屏的效果要优于TN材质的
材质TFT-VA液晶分子排列和通电扭转方式不同,VA采用负性液晶
接口类型RGB模式16/18/24位并行接口,一般还包括VSYNC,HSYNC,DOTCLK,CS,RST
接口类型MDDI模式高通2004年提出的,连线
接口类型VSYNC模式该模式是在MCU模式上加了一个VSYNC信号,在这种模式下,内部的显示操作与外部VSYNC信号同步,可以实现比内部操作更高速率的动画显示
接口类型DSI模式该模式串行的双向高速命令传输模式,连线有D0P,D0N,D1P,D1N,CLKP,CLKN
接口类型MCU模式英特尔I8080和摩托罗拉M6800两种接口

其中

MCU≤800*480带SRAM,无需频繁刷新,无需大内存,驱动简单
RGB≤1280*800不带SRAM,需要实时刷新,需要大内存,驱动稍微复杂
MIPI4K不带SRAM,支持分辨率高,省电,大部分手机屏用此接口

2. 液晶显示控制器(ILI9341)

TFTLCD 模块其驱动芯片有:ILI9341/ST7789/NT35310/NT35510/SSD1963等,我学习的这块LCD采用ILI9341芯片,下面学习也会围绕此芯片进行。

ILI9341 液晶控制器自带显存,可配置支持 8/9/16/18 位的总线中的一种,可以通过 3/4 线串行协议或 8080 并口驱动。正点原子的 TFT-LCD 模块上的电路配置为 8080 并口方式,其显存总大小为 172800(24032018/8),即 18 位模式(26 万色)下的显存量。在 16 位模式下,ILI9341 采用 RGB565 格式存储颜色数据,此时 ILI9341 的 18 位显存与 MCU 的 16 位数据线以及 RGB565 的对应关系如图 25.1.2.1 所示:

image-20240825140112946

从图中可以看出,ILI9341 在 16 位模式下面,18 位显存的 B0 和 B12 并没有用到,对外的数据线使用 DB0-DB15 连接 MCU 的 D0-D15 实现 16 位颜色的传输。MCU 的 16 位数据,最低 5 位代表蓝色,中间 6 位为绿色,最高 5 位为红色。数值越大,表示该颜色越深。另外,特别注意 ILI9341 所有的指令都是 8 位的(高 8 位无效),且参数除了读写 GRAM 的时候是 16 位,其他操作参数,都是 8 位的。

2.1 芯片功能框图

image-20240825143250623

2.2 接口逻辑信号

Pin NameI/o类型描述
IM[3:0]I(VDDI/VSS)设定MCU接口模式,8-bits,9-bits,16bits,18b-bits 的8080-I/8080-II串行MCU接口等
RESXIMCU(VDDI/VSS)低电平有效。将重置设备
EXTCIMCU(VDDI/VSS)扩展命令集有效,LOW无效,HIGH有效。把EXTC连到VDDI才能读写扩展寄存器(RB0hRCFh,RE0hRFFh)
CSXIMCU(VDDI/VSS)低电平有效,芯片片选信号。此脚 只能在MPU接口模式下永久固定在“LOW”电平上。并行接口模式中,如果CSX连接VSS,则显示模块不会出现异常可见效果。此外,也不会限制使用并行读写协议,电源开关序列或其他功能。当CSX='1’时,对并行和串行接口没有影响。
D/CX(SCL)IMCU(VDDI/VSS)该引脚用于在并行接口或4线8位串行数据接口中选择“数据或命令”。DCX=1时数据被选择,DCX=0时命令被选择。此引脚在3线9bit或4线8bit串行数据接口中被作为串行接口时钟信号
RDXIMCU(VDDI/VSS)8080-I/8080-II系统(RDX):作为一个读取信号,在其上升沿时MCU读取数据。如果不使用时,把RDX接到VDDI电平上
WRX(D/CX)IMCU(VDDI/VSS)8080-I/8080-II系统(WRX):做为一个写入信号,当在其上升沿时写入数据。4线系统(D/CX):作为命令或参数选择不用时接VDDI电平上
D[17:0]I/oMCU(VDDI/VSS)18bits 双向并行数据总线,用于MCU系统和RGB接口模式
SDI/SDAOMCU(VDDI/VSS)当IM[3]=0时,串口模式下该脚为in/out信号(SDA);当IM[3]=1时,串口模式下该脚为in模式(SDI);该数据适用于SCL信号的上升沿。如果不用时,该脚需要接VDDI或VSS
SDOOMCU(VDDI/VSS)串口输出信号。数据是在SCL信号下降沿输出。不例用时,需悬空
TEOMCU(VDDI/VSS)撕裂效果输出引脚用于同步MPU的帧输出,由S/W命令激活。当这个引脚没有激活时,输出低电平。如果没使用,需悬空该引脚
DOTCLKIMCU(VDDI/VSS)RGB接口操作的点时钟信号。不使用把该引脚引到VDDI或VSS电平上。
VSYNCIMCU(VDDI/VSS)RGB接口操作 帧同步信号。不用时把该引脚连接到VDDI或VSS电平上。
HSYNCIMCU(VDDI/VSS)RGB接口操作的线同步信号。不用时把该引脚连接到VDDI或VSS电平上。
DEIMCU(VDDI/VSS)RGB接口操作的数据使能信号。不用时把该引脚连接到VDDI或VSS电平上。

其中IM[3:0]引脚取值来选择接口模式

在这里插入图片描述

2.3 时序逻辑

8080-I/8080-II串并接口,通过D[17:0]数据引脚实现寄存器的存取

在这里插入图片描述

写周期为例,8080 方式下的操作时序如图

image-20240825144840505

TFLCD模块引脚为

image-20240825145128285

通过原理图对比可操控对应的GPIO口输出正确的写时序和读时序

2.4 指令格式

指令(HEX)名称作用
0xD3读ID用于读取LCD控制器的ID,区分型号用
0x36访问控制设置GRAM读写方向,控制显示方向
0x2A列地址一般用于设置X坐标
0x2B页地址一般用于设置Y坐标
0x2C写GRAM用于往LCD写GRAM数据
0x2E读GRAM用于读取LCD的GRAM数据
2.4.1 读ID(0xD3)

image-202408251531063770xD3 指令后面跟了 4 个参数,最后 2 个参数,读出来是 0x93 和 0x41,刚好是我们控制器 ILI9341 的数字部分,从而,通过该指令,即可判别所用的 LCD 驱动器是什么型号,这样,我们的代码,就可以根据控制器的型号去执行对应驱动 IC 的初始化代码,从而兼容不同驱动 IC 的屏,使得一个代码支持多款 LCD。

值得一提的是,该指令发出后读出的第一个参数为假读,是没有意义的,丢弃即可。

2.4.2 访问控制(0x36)

image-20240825153031555从上表可以看出,0x36 指令后面,紧跟一个参数,这里主要关注:MY、MX、MV 这三个位,通过这三个位的设置,我们可以控制整个 ILI9341 的全部扫描方向,如表 25.1.2.4 所示:

image-20240825153054129

2.4.3 X坐标设置指令(0X2A)

image-20240825153019538SC:起始坐标

EC:结束坐标

0≤SP≤EP≤319(LCD像素高度)

2.4.4 Y坐标设置指令(0X2B)

image-20240825153000312SP:起始坐标

EP:结束坐标

0≤SP≤EP≤319(LCD像素高度)

2.4.5 写GRAM指令(0X2C)

image-20240825152945890由表 25.1.2.7 可知,在收到指令 0X2C 之后,数据有效位宽变为 16 位,我们可以连续写入LCD GRAM 值,而 GRAM 的地址将根据 MY/MX/MV 设置的扫描方向进行自增。例如:假设设置的是从左到右,从上到下的扫描方式,那么设置好起始坐标(通过 SC,SP 设置)后,每写入一个颜色值,GRAM 地址将会自动自增 1(SC++),如果碰到 EC,则回到 SC,同时 SP++,一直到坐标:EC,EP 结束,期间无需再次设置的坐标,从而大大提高写入速度。

2.4.6 读GRAM指令(0X2E)

image-20240825152919221

该指令用于读取 GRAM,如表 25.1.2.7 所示,ILI9341 在收到该指令后

第一次输出的是dummy 数据,也就是无效的数据

第二次开始,读取到的才是有效的 GRAM 数据(从坐标:SC,SP 开始),输出规律为:每个颜色分量占 8 个位,一次输出 2 个颜色分量。比如:第一次输出是 R1G1,随后的规律为:B1R2→G2B2→R3G3→B3R4→G4B4→R5G5…以此类推。

如果我们只需要读取一个点的颜色值,那么只需要接收到参数 3 即可,如果要连续读取(利用 GRAM地址自增,方法同上),那么就按照上述规律去接收颜色数据

2.4.7 代码实例
uint16_t  lcd_rd_data(void)
{
	uint16_t ram;  			/* 定义变量 */
	DATA_IN_MODE();			/* 设置数据输入 */
 	LCD_RS(1);              /* 操作数据 */
    LCD_CS(0);				/* 选中 */
    LCD_RD(0);				/* RD低电平 */
   	ram = LCD_DATA_IN;    	/* 读取数据 */
    LCD_RD(1);				/* RD高电平 */
    LCD_CS(1);				/* 释放片选 */
	DATA_OUT_MODE();		/* 设置数据输出 */
	return ram;				/* 返回读数 */
}

uint16_t  lcd_read_point (uint16_t x, uint16_t y)
{
	uint16_t r = 0, g = 0, b = 0;	/* 定义变量 */
	lcd_set_cursor(x, y);			/* 设置坐标 */
 	lcd_wr_regno(0X2E);				/* 发读点命令 */
 	r = lcd_rd_data();  			/* 假读 */
	r = lcd_rd_data();  			/* 读rg */
	b = lcd_rd_data(); 				/* 读b */
    g = r & 0XFF;       			/* 得到g值 */
	return (((r >> 11) << 11) | ((g >> 2) << 5) | (b >> 11));

3. 使用FSMC控制LCD

3.1 FSMC简介

ILI9341 的 8080 通讯接口时序可以由 STM32 使用 GPIO 接口进行模拟,但这样效率太低,STM32 提供了一种更高效的控制方法——使用 FSMC 接口实现 8080 时序,但 FSMC 是 STM32片上外设的一种,并非所有的 STM32 都拥有这种硬件接口,使用何种方式驱动需要在芯片选型时就确定好。我们的开发板支持 FSMC 接口,下面我们来了解一下这个接口的功能。

FSMC,即灵活的静态存储控制器,能够与同步或异步存储器和 16 位 PC 存储器卡连接,FSMC 接口可以通过地址信号,快速地找到存储器对应存储块上的数据。STM32F1 的 FSMC 接口支持包括 SRAM、NAND FLASH、NOR FLASH 和 PSRAM 等存储器。F1 系列的大容量型号,且引脚数目在 100 脚及以上的 STM32F103 芯片都带有 FSMC 接口,正点原子战舰STM32F103 的主芯片为 STM32F103ZET6,是带有 FSMC 接口的。

3.2 程序框图

image-20240826235234885

3.3 FMC通信引脚介绍

我们可以看出,STM32 的 FSMC 可以驱动 NOR/PSRAM、NAND、PC 卡这 3类设备,他们具有不同的 CS 以区分不同的设备。本部分我们要用到的是 NOR/PSRAM 的功能。

①为 FSMC 的总线和时钟源

②为 STM32 内部的 FSMC 控制单元

③是连接硬件的引脚

④是 NOR/PSRAM 会使用到的信号控制线,

这里的“公共信号”表示不论我们驱动前面提到的 3 种设备中的哪种,这些 IO 是共享的,所以如果需要用到多种功能的情况,程序上还要考虑分时复用。③和④这些信号比较重要,它们的功能如表

image-20240826235403397

3.4 FMC地址映射

STM32F1 的 FSMC 将外部存储器划分为固定大小为 256M 字节的四个存储块,FSMC 的外部设备地址映像如图 25.1.3.2 所示:

image-20240826235625794

从上图可以看出,FSMC 总共管理 1GB 空间,拥有 4 个存储块(Bank),FSMC 各 Bank 配置寄存器如表 25.1.3.2:

image-20240826235649754

本章,我们用到的是块 1,所以在本章我们仅讨论块 1 的相关配置

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 个区进行寻址。如表 25.1.3.3 所示:

image-20240826235948650

3.5 HADDR与FMC_A关系

FSMC不同位宽操作

HADDR总线是转换到外部存储器的内部AHB地址线,简单来说,从CPU通过AHB总线到外部信号线之间的关系。

HADDR是字节地址,而存储器访问不都是按字节访问,接到存储器的地址线与其数据宽度相关。

image-20240827000333452

image-20240827000517360

这里要注意的是FSMC中每一个地址,对应的是存储器中的一个字节

假设我们用BANK1的区1,那么0x6000 0000-0x63ff ffff (共64M个字节byte地址) 对应的就是存储器的地址就是64Mx8=512M地址

也就是FSMC的64M个bit地址映射着存储器64M个字节byte地址

那也就是默认情况下存储器地址数据为8位,可以正常读取,但是如果存储器地址数据为16位,32位,就是两个字节一个地址,四个字节一个地址,那么控制16位,32位宽度的存储设备,且不支持单字节访问就比较麻烦了。比如16位宽度的NOR Flash,写入仅支持16位或者32位(通过写入两次16位),写入8位数据是不支持的 。

这样的话,我们本来对应64M的字节byte地址,就变成了32M的双字节byte地址,也就是最后一位无效了,因为地址一次要加2

这个时候,为了方便操作,FMC在硬件设计上就提供了一种解决办法,将内部数据总线ADDR[25:0]措位后接到FMC_A地址引脚上,“丢弃”地址的bit-0,从bit-1开始对地址计数(相当于只计算偶数地址,总共有32M个地址)

FSMC在实际输出地址时,将地址的值右移一位(相当于除以2,变成了偶数地址),输出到实际的地址线上。

当 Bank1 接的是 16 位宽度存储器的时候:HADDR[25:1]→FSMC_A[24:0]。

当 Bank1 接的是 8 位宽度存储器的时候:HADDR[25:0] →FSMC_A[25:0]。

不论外部接8位/16 位宽设备,FSMC_A[0]永远接在外部设备地址A[0]

这里,TFTLCD使用的是 16 位数据宽度,所以 HADDR[0]并没有用到,只有 HADDR[25:1]是有效的,对应关系变为:HADDR[25:1]→FSMC_A[24:0],相当于右移了一位。具体来说,比如地址:0x7E,对应二进制是:01111110,此时 FSMC_A6 是 0 而不是 1,因为要右移一位,这里请特别注意。

另外,HADDR[27:26]的设置,是不需要我们干预的,比如:当你选择使用 Bank1 的第三个区,即使用 FSMC_NE3 来连接外部设备的时候,即对应了 HADDR[27:26]=10,我们要做的就是配置对应第 3 区的寄存器组,来适应外部设备即可。对于 NOR FLASH 控制器,主要是通过FSMC_BCRx、FSMC_BTRx 和 FSMC_BWTRx 寄存器设置(其中 x=1~4,对应 4 个区)。通过这 3 个寄存器,可以设置 FSMC 访问外部存储器的时序参数,拓宽了可选用的外部存储器的速度范围。

3.6 FMC时序介绍

FMC是Flexible灵活的,可以产生多种时序来控制外部存储器**。FSMC 外设挂载在 AHB 总线上,时钟信号来自于 HCLK(默认 72MHz),控制器的同步时钟输出就是由它分频得到。**我们控制LCD主要应用FMC的异步时序

3.6.1时序参数设置

在这里插入图片描述

  • 地址建立(ADDSET),地址保存(ADDHLD),数据建立(DATAST) 这三个设置是异步存储器专属的
  • 总线周转是每次操作的间隔时间
  • **时钟分频比(CLKDIV)和数据延迟(DATLAT)**是同步存储器的
3.6.1 同步时序

FSMC 的 NORFLASH 控制器支持同步和异步突发两种访问方式。选用同步突发访问方式时,FSMC 将 HCLK(系统时钟)分频后,发送给外部存储器作为同步时钟信号 FSMC_CLK。

此时需要的设置的时间参数有 2 个:

  1. HCLK与FSMC_CLK的分频系数(CLKDIV),可以为2~16分频;

  2. 同步突发访问中获得第1个数据所需要的等待延迟(DATLAT)。

3.6.2 异步时序

对于异步突发访问方式,FSMC 主要设置 3 个时间参数:

  • 地址建立时间(ADDSET)

  • 数据建立时间(DATAST)

  • 地址保持时间(ADDHLD)。

  • FSMC 综合了 SRAM/ROM、PSRAM 和 NOR Flash 产品的信号特点,定义了 4 种不同的异步时序模型。选用不同的时序模型时,需要设置不同的时序参数,如表 25.1.3.4 所列:

image-20240827001433158

在实际扩展时,根据选用存储器的特征确定时序模型,从而确定各时间参数与存储器读/写周期参数指标之间的计算关系;利用该计算关系和存储芯片数据手册中给定的参数指标,可计算出 FSMC 所需要的各时间参数,从而对时间参数寄存器进行合理的配置。

模式A支持独立的读写时序控制。这个对我们驱动TFTLCD来说非常有用,因为TFTLCD在读的时候,一般比较慢,而在写的时候可以比较快,如果读写用一样的时序,那么只能以读的时序为基准,从而导致写的速度变慢,或者在读数据的时候,重新配置FSMC的延时,在读操作完成的时候,再配置回写的时序,这样虽然也不会降低写的速度,但是频繁配置,比较麻烦。而如果有独立的读写时序控制,那么我们只要初始化的时候配置好,之后就不用再配置,既可以满足速度要求,又不需要频繁改配置。

模式A与模式1的区别是NOE的变化和相互独立的读写时序

image-20240827001955355

写时序类似,区别是它的一个存储器操作周期仅由地址建立周期(ADDSET)和数据建立周期(DATAST)组成,且在数据建立周期期间写使能信号线发出写信号,接着 FSMC 把数据通过数据线传输到存储器中。

image-20240827002009103

图 25.1.3.3 和图 25.1.3.4 中的 ADDSET 与 DATAST,是通过不同的寄存器设置的。

以读时序为例,该图表示一个存储器读操作周期由地址建立周期(ADDSET)、**数据建立周期(DATAST)**以及 2 个 HCLK 周期组成。

在地址建立周期中,地址线发出要访问的地址,数据掩码信号线指示出要读取地址的高、低字节部分,片选信号使能存储器芯片;
地址建立周期结束后读使能信号线发出读使能信号,接着存储器通过数据信号线把目标数据传输给 FSMC,FSMC 把它交给内核。

3.7 FSMC寄存器介绍

寄存器名称作用
FMC_BCRx(x=1~4)片选控制寄存器包含存储器块的信息(存储器类型/数据宽度等)
FMC_BTR1x(x=1~4)片选时序寄存器设置读操作时序参数(ADDSET/DATAST)
FMC_BWTR1x(x=1~4)写时序寄存器设置写操作时序参数(ADDSET/DATAST)
3.7.1 FMC_BCRx 片选控制寄存器

image-20240827011910903

  • EXTMOD:扩展模式使能位,也就是是否允许读写不同的时序,很明显,我们本章需要读写不同的时序,故该位需要设置为 1。

  • WREN:写使能位。我们需要向 TFTLCD 写数据,故该位必须设置为 1。

  • MWID[1:0]:存储器数据总线宽度。我们的 TFTLCD 是 16 位数据线,所以设置该值为 01。

  • MTYP[1:0]:存储器类型。前面提到,我们把 TFTLCD 当成 SRAM 用,所以需要设置该值为 00。

  • MBKEN:存储块使能位。这个容易理解,我们需要用到该存储块控制 TFTLCD,当然要使能这个存储块了。

3.7.2 FSMC_BTRx 片选时序寄存器

image-20240827012643810

这个寄存器包含了每个存储器块的控制信息,可以用于SRAM、ROM和NOR闪存存储器。

如果FSMC_BCRx寄存器中设置了EXTMOD位,则有两个时序寄存器分别对应读(本寄存器)和写操作(FSMC_BWTRx寄存器)。因为我们要求读写分开时序控制,所以EXTMOD是使能了的,也就是本寄存器是读操作时序寄存器,控制读操作的相关时序。本章我们要用到的设置有:ACCMOD、DATAST和ADDSET这三个设置。

  • ACCMOD[1:0]:访问模式。本章我们用到模式A,故设置为00。
  • DATAST[7:0]:数据保持时间。
  • ADDSET[3:0]:地址建立时间。

DATAST数据保持时间。对 ILI9341 来说,其实就是 RD 低电平持续时间,最小为355ns。而一个 HCLK 时钟周期为 13.9ns 左右(1/72Mhz),为了兼容其他屏,我们这里设置DATAST 为 15,也就是 16 个 HCLK 周期,时间大约是 222ns(未计算数据存储的 2 个 HCLK时间,对 9341 来说超频了,但是实际上是可以正常使用的)

ADDSET[3:0]:地址建立时间。其建立时间为:ADDSET 个 HCLK 周期,最大为 15 个 HCLK周期。对 ILI9341 来说,这里相当于 RD 高电平持续时间,为 90ns,本来这里我们应该设置和DATAST 一样,但是由于 STM32F103 FSMC 的性能问题,就算设置 ADDSET 为 0,RD 的高电平持续时间也超过 90ns。所有,我们这里可以设置 ADDSET 为较小的值,本章我们设置 ADDSET为 1,即 2 个 HCLK 周期。

3.7.3 FSMC_BWTRx写时序寄存器

image-20240827015152418

该寄存器在本章用作写操作时序控制寄存器,需要用到的设置同样是:ACCMOD、DATAST和 ADDSET 这三个设置。这三个设置的方法同 FSMC_BTRx 一模一样,只是这里对应的是写操作的时序

3.8 FMC寄存器组合说明

在 MDK 的寄存器定义里面,并没有定义 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]保留,没有用到。

3.6 流程总结

当 FSMC 外设被配置成正常工作,并且外部接了 NOR FLASH 时,若向 0x60000000 地址写入数据如 0xABCD,FSMC 会自动在各信号线上产生相应的电平信号,写入数据。FSMC 会控制片选信号 NE1 输出低电平选择相应的 NOR 芯片,然后使用地址线 A[25:0]输出0x60000000,在 NWE 写使能信号线上发出低电平的写使能信号,而要写入的数据信号0xABCD 则从数据线 D[15:0]输出,然后数据就被保存到 NOR FLASH 中了。

4. 代码实例

4.1 FSMC初始化顺序

这里我们先说下FSMC的总体初始化顺序:

  1. FSMC硬件层初始化:使能 FSMC 和 GPIO 时钟,初始化 IO 口配置,设置映射关系
  2. 设置FSMC接口句柄初始化
  3. 设置FSMC读时序/写时序初始化

STM32技术手册中,针对FSMC引脚的GPIO模式配置,已经进行了说明,具体FSMC总线配置方法如下。下表指的是整个FSMC接口的引脚初始化配置说明,应该根据实际初始化相关的引脚

在这里插入图片描述

FSMC 接口支持多种存储器,包括 SDRAM,NOR,NAND 和 PC CARD等。HAL 库为每种支持的存储器类型都定义了一个独立的 HAL 库文件,并且在文件中定义了独立的初始化函数,这里我们就列出几种存储器的初始化函数:

HAL_SRAM_Init();//SRAM 初始化函数,省略入口参数
HAL_SDRAM_Init();//SDRAM 初始化函数,省略入口参数
HAL_NOR_Init();//NOR 初始化函数,省略入口参数
HAL_NAND_Init();//NAND 初始化函数,省略入口参数

我们以异步SRAM,hal库来说下具体的初始化流程

4.2 FSMC硬件层初始化

STM32的管脚排列很没有规律,而且分布在多个不同端口上,初始化要十分小心.需要用到的引脚都要先初始化成”复用功能推挽输出”模式.(GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP )并且开启时钟 (RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE); )使能 FSMC 和 GPIO 时钟,初始化 IO 口配置,设置映射关系

 void FSMC_SRAM_MspInit(void)
{
  GPIO_InitTypeDef GPIO_Init_Structure;

  /* Enable FMC clock */
  __HAL_RCC_FSMC_CLK_ENABLE();
  
  /* Enable GPIOs clock */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOG_CLK_ENABLE();
  
  /* Common GPIO configuration */
  GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
  GPIO_Init_Structure.Pull      = GPIO_PULLUP;
  GPIO_Init_Structure.Speed     = GPIO_SPEED_FREQ_HIGH;
  
  /* 地址线:F0---F5,F12---F15,G0---G5,D11---D13;
     数据信号线;D0,D1,D8---D10,D14,D15,E7---E15;
     CS片选:G10;
     WE写使能:D5;
     OE读使能;D4;
     UB数据掩码;E1;
     LB数据掩码;E0;
  */
  
  /* GPIOD configuration */
  GPIO_Init_Structure.Pin   = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | 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(GPIOD, &GPIO_Init_Structure);

  /* GPIOE configuration */  
  GPIO_Init_Structure.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_Structure);
  
  /* GPIOF configuration */  
  GPIO_Init_Structure.Pin   = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2| GPIO_PIN_3 | GPIO_PIN_4     |\
                              GPIO_PIN_5 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
  HAL_GPIO_Init(GPIOF, &GPIO_Init_Structure);
  
  /* GPIOG configuration */  
  GPIO_Init_Structure.Pin   = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2| GPIO_PIN_3 | GPIO_PIN_4     |\
                              GPIO_PIN_5 | GPIO_PIN_10;
  
  HAL_GPIO_Init(GPIOG, &GPIO_Init_Structure);   
}


在完成硬件的MSP初始化之后,就要初始化SRAM了,使用的函数为 HAL_SRAM_Init()

 HAL_SRAM_Init(SRAM_HandleTypeDef *hsram, 
               FSMC_NORSRAM_TimingTypeDef *Timing, 
               FSMC_NORSRAM_TimingTypeDef *ExtTiming;

该函数有三个入口参数,

  • 第一个参数 hsram,它是SRAM_HandleTypeDef 结构体指针类型 设置SRAM的基本参数
  • 第二个参数 Timing 设置NORSRAM存储器的读时序
  • 第三个参数 ExtTiming 设置NORSRAM存储器的写时序

4.3 FMC 接口句柄初始化

我们使用的结构体为:SRAM_HandleTypeDef

结构体 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;
 DMA_HandleTypeDef *hdma; 
}SRAM_HandleTypeDef;


  • Instance: 寄存器基地址,如果读写时序一样则配置基地址就行
  • Extended 扩展模式寄存器基地址 也就是读写时序不同的时候, 指定写操作时序寄存器地址
  • FMC_NORSRAM_InitTypeDef Init 是 FSMC_NORSRAM_InitTypeDef 结构体指针类型,是真正用来设置 SRAM 控制接口参数的
  • 成员变量 Lock 和 State 是 HAL 库处理状态标识变量
  • 成员变量 hdma 在使用 DMA 时候才使用

也就是我们只需要关心前三个参数,最主要的是第三个Init ,我们来看下:

 /** 
 * @brief FSMC NOR/SRAM Init structure definition
 */
 typedef struct
 {
 uint32_t NSBank; /*设置要控制的 Bank 区域 */
 uint32_t DataAddressMux; /*设置地址总线与数据总线是否复用 */
 uint32_t MemoryType; /*设置存储器的类型 */
 uint32_t MemoryDataWidth; /*设置存储器的数据宽度*/
 uint32_t BurstAccessMode; /*设置是否支持突发访问模式,只支持同步类型的存储器 */ 
 uint32_t WaitSignalPolarity; /*设置等待信号的极性*/
 uint32_t WrapMode; /*设置是否支持对齐的突发模式 */
 uint32_t WaitSignalActive; /*配置等待信号在等待前有效还是等待期间有效 */
 uint32_t WriteOperation; /*设置是否写使能 */
 uint32_t WaitSignal; /*设置是否使能等待状态插入 */
 uint32_t ExtendedMode; /*设置是否使能扩展模式 */
 uint32_t WriteBurst; /*设置是否使能写突发操作*/
 uint32_t AsynchronousWait; /*设置是否使能等待信号*/
 uint32_t PageSize; /*指定页的大小*/

 } FSMC_NORSRAM_InitTypeDef;

NSBank 用来指定使用到的存储块区号,

DataAddressMux 用来设置是否使能地址/数据复用,该变量仅对 NOR/PSRAM 有效

MemoryType 用来设置存储器类型

MemoryDataWidth用来设置存储器数据总线宽度,可选 8 位还是 16 位,

WriteOperation 用来设置存储器写使能,也就是是否允许写入。毫无疑问我们会进行存储器写操作

ExtendedMode 用来设置是否使能扩展模式,也就是是否允许读写使用不同时序

ContinuousClock 用来设置启用/禁止 FSMC 时钟输出到外部存储设备 ,

其他参数 WriteBurst,BurstAccessMode,WaitSignalPolarity,WrapMode,WaitSignalActive,WaitSignal,AsynchronousWait 等是用在突发访问和同步时序情况下,这里我们不做过多讲解。

4.4 设置FSMC读时序/写时序初始化

2个参数Timing和ExtTiming,它们都是FSMC_NORSRAM_TimingTypeDef结构体指针类型,分别用来设置 FMC 接口读和写时序,主要涉及地址建立保持时间,数据建立时间等等配置,
如果是异步存储器,读写时序不一样,读写速度要求不一样,参数Timing 和 ExtTiming 需要设置了不同的值。
如果是同步存储器,读写时序设置一致就可

FMC_NORSRAM_TimingTypeDef 结构体定义如下:

typedef struct
{
 uint32_t AddressSetupTime; //地址建立时间 
 uint32_t AddressHoldTime; //地址保持时间 
 uint32_t DataSetupTime; //数据建立时间 
 uint32_t BusTurnAroundDuration;   //总线恢复时间
 uint32_t CLKDivision;  // 时钟分频因子
 uint32_t DataLatency; //同步突发 NOR FLASH 的数据延迟 
 uint32_t AccessMode; //异步模式配置 
}FSMC_NORSRAM_TimingTypeDef;

4.5 最终初始化代码


/**
  * @brief  Initializes the SRAM device.
  * @retval SRAM status
  */
void FSMC_SRAM_Init(void)
{ 
	
	//结构体句柄初始化
	SRAM_HandleTypeDef  SRAM_Handler;

	FSMC_NORSRAM_TimingTypeDef FSMC_ReadWriteTim;
	FSMC_NORSRAM_TimingTypeDef FSMC_WriteTim;
	
	
	
	//FSMC初始化寄存器
	SRAM_Handler.Instance = FSMC_NORSRAM_DEVICE;
	SRAM_Handler.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
  

	SRAM_Handler.Init.NSBank=FSMC_NORSRAM_BANK4;     					        //使用NE4
	SRAM_Handler.Init.DataAddressMux=FSMC_DATA_ADDRESS_MUX_DISABLE; 	//地址/数据线不复用
	SRAM_Handler.Init.MemoryType=FSMC_MEMORY_TYPE_SRAM;   				    //SRAM
	SRAM_Handler.Init.MemoryDataWidth=FSMC_NORSRAM_MEM_BUS_WIDTH_16; 	//16位数据宽度
	SRAM_Handler.Init.BurstAccessMode=FSMC_BURST_ACCESS_MODE_DISABLE; //是否使能突发访问,仅对同步突发存储器有效,此处未用到
	SRAM_Handler.Init.WaitSignalPolarity=FSMC_WAIT_SIGNAL_POLARITY_LOW;//等待信号的极性,仅在突发模式访问下有用
	SRAM_Handler.Init.WaitSignalActive=FSMC_WAIT_TIMING_BEFORE_WS;   	//存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT
	SRAM_Handler.Init.WriteOperation=FSMC_WRITE_OPERATION_ENABLE;    	//存储器写使能
	SRAM_Handler.Init.WaitSignal=FSMC_WAIT_SIGNAL_DISABLE;           	//等待使能位,此处未用到
	SRAM_Handler.Init.ExtendedMode=FSMC_EXTENDED_MODE_DISABLE;        	//读写使用相同的时序
	SRAM_Handler.Init.AsynchronousWait=FSMC_ASYNCHRONOUS_WAIT_DISABLE;	//是否使能同步传输模式下的等待信号,此处未用到
	SRAM_Handler.Init.WriteBurst=FSMC_WRITE_BURST_DISABLE;           	  //禁止突发写
  
  
  
  	//FSMC读时序控制寄存器
	FSMC_ReadWriteTim.AddressSetupTime=0x06;       	//地址建立时间(ADDSET)为7个HCLK 13.8ns*7=96.6ns
	FSMC_ReadWriteTim.AddressHoldTime=0;
	FSMC_ReadWriteTim.DataSetupTime=26;				//数据保存时间为27个HCLK	=13.8*27=372.6ns
	FSMC_ReadWriteTim.AccessMode=FSMC_ACCESS_MODE_A;//模式A
	//FSMC写时序控制寄存器
	FSMC_WriteTim.BusTurnAroundDuration=0;			//总线周转阶段持续时间为0,此变量不赋值的话会莫名其妙的自动修改为4。导致程序运行正常
	FSMC_WriteTim.AddressSetupTime=3;          		//地址建立时间(ADDSET)为4个HCLK =55.2ns 
	FSMC_WriteTim.AddressHoldTime=0;
	FSMC_WriteTim.DataSetupTime=0x06;              	//数据保存时间为13.8ns*7个HCLK=96.6ns
	FSMC_WriteTim.AccessMode=FSMC_ACCESS_MODE_A;    //模式A
  
  
  
  
  /* SRAM controller initialization */
  FSMC_SRAM_MspInit();
  HAL_SRAM_Init(& SRAM_Handler, &FSMC_ReadWriteTim, &FSMC_WriteTim);

}
		
  • 17
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值