首页 » 互联网 » 探索者 STM32F407 开拓板资料连载第十八章 TFTLCD 显示实验_存放器_函数

探索者 STM32F407 开拓板资料连载第十八章 TFTLCD 显示实验_存放器_函数

少女玫瑰心 2025-01-07 03:47:27 0

扫一扫用手机浏览

文章目录 [+]

2)摘自《STM32F7 开拓指南(HAL 库版)》关注官方微旗子暗记公众年夜众号,获取更多资料:正点原子

第十八章 TFTLCD 显示实验

探索者 STM32F407 开拓板资料连载第十八章 TFTLCD 显示实验_存放器_函数 探索者 STM32F407 开拓板资料连载第十八章 TFTLCD 显示实验_存放器_函数 互联网

上一章我们先容了 OLED 模块及其显示,但是该模块只能显示单色/双色,不能显示彩色,

探索者 STM32F407 开拓板资料连载第十八章 TFTLCD 显示实验_存放器_函数 探索者 STM32F407 开拓板资料连载第十八章 TFTLCD 显示实验_存放器_函数 互联网
(图片来自网络侵删)

而且尺寸也较小。
本章我们将先容 ALIENTEK 2.8 寸 TFT LCD 模块,该模块采取 TFTLCD 面

板,可以显示 16 位色的真彩图片。
在本章中,我们将利用探索者 STM32F4 开拓板上的 LCD

接口,来点亮 TFTLCD,并实现 ASCII 字符和彩色的显示等功能,并在串口打印 LCD 掌握器

ID,同时在 LCD 上面显示。
本章分为如下几个部分:

18.1 TFTLCD & FSMC 简介

18.2 硬件设计

18.3 软件设计

18.4 下载验证

18.1 TFTLCD&FSMC 简介

本章我们将通过STM32F4的FSMC接口来掌握TFTLCD的显示,以是本节分为两个部分,

分别先容 TFTLCD 和 FSMC。

18.1.1 TFTLCD 简介

TFT-LCD 即薄膜晶体管液晶显示器。
其英文全称为:Thin Film Transistor-Liquid Crystal

Display。
TFT-LCD 与无源 TN-LCD、STN-LCD 的大略矩阵不同,它在液晶显示屏的每一个象

素上都设置有一个薄膜晶体管(TFT),可有效地战胜非选通时的串扰,使显示液晶屏的静态特

性与扫描线数无关,因此大大提高了图像质量。
TFT-LCD 也被叫做真彩液晶显示器。

上一章先容了 OLED 模块,本章,我们给大家先容 ALIENTEK TFTLCD 模块,该模块有

如下特点:

1,2.4’/2.8’/3.5’/4.3’/7’ 5 种大小的屏幕可选。

2,320×240 的分辨率(3.5’分辨率为:320480,4.3’和 7’分辨率为:800480)。

3,16 位真彩显示。

4,自带触摸屏,可以用来作为掌握输入。

本章,我们以 2.8 寸(其他 3.5 寸/4.3 寸等 LCD 方法类似,请参考 2.8 的即可)的 ALIENTEK

TFTLCD 模块为例先容,该模块支持 65K 色显示,显示分辨率为 320×240,接口为 16 位的 80

并口,自带触摸屏。

该模块的外不雅观图如图 18.1.1.1 所示:

图 18.1.1.1 ALIENTEK 2.8 寸 TFTLCD 外不雅观图

模块事理图如图 18.1.1.2 所示:

图 18.1.1.2 ALIENTEK 2.8 寸 TFTLCD 模块事理图

TFTLCD 模块采取 217 的 2.54 公排针与外部连接,接口定义如图 18.1.1.3 所示:

图 18.1.1.3 ALIENTEK 2.8 寸 TFTLCD 模块接口图

从图 18.1.1.3 可以看出,ALIENTEK TFTLCD 模块采取 16 位的并办法与外部连接,之以是

不采取 8 位的办法,是由于彩屏的数据量比较大,尤其在显示图片的时候,如果用 8 位数据线,

就会比 16 位办法慢一倍以上,我们当然希望速率越快越好,以是我们选择 16 位的接口。

18.1.1.3 还列出了触摸屏芯片的接口,关于触摸屏本章我们不多先容,后面的章节会有详细的介

绍。
该模块的 80 并口有如下一些旗子暗记线:

CS:TFTLCD 片选旗子暗记。

WR:向 TFTLCD 写入数据。

RD:从 TFTLCD 读取数据。

D[15:0]:16 位双向数据线。

RST:硬复位 TFTLCD。

RS:命令/数据标志(0,读写命令;1,读写数据)。

80 并口在上一节我们已经有详细的先容了,这里我们就不再先容,须要解释的是,TFTLCD

模块的 RST 旗子暗记线是直接接到 STM32F4 的复位脚上,并不由软件掌握,这样可以省下来一个

IO 口。
其余我们还须要一个背光掌握线来掌握 TFTLCD 的背光。
以是,我们统共须要的 IO 口

数目为 21 个。
这里还须要把稳,我们标注的 DB1~DB8,DB10~DB17,是相对付 LCD 掌握 IC

标注的,实际上大家可以把他们就等同于 D0~D15,这样理解起来就比较大略一点。

ALIENTEK 供应 2.8/3.5/4.3/7 寸平分歧尺寸的 TFTLCD 模块,其驱动芯片有很多种类型,

比如有:ILI9341/ILI9325/RM68042/RM68021/ILI9320/ILI9328/LGDP4531/LGDP4535/SPFD5408

/SSD1289/1505/B505/C505/NT35310/NT35510/SSD1963 等(详细的型号,大家可以通过下载本章

实验代码,通过串口或者 LCD 显示查看),这里我们仅以 ILI9341 掌握器为例进行先容,其他

的掌握基本都类似,我们就不详细阐述了。

ILI9341 液晶掌握器自带显存,其显存总大小为 172800(24032018/8),即 18 位模式(26

万色)下的显存量。
在 16 位模式下,ILI9341 采取 RGB565 格式存储颜色数据,此时 ILI9341

的 18 位数据线与 MCU 的 16 位数据线以及 LCD GRAM 的对应关系如图 18.1.1.4 所示:

图 18.1.1.4 16 位数据与显存对应关系图

从图中可以看出,ILI9341 在 16 位模式下面,数据线有用的是:D17~D13 和 D11~D1,D0

和 D12 没有用到,实际上在我们 LCD 模块里面,ILI9341 的 D0 和 D12 压根就没有引出来,这

样,ILI9341 的 D17~D13 和 D11~D1 对应 MCU 的 D15~D0。

这样 MCU 的 16 位数据,最低 5 位代表蓝色,中间 6 位为绿色,最高 5 位为赤色。
数值越

大,表示该颜色越深。
其余,特殊把稳 ILI9341 所有的指令都是 8 位的(高 8 位无效),且参数

除了读写 GRAM 的时候是 16 位,其他操作参数,都是 8 位的,这个和 ILI9320 等驱动器不一

样,必须加以把稳。

接下来,我们先容一下 ILI9341 的几个主要命令,由于 ILI9341 的命令很多,我们这里就

不全部先容了,有兴趣的大家可以找到 ILI9341 的 datasheet 看看。
里面对这些命令有详细的介

绍。
我们将先容:0XD3,0X36,0X2A,0X2B,0X2C,0X2E 等 6 条指令。

首先来看指令:0XD3,这个是读 ID4 指令,用于读取 LCD 掌握器的 ID,该指令如表 18.1.1.1

所示:

表 18.1.1.1 0XD3 指令描述

从上表可以看出,0XD3 指令后面跟了 4 个参数,末了 2 个参数,读出来是 0X93 和 0X41,

刚好是我们掌握器 ILI9341 的数字部分,从而,通过该指令,即可判别所用的 LCD 驱动器是什

么型号,这样,我们的代码,就可以根据掌握器的型号去实行对应驱动 IC 的初始化代码,从而

兼容不同驱动 IC 的屏,使得一个代码支持多款 LCD。

接下来看指令:0X36,这是存储访问掌握指令,可以掌握 ILI9341 存储器的读写方向,简

单的说,便是在连续写 GRAM 的时候,可以掌握 GRAM 指针的增长方向,从而掌握显示办法

(读 GRAM 也是一样)。
该指令如表 18.1.1.2 所示:

表 18.1.1.2 0X36 指令描述

从上表可以看出,0X36 指令后面,紧跟一个参数,这里我们紧张关注:MY、MX、MV

这三个位,通过这三个位的设置,我们可以掌握全体 ILI9341 的全部扫描方向,如表 18.1.1.3

所示:

表 18.1.1.3 MY、MX、MV 设置与 LCD 扫描方向关系表

这样,我们在利用 ILI9341 显示内容的时候,就有很大灵巧性了,比如显示 BMP 图片,

BMP 解码数据,便是从图片的左下角开始,逐步显示到右上角,如果设置 LCD 扫描方向为从

左到右,从下到上,那么我们只须要设置一次坐标,然后就一直的往 LCD 添补颜色数据即可,

这样可以大大提高显示速率。

接下来看指令:0X2A,这是列地址设置指令,在从左到右,从上到下的扫描办法(默认)

下面,该指令用于设置横坐标(x 坐标),该指令如表 18.1.1.4 所示:

表 18.1.1.4 0X2A 指令描述

在默认扫描办法时,该指令用于设置 x 坐标,该指令带有 4 个参数,实际上是 2 个坐标值:

SC 和 EC,即列地址的起始值和结束值,SC 必须小于即是 EC,且 0≤SC/EC≤239。
一样平常在设

置 x 坐标的时候,我们只须要带 2 个参数即可,也便是设置 SC 即可,由于如果 EC 没有变革,

我们只须要设置一次即可(在初始化 ILI9341 的时候设置),从而提高速率。

与 0X2A 指令类似,指令:0X2B,是页地址设置指令,在从左到右,从上到下的扫描办法

(默认)下面,该指令用于设置纵坐标(y 坐标)。
该指令如表 18.1.1.5 所示:

表 18.1.1.5 0X2B 指令描述

在默认扫描办法时,该指令用于设置 y 坐标,该指令带有 4 个参数,实际上是 2 个坐标值:

SP 和 EP,即页地址的起始值和结束值,SP 必须小于即是 EP,且 0≤SP/EP≤319。
一样平常在设置

y 坐标的时候,我们只须要带 2 个参数即可,也便是设置 SP 即可,由于如果 EP 没有变革,我

们只须要设置一次即可(在初始化 ILI9341 的时候设置),从而提高速率。

接下来看指令:0X2C,该指令是写 GRAM 指令,在发送该指令之后,我们便可以往 LCD

的 GRAM 里面写入颜色数据了,该指令支持连续写,指令描述如表 18.1.1.6 所示:

表 18.1.1.6 0X2C 指令描述

从上表可知,在收到指令 0X2C 之后,数据有效位宽变为 16 位,我们可以连续写入 LCD

GRAM 值,而 GRAM 的地址将根据 MY/MX/MV 设置的扫描方向进行自增。
例如:假设设置

的是从左到右,从上到下的扫描办法,那么设置好起始坐标(通过 SC,SP 设置)后,每写入

一个颜色值,GRAM 地址将会自动自增 1(SC++),如果碰到 EC,则回到 SC,同时 SP++,一

直到坐标:EC,EP 结束,其间无需再次设置的坐标,从而大大提高写入速率。

末了,来看看指令:0X2E,该指令是读 GRAM 指令,用于读取 ILI9341 的显存(GRAM),

该指令在 ILI9341 的数据手册上面的描述是有误的,真实的输出情形如表 18.1.1.7 所示:

表 18.1.1.7 0X2E 指令描述

该指令用于读取 GRAM,如表 18.1.1.7 所示,ILI9341 在收到该指令后,第一次输出的是

dummy 数据,也便是无效的数据,第二次开始,读取到的才是有效的 GRAM 数据(从坐标:

SC,SP 开始),输出规律为:每个颜色分量占 8 个位,一次输出 2 个颜色分量。
比如:第一次

输出是 R1G1,随后的规律为:B1R2→G2B2→R3G3→B3R4→G4B4→R5G5... 以此类推。
如果

我们只须要读取一个点的颜色值,那么只须要吸收到参数 3 即可,如果要连续读取(利用 GRAM

地址自增,方法同上),那么就按照上述规律去吸收颜色数据。

以上,便是操作 ILI9341 常用的几个指令,通过这几个指令,我们便可以很好的掌握 ILI9341

显示我们所要显示的内容了。

一样平常 TFTLCD 模块的利用流程如图 18.1.1.5:

图 18.1.1.5 TFTLCD 利用流程

任何 LCD,利用流程都可以大略的用以上流程图表示。
个中硬复位和初始化序列,只须要

实行一次即可。
而画点流程便是:设置坐标→写 GRAM 指令→写入颜色数据,然后在 LCD 上

面,我们就可以看到对应的点显示我们写入的颜色了。
读点流程为:设置坐标→读 GRAM 指令

→读取颜色数据,这样就可以获取到对应点的颜色数据了。

以上只是最大略的操作,也是最常用的操作,有了这些操作,一样平常就可以正常利用 TFTLCD

了。
接下来我们将该模块用来来显示字符和数字,通过以上先容,我们可以得出 TFTLCD 显示

须要的干系设置步骤如下:

1)设置 STM32F4 与 TFTLCD 模块相连接的 IO。

这一步,先将我们与 TFTLCD 模块相连的 IO 口进行初始化,以便驱动 LCD。
这里我们用

到的是 FSMC,FSMC 将在 18.1.2 节向大家详细先容。

2)初始化 TFTLCD 模块。

即图 18.1.1.5 的初始化序列,这里我们没有硬复位 LCD,由于探索者 STM32F4 开拓板的

LCD 接口,将 TFTLCD 的 RST 同 STM32F4 的 RESET 连接在一起了,只要按下开拓板的 RESET

键,就会对 LCD 进行硬复位。
初始化序列,便是向 LCD 掌握器写入一系列的设置值(比如伽

马校准),这些初始化序列一样平常 LCD 供应商会供应给客户,我们直策应用这些序列即可,不需

要深入研究。
在初始化之后,LCD 才可以正常利用。

3)通过函数将字符和数字显示到 TFTLCD 模块上。

这一步则通过图 18.1.1.5 左侧的流程,即:设置坐标→写 GRAM 指令→写 GRAM 来实现,

但是这个步骤,只是一个点的处理,我们要显示字符/数字,就必须要多次利用这个步骤,从而

达到显示字符/数字的目的,以是须要设计一个函数来实现数字/字符的显示,之后调用该函数,

就可以实现数字/字符的显示了。

18.1.2 FSMC 简介

STM32F407 或 STM32F417 系列芯片都带有 FSMC 接口,ALIENTEK 探索者 STM32F4 开

发板的主芯片为 STM32F407ZGT6,是带有 FSMC 接口的。

FSMC,即灵巧的静态存储掌握器,能够与同步或异步存储器和 16 位 PC 存储器卡连接,

STM32F4 的 FSMC 接口支持包括 SRAM、NAND FLASH、NOR FLASH 和 PSRAM 等存储器。

FSMC 的框图如图 18.1.2.1 所示:

图 18.1.2.1 FSMC 框图

从上图我们可以看出,STM32F4 的 FSMC 将外部设备分为 2 类:NOR/PSRAM 设备、

NAND/PC 卡设备。
他们共用地址数据总线等旗子暗记,他们具有不同的 CS 以区分不同的设备,比

如本章我们用到的 TFTLCD 便是用的 FSMC_NE4 做片选,实在便是将 TFTLCD 当成 SRAM 来

掌握。

这里我们先容下为什么可以把 TFTLCD 当成 SRAM 设备用:首先我们理解下外部 SRAM

的连接,外部 SRAM 的掌握一样平常有:地址线(如 A0~A18)、数据线(如 D0~D15)、写旗子暗记(WE)、

读旗子暗记(OE)、片选旗子暗记(CS),如果 SRAM 支持字节掌握,那么还有 UB/LB 旗子暗记。
而 TFTLCD

的旗子暗记我们在 18.1.1 节有先容,包括:RS、D0~D15、WR、RD、CS、RST 和 BL 等,个中真

正在操作 LCD 的时候须要用到的就只有:RS、D0~D15、WR、RD 和 CS。
其操作时序和 SRAM

的掌握完备类似,唯一不同便是 TFTLCD 有 RS 旗子暗记,但是没有地址旗子暗记。

TFTLCD 通过 RS 旗子暗记来决定传送的数据是数据还是命令,实质上可以理解为一个地址信

号,比如我们把 RS 接在 A0 上面,那么当 FSMC 掌握器写地址 0 的时候,会使得 A0 变为 0,

对 TFTLCD 来说,便是写命令。
而 FSMC 写地址 1 的时候,A0 将会变为 1,对 TFTLCD 来说,

便是写数据了。
这样,就把数据和命令区分开了,他们实在便是对应 SRAM 操作的两个连续地

址。
当然 RS 也可以接在其他地址线上,探索者 STM32F4 开拓板是把 RS 连接在 A6 上面的。

STM32F4 的 FSMC 支持 8/16/32 位数据宽度,我们这里用到的 LCD 是 16 位宽度的,以是

在设置的时候,选择 16 位宽就 OK 了。
我们再来看看 FSMC 的外部设备地址映像,STM32F4

的 FSMC 将外部存储器划分为固定大小为 256M 字节的四个存储块,如图 18.1.2.2 所示:

图 18.1.2.2 FSMC 存储块地址映像

从上图可以看出,FSMC 统共管理 1GB 空间,拥有 4 个存储块(Bank),本章,我们用到

的是块 1,以是在本章我们仅谈论块 1 的干系配置,其他块的配置,请参考《STM32F4xx 中文

参考手册》第 32 章(1191 页)的干系先容。

STM32F4 的 FSMC 存储块 1(Bank1)被分为 4 个区,每个区管理 64M 字节空间,每个区

都有独立的寄存器对所连接的存储器进行配置。
Bank1 的 256M 字节空间由 28 根地址线

(HADDR[27:0])寻址。

这 里 HADDR 是内 部 AHB 地址总 线,其 中 HADDR[25:0]来自外部存储器地址

FSMC_A[25:0],而 HADDR[26:27]对 4 个区进行寻址。
如表 18.1.2.1 所示:

表 18.1.2.1 Bank1 存储区选择表

表 18.1.2.1 中,我们要特殊把稳 HADDR[25:0]的对应关系:

当 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],相称于右移了一位,这里请大家特殊留神。
其余,

HADDR[27:26]的设置,是不须要我们干预的,比如:当你选择利用 Bank1 的第三个区,纵然

用 FSMC_NE3 来连接外部设备的时候,即对应了 HADDR[27:26]=10,我们要做的便是配置对

应第 3区的寄存器组,来适应外部设备即可。
STM32F4 的 FSMC 各Bank配置寄存器如表 18.1.2.2

所示:

表 18.1.2.2 FSMC 各 Bank 配置寄存器表

对付 NOR FLASH 掌握器,紧张是通过 FSMC_BCRx、FSMC_BTRx 和 FSMC_BWTRx 寄

存器设置(个中 x=1~4,对应 4 个区)。
通过这 3 个寄存器,可以设置 FSMC 访问外部存储器

的时序参数,拓宽了可选用的外部存储器的速率范围。
FSMC 的 NOR FLASH 掌握器支持同步

和异步突发两种访问办法。
选用同步突发访问办法时,FSMC 将 HCLK(系统时钟)分频后,发

送给外部存储器作为同步时钟旗子暗记 FSMC_CLK。
此时须要的设置的韶光参数有 2 个:

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

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

对付异步突发访问办法,FSMC 紧张设置 3 个韶光参数:地址建立韶光(ADDSET)、数据

建立韶光(DATAST)和地址保持韶光(ADDHLD)。
FSMC 综合了 SRAM/ROM、PSRAM 和 NOR

Flash 产品的旗子暗记特点,定义了 4 种不同的异步时序模型。
选用不同的时序模型时,须要设置不

同的时序参数,如表 18.1.2.3 所列:

表 18.1.2.3 NOR FLASH 掌握器支持的时序模型

在实际扩展时,根据选用存储器的特色确定时序模型,从而确定各韶光参数与存储器读/

写周期参数指标之间的打算关系;利用该打算关系和存储芯片数据手册中给定的参数指标,可

打算出 FSMC 所须要的各韶光参数,从而对韶光参数寄存器进行合理的配置。

本章,我们利用异步模式 A(ModeA)办法来掌握 TFTLCD,模式 A 的读操作时序如图

18.1.2.3 所示:

图 18.1.2.3 模式 A 读操作时序图

模式 A 支持独立的读写时序掌握,这个对我们驱动 TFTLCD 来说非常有用,由于 TFTLCD

在读的时候,一样平常比较慢,而在写的时候可以比较快,如果读写用一样的时序,那么只能以读

的时序为基准,从而导致写的速率变慢,或者在读数据的时候,重新配置 FSMC 的延时,在读

操作完成的时候,再配置回写的时序,这样虽然也不会降落写的速率,但是频繁配置,比较麻

烦。
而如果有独立的读写时序掌握,那么我们只要初始化的时候配置好,之后就不用再配置,

既可以知足速率哀求,又不须要频繁改配置。

模式 A 的写操作时序如图 18.1.2.4 所示:

图 18.1.2.4 模式 A 写操作时序

图 18.1.2.3 和图 18.1.2.4 中的 ADDSET 与 DATAST,是通过不同的寄存器设置的,接下来

我们讲解一下 Bank1 的几个掌握寄存器

首先,我们先容 SRAM/NOR 闪存片选掌握寄存器:FSMC_BCRx(x=1~4),该寄存器各位

描述如图 18.1.2.5 所示:

图 18.1.2.5 FSMC_BCRx 寄存器各位描述

该寄存器我们在本章用到的设置有:EXTMOD、WREN、MWID、MTYP 和 MBKEN 这几

个设置,我们将逐个先容。

EXTMOD:扩展模式使能位,也便是是否许可读写不同的时序,很明显,我们本章须要读

写不同的时序,故该位须要设置为 1。

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

MWID[1:0]:存储器数据总线宽度。
00,表示 8 位数据模式;01 表示 16 位数据模式;10

和 11 保留。
我们的 TFTLCD 是 16 位数据线,以是设置 WMID[1:0]=01。

MTYP[1:0]:存储器类型。
00 表示 SRAM、ROM;01 表示 PSRAM;10 表示 NOR FLASH;11

保留。
前面提到,我们把 TFTLCD 当成 SRAM 用,以是须要设置 MTYP[1:0]=00。

MBKEN:存储块使能位。
这个随意马虎理解,我们须要用到该存储块掌握 TFTLCD,当然要

使能这个存储块了。

接下来,我们看看 SRAM/NOR 闪存片选时序寄存器:FSMC_BTRx(x=1~4),该寄存器各

位描述如图 18.1.2.6 所示:

图 18.1.2.6 FSMC_BTRx 寄存器各位描述

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

如果 FSMC_BCRx 寄存器中设置了 EXTMOD 位,则有两个时序寄存器分别对应读(本寄存器)

和写操作(FSMC_BWTRx 寄存器)。
由于我们哀求读写分开时序掌握,以是 EXTMOD 是使能了

的,也便是本寄存器是读操作时序寄存器,掌握读操作的干系时序。
本章我们要用到的设置有:

ACCMOD、DATAST 和 ADDSET 这三个设置。

ACCMOD[1:0]:访问模式。
00 表示访问模式 A;01 表示访问模式 B;10 表示访问模式 C;

11 表示访问模式 D,本章我们用到模式 A,故设置为 00。

DATAST[7:0]:数据保持韶光。
0 为保留设置,其他设置则代表保持韶光为: DATAST 个

HCLK 时钟周期,最大为 255 个 HCLK 周期。
对 ILI9341 来说,实在便是 RD 低电平持续韶光,

一样平常为 355ns。
而一个 HCLK 时钟周期为 6ns 旁边(1/168Mhz),为了兼容其他屏,我们这里设

置 DATAST 为 60,也便是 60 个 HCLK 周期,韶光大约是 360ns。

ADDSET[3:0]:地址建立韶光。
其建立韶光为:ADDSET 个 HCLK 周期,最大为 15 个 HCLK

周期。
对 ILI9341 来说,这里相称于 RD 高电平持续韶光,为 90ns,我们设置 ADDSET 为 15,

即 156=90ns。

末了,我们再来看看 SRAM/NOR 闪写时序寄存器:FSMC_BWTRx(x=1~4),该寄存器各

位描述如图 18.1.2.7 所示:

图 18.1.2.7 FSMC_BWTRx 寄存器各位描述

该寄存器在本章用作写操作时序掌握寄存器,须要用到的设置同样是:ACCMOD、DATAST

和 ADDSET 这三个设置。
这三个设置的方法同 FSMC_BTRx 千篇一律,只是这里对应的是写

操作的时序,ACCMOD 设置同 FSMC_BTRx 千篇一律,同样是选择模式 A,其余 DATAST 和

ADDSET 则对应低电平和高电平持续韶光,对 ILI9341 来说,这两个韶光只须要 15ns 就够了,

比读操作快得多。
以是我们这里设置 DATAST 为 2,即 3 个 HCLK 周期,韶光约为 18ns。
然后

ADDSET 设置为 3,即 3 个 HCLK 周期,韶光为 18ns。

至此,我们对 STM32F4 的 FSMC 先容就差不多了,通过以上两个小节的理解,我们可以

开始写 LCD 的驱动代码了。
不过,这里还要给大家做下科普,在 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]保留,没有用到。

通过上面的讲解,通过对 FSMC 干系的寄存器的描述,大家对 FSMC 的事理有了一个初步

的认识,如果还不熟习的朋友,请一定要搜索网络资料理解 FSMC 的事理。
只有理解了事理,

利用库函数才可以得心应手。
那么在库函数中是怎么实现 FSMC 的配置的呢?FSMC_BCRx,

FSMC_BTRx 寄存器在库函数是通过什么函数来配置的呢?下面我们来讲解一下 FSMC 干系的

库函数:

FSMC 初始化函数

根据前面的讲解,初始化 FSMC 紧张是初始化三个寄存器 FSMC_BCRx,FSMC_BTRx,

FSMC_BWTRx,在 HAL 库中供应了 FSMC 初始化函数为

HAL_SRAM_Init ();

下面我们看看函数定义:

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

这 个 函 数 有 三 个 入 口 参 数 , SRAM_HandleTypeDef 类 型 指 针 变 量 、

FMC_NORSRAM_TimingTypeDef 类型指针变量、FMC_NORSRAM_TimingTypeDef 类型指针变

量。

FMC_NORSRAM_TimingTypeDef 指针类型的成员变量。
前面我们讲到,FSMC 有读时序

和写时序之分,以是这里便是用来设置读时序和写时序的参数了, 也便是说,这两个参数是用

来配置寄存器 FSMC_BTRx 和 FSMC_BWTRx,后面我们会讲解到。
下面我们紧张来看看模式

A 下的干系配置参数:

参数 NSBank 用来设置利用到的存储块标号和区号,前面讲过,我们是利用的存储块 1 区号 4,

以是选择值为 FSMC_NORSRAM_BANK4。

参数 MemoryType 用来设置存储器类型,我们这里是 SRAM,以是选择值为

FSMC_MEMORY_TYPE_SRAM。

参数 MemoryDataWidth 用来设置数据宽度,可选 8 位还是 16 位,这里我们是 16 位数据宽度,

以是选择值为 FSMC_NORSRAM_MEM_BUS_WIDTH_16。

参数 WriteOperation 用来设置写使能,毫无疑问,我们前面讲解过我们要向 TFT 写数据,以是

要写使能,这里我们选择 FSMC_WRITE_OPERATION_ENABLE。

参数 ExtendedMode 是设置扩展模式使能位,也便是是否许可读写不同的时序,这里我们采纳

的读写不同时序,以是设置值为 FSMC_EXTENDED_MODE_ENABLE。

上面的这些参数是与模式 A 干系的,下面我们也来轻微理解一下其他几个参数的意义吧:

参数 DataAddressMux 用来设置地址/数据复用使能,若设置为使能,那么地址的低 16 位和数据

将共用数据总线,仅对 NOR 和 PSRAM 有效,以是我们设置为默认值不复用,值

FSMC_DATA_ADDRESS_MUX_DISABLE。

其他参数在成组模式同步模式才须要设置,大家可以参考中文参考手册理解干系参数的意

思。

接下来我们看看设置读写时序参数的两个变量 FSMC_ReadWriteTim 和 FSMC_WriteTim,

他们都是 FSMC_NORSRAM_TimingTypeDef 构造体指针类型,这两个参数在初始化的时候分

别用来初始化片选掌握寄存器 FSMC_BTRx 和写操作时序掌握寄存器 FSMC_BWTRx。

下面我

们看看 FSMC_NORSRAMTimingInitTypeDef 类型的定义:

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

这个构造体有 7 个参数用来设置 FSMC 读写时序。
实在这些参数的意思我们前面在讲解 FSMC

的时序的时候有提到,紧张是设计地址建立保持韶光,数据建立韶光等等配置,对付我们的实

验中,读写时序不一样,读写速率哀求不一样,以是对付参数 FSMC_DataSetupTime 设置了不

同的值,大家可以对照理解一下。
记住,这些参数的意义在前面讲解 FSMC_BTRx 和

FSMC_BWTRx 寄存器的时候都有提到,大家可以翻过去看看。

18.2 硬件设计

本实验用到的硬件资源有:

1) 指示灯 DS0

2) TFTLCD 模块

TFTLCD 模块的电路见图 18.1.1.2,这里我们先容 TFTLCD 模块与正点原子探索者

STM32F4 开拓板的连接,探索者 STM32F4 开拓板底板的 LCD 接口和 TFTLCD 模块直接可以

对插,连接关系如图 18.2.1 所示:

图 18.2.1 TFTLCD 与开拓板连接示意图

图 18.2.1 中圈出来的部分便是连接 TFTLCD 模块的接口,液晶模块直接插上去即可。

在硬件上,TFTLCD 模块与探索者 STM32F4 开拓板的 IO 口对应关系如下:

LCD_BL(背光掌握)对应 PB0;

LCD_CS 对应 PG12 即 FSMC_NE4;

LCD _RS 对应 PF12 即 FSMC_A6;

LCD _WR 对应 PD5 即 FSMC_NWE;

LCD _RD 对应 PD4 即 FSMC_NOE;

LCD _D[15:0]则直接连接在 FSMC_D15~FSMC_D0;

这些线的连接,探索者 STM32F4 开拓板的内部已经连接好了,我们只须要将 TFTLCD 模

块插上去就好了。
实物连接如图 18.2.2 所示:

图 18.2.2 TFTLCD 与开拓板连接实物图

18.3 软件设计

打开我们光盘的 TFT LCD 显示实验工程可以看到我们添加了两个文件 lcd.c 和头文件 lcd.h。

同时,FSMC 干系的库函数分布在 stm32f4xx_hal_fsmc.c 文件和头文件 stm32f4xx_hal_fsmc.h 中。

以是我们在工程中要引入 stm32f4xx_hal_fsmc.c 源文件。

在 lcd.c 里面代码比较多,我们这里就不贴出来了,只针对几个主要的函数进行讲解。
完全

版的代码见光盘→4,程序源码→标准例程-HAL 库版本→实验 13 TFTLCD 显示实验的 lcd.c 文

件。

本实验,我们用到 FSMC 驱动 LCD,通过前面的先容,我们知道 TFTLCD 的 RS 接在 FSMC

的 A6 上面,CS 接在 FSMC_NE4 上,并且是 16 位数据总线。
即我们利用的是 FSMC 存储器 1

的第 4 区,我们定义如下 LCD 操作构造体(在 lcd.h 里面定义):

//LCD 操作构造体typedef struct{vu16 LCD_REG;vu16 LCD_RAM;} LCD_TypeDef;//利用 NOR/SRAM 的 Bank1.sector4,地址位 HADDR[27,26]=11 A6 作为数据命令区分线//把稳 16 位数据总线时,STM32 内部地址会右移一位对齐!#define LCD_BASE ((u32)(0x6C000000 | 0x0000007E))#define LCD ((LCD_TypeDef ) LCD_BASE)

个中 LCD_BASE,必须根据我们外部电路的连接来确定,我们利用 Bank1.sector4 便是从

地址 0X6C000000 开始,而 0X0000007E,则是 A6 的偏移量,这里很多朋友不理解这个偏移量

的观点,大略解释下:以 A6 为例,7E 转换成二进制便是:1111110,而 16 位数据时,地址右

移一位对齐,那么实际对应到地址引脚的时候,便是:A6:A0=0111111,此时 A6 是 0,但是如

果 16 位地址再加 1(把稳:对应到 8 位地址是加 2,即 7E+0X02),那么:A6:A0=1000000,此

时 A6 便是 1 了,即实现了对 RS 的 0 和 1 的掌握。

我们将这个地址逼迫转换为 LCD_TypeDef 构造体地址,那么可以得到 LCD->LCD_REG 的

地址便是 0X6C00,007E,对应 A6 的状态为 0(即 RS=0),而 LCD-> LCD_RAM 的地址便是

0X6C00,0080(构造体地址自增),对应 A6 的状态为 1(即 RS=1)。

以是,有了这个定义,当我们要往 LCD 写命令/数据的时候,可以这样写:

LCD->LCD_REG=CMD; //写命令LCD->LCD_RAM=DATA; //写数据

而读的时候反过来操作就可以了,如下所示:

CMD= LCD->LCD_REG;//读 LCD 寄存器DATA = LCD->LCD_RAM;//读 LCD 数据

这个中,CS、WR、RD 和 IO 口方向都是由 FSMC 掌握,不须要我们手动设置了。
接下来,

我们先先容一下 lcd.h 里面的另一个主要构造体:

//LCD 主要参数集typedef struct{u16 width;//LCD 宽度u16 height;//LCD 高度u16 id;//LCD IDu8 dir;//横屏还是竖屏掌握:0,竖屏;1,横屏。
u16 wramcmd;//开始写 gram 指令u16 setxcmd;//设置 x 坐标指令u16 setycmd;//设置 y 坐标指令}_lcd_dev;//LCD 参数extern _lcd_dev lcddev; //管理 LCD 主要参数

该构造体用于保存一些 LCD 主要参数信息,比如 LCD 的长宽、LCD ID(驱动 IC 型号)、

LCD 横竖屏状态等,这个构造体虽然占用了十几个字节的内存,但是却可以让我们的驱动函数

支持不同尺寸的 LCD,同时可以实现 LCD 横竖屏切换等主要功能,以是还是利大于弊的。

了以上理解,下面我们开始先容 lcd.c 里面的一些主要函数。

先看 7 个大略,但是很主要的函数:

//写寄存器函数//regval:寄存器值void LCD_WR_REG(vu16 regval){ regval=regval;//利用-O2 优化的时候,必须插入的延时LCD->LCD_REG=regval;//写入要写的寄存器序号}//写 LCD 数据//data:要写入的值void LCD_WR_DATA(vu16 data){data=data;//利用-O2 优化的时候,必须插入的延时LCD->LCD_RAM=data;}//读 LCD 数据//返回值:读到的值u16 LCD_RD_DATA(void){vu16 ram;//防止被优化ram=LCD->LCD_RAM;return ram;}//写寄存器//LCD_Reg:寄存器地址//LCD_RegValue:要写入的数据void LCD_WriteReg(vu16 LCD_Reg, vu16 LCD_RegValue){LCD->LCD_REG = LCD_Reg;//写入要写的寄存器序号LCD->LCD_RAM = LCD_RegValue; //写入数据}//读寄存器//LCD_Reg:寄存器地址//返回值:读到的数据u16 LCD_ReadReg(vu16 LCD_Reg){LCD_WR_REG(LCD_Reg);//写入要读的寄存器序号delay_us(5);return LCD_RD_DATA();//返回读到的值}//开始写 GRAMvoid LCD_WriteRAM_Prepare(void){ LCD->LCD_REG=lcddev.wramcmd;}//LCD 写 GRAM//RGB_Code:颜色值void LCD_WriteRAM(u16 RGB_Code){ LCD->LCD_RAM = RGB_Code;//写十六位 GRAM}

由于 FSMC 自动掌握了 WR/RD/CS 等这些旗子暗记,以是这 7 个函数实现起来都非常大略,我

们就不多说,把稳,上面有几个函数,我们添加了一些对 MDK –O2 优化的支持,去掉的话,

在-O2 优化的时候会出问题。
这些函数实现功能见函数前面的备注,通过这几个大略函数的组

合,我们就可以对 LCD 进行各种操作了。

第七个要先容的函数是坐标设置函数,该函数代码如下:

//设置光标位置//Xpos:横坐标//Ypos:纵坐标void LCD_SetCursor(u16 Xpos, u16 Ypos){if(lcddev.id==0X9341||lcddev.id==0X5310){LCD_WR_REG(lcddev.setxcmd);LCD_WR_DATA(Xpos>>8);LCD_WR_DATA(Xpos&0XFF);LCD_WR_REG(lcddev.setycmd);LCD_WR_DATA(Ypos>>8);LCD_WR_DATA(Ypos&0XFF);}else if(lcddev.id==0X6804){if(lcddev.dir==1)Xpos=lcddev.width-1-Xpos;//横屏时处理LCD_WR_REG(lcddev.setxcmd);LCD_WR_DATA(Xpos>>8);LCD_WR_DATA(Xpos&0XFF);LCD_WR_REG(lcddev.setycmd);LCD_WR_DATA(Ypos>>8);LCD_WR_DATA(Ypos&0XFF);}else if(lcddev.id==0X5510){LCD_WR_REG(lcddev.setxcmd);LCD_WR_DATA(Xpos>>8);LCD_WR_REG(lcddev.setxcmd+1);LCD_WR_DATA(Xpos&0XFF);LCD_WR_REG(lcddev.setycmd);LCD_WR_DATA(Ypos>>8);LCD_WR_REG(lcddev.setycmd+1);LCD_WR_DATA(Ypos&0XFF);}else{if(lcddev.dir==1)Xpos=lcddev.width-1-Xpos;//横屏实在便是调转 x,y 坐标LCD_WriteReg(lcddev.setxcmd, Xpos);LCD_WriteReg(lcddev.setycmd, Ypos);}}

该函数实现将 LCD 确当前操作点设置到指定坐标(x,y)。
由于 9341/5310/6804/5510 等的设

置同其他屏有些不太一样,以是进行了差异对待。

接下来我们先容第八个函数:画点函数。
该函数实当代码如下:

//画点//x,y:坐标//POINT_COLOR:此点的颜色void LCD_DrawPoint(u16 x,u16 y){LCD_SetCursor(x,y);//设置光标位置LCD_WriteRAM_Prepare(); //开始写入 GRAMLCD->LCD_RAM=POINT_COLOR;}

该函数实现比较大略,便是先设置坐标,然后往坐标写颜色。
个中 POINT_COLOR 是我们

定义的一个全局变量,用于存放画笔颜色,顺带先容一下其余一个全局变量:BACK_COLOR,

该变量代表 LCD 的背景色。
LCD_DrawPoint 函数虽然大略,但是至关主要,其他险些所有上

层函数,都是通过调用这个函数实现的。

有了画点,当然还须要有读点的函数,第九个先容的函数便是读点函数,用于读取 LCD

的 GRAM,这里解释一下,为什么 OLED 模块没做读 GRAM 的函数,而这里做了。
由于 OLED

模块是单色的,所须要全部 GRAM 也就 1K 个字节,而 TFTLCD 模块为彩色的,点数也比 OLED

模块多很多,以 16 位色打算,一款 320×240 的液晶,须要 320×240×2 个字节来存储颜色值,

也便是也须要 150K 字节,这对任何一款单片机来说,都不是一个小数目了。
而且我们在图形

叠加的时候,可以先读回原来的值,然后写入新的值,在完成叠加后,我们又恢复原来的值。

这样在做一些大略菜单的时候,是很有用的。
这里我们读取 TFTLCD 模块数据的函数为

LCD_ReadPoint,该函数直接返回读到的 GRAM 值。
该函数利用之前要先设置读取的 GRAM

地址,通过 LCD_SetCursor 函数来实现。
LCD_ReadPoint 的代码如下:

//读取个某点的颜色值//x,y:坐标//返回值:此点的颜色u16 LCD_ReadPoint(u16 x,u16 y){vu16 r=0,g=0,b=0;if(x>=lcddev.width||y>=lcddev.height)return 0;//超过了范围,直接返回LCD_SetCursor(x,y);if(lcddev.id==0X9341||lcddev.id==0X6804||lcddev.id==0X5310)LCD_WR_REG(0X2E);//9341/6804/3510 发送读 GRAM 指令else if(lcddev.id==0X5510)LCD_WR_REG(0X2E00);//5510 发送读 GRAM 指令else LCD_WR_REG(R34);//其他 IC 发送读 GRAM 指令if(lcddev.id==0X9320)opt_delay(2);//FOR 9320,延时 2usLCD_RD_DATA();//dummy Readopt_delay(2);r=LCD_RD_DATA();//实际坐标颜色if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510){//9341/NT35310/NT35510 要分 2 次读出opt_delay(2);b=LCD_RD_DATA();g=r&0XFF;//9341/5310/5510 等,第一次读取的是 RG 的值,R 在前,G 在后,各占 8 位g<<=8;}if(lcddev.id==0X9325||lcddev.id==0X4535||lcddev.id==0X4531||lcddev.id==0XB505||lcddev.id==0XC505)return r; //这几种 IC 直接返回颜色值else if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510)return (((r>>11)<<11)|((g>>10)<<5)|(b>>11)); //ILI9341/NT35310/NT35510 须要公式转换一下else return LCD_BGR2RGB(r); //其他 IC}

在 LCD_ReadPoint 函数中,由于我们的代码不止支持一种 LCD 驱动器,以是,我们根据

不同的 LCD 驱动器((lcddev.id)型号,实行不同的操作,以实现对各个驱动器兼容,提高函数

的通用性。

第十个要先容的是字符显示函数 LCD_ShowChar,该函数同前面 OLED 模块的字符显示函

数差不多,但是这里的字符显示函数多了 1 个功能,便是可以以叠加办法显示,或者以非叠加

办法显示。
叠加办法显示多用于在显示的图片上再显示字符。
非叠加办法一样平常用于普通的显示。

该函数实当代码如下:

//在指定位置显示一个字符//x,y:起始坐标//num:要显示的字符:" "--->"~"//size:字体大小 12/16/24//mode:叠加办法(1)还是非叠加办法(0)void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode){ u8 temp,t1,t; u16 y0=y;u8 csize=(size/8+((size%8)?1:0))(size/2);//得到字体一个字符对应点阵集所占的字节数//设置窗口num=num-' ';//得到偏移后的值for(t=0;t<csize;t++){if(size==12)temp=asc2_1206[num][t];//调用 1206 字体else if(size==16)temp=asc2_1608[num][t]; //调用 1608 字体else if(size==24)temp=asc2_2412[num][t]; //调用 2412 字体else return;//没有的字库for(t1=0;t1<8;t1++){if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);else if(mode==0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);temp<<=1;y++;if(y>=lcddev.height)return;//超区域了if((y-y0)==size){y=y0; x++;if(x>=lcddev.width)return; //超区域了break;}}}}

在 LCD_ShowChar 函数里面,我们采取快速画点函数 LCD_Fast_DrawPoint 来画点显示字

符,该函数同 LCD_DrawPoint 一样,只是带了颜色参数,且减少了函数调用的韶光,详见本例

程源码。
该代码中我们用到了三个字符集点阵数据数组 asc2_2412、asc2_1206 和 asc2_1608,

这几个字符集的点阵数据的提取办法,同十七章先容的提取方法是千篇一律的。
详细请参考第

十七章。

末了,我们再先容一下 TFTLCD 模块的初始化函数 LCD_Init,该函数先初始化 STM32 与

TFTLCD 连接的 IO 口,并配置 FSMC 掌握器,然后读取 LCD 掌握器的型号,根据掌握 IC 的

型号实行不同的初始化代码,其简化代码如下:

void LCD_Init(void){ GPIO_InitTypeDef GPIO_Initure;FSMC_NORSRAM_TimingTypeDef FSMC_ReadWriteTim;FSMC_NORSRAM_TimingTypeDef FSMC_WriteTim;__HAL_RCC_GPIOB_CLK_ENABLE();//开启 GPIOB 时钟GPIO_Initure.Pin=GPIO_PIN_15;//PB15,背光掌握GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出GPIO_Initure.Pull=GPIO_PULLUP;//上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH;//高速HAL_GPIO_Init(GPIOB,&GPIO_Initure);TFTSRAM_Handler.Instance=FSMC_NORSRAM_DEVICE;TFTSRAM_Handler.Extended=FSMC_NORSRAM_EXTENDED_DEVICE;TFTSRAM_Handler.Init.NSBank=FSMC_NORSRAM_BANK4; //利用 NE4TFTSRAM_Handler.Init.DataAddressMux=FSMC_DATA_ADDRESS_MUX_DISABLE;//地址/数据线不复用TFTSRAM_Handler.Init.MemoryType=FSMC_MEMORY_TYPE_SRAM; //SRAMTFTSRAM_Handler.Init.MemoryDataWidth=FSMC_NORSRAM_MEM_BUS_WIDTH_16; //16 位数据宽度TFTSRAM_Handler.Init.BurstAccessMode=FSMC_BURST_ACCESS_MODE_DISABLE;//是否使能突发访问,仅对同步突发存储器有效,此处未用到TFTSRAM_Handler.Init.WaitSignalPolarity=FSMC_WAIT_SIGNAL_POLARITY_LOW;//等待旗子暗记的极性,仅在突发模式访问下有用TFTSRAM_Handler.Init.WaitSignalActive=FSMC_WAIT_TIMING_BEFORE_WS;//存储器是在等待周期之前的一个时钟周期还是等待周期期间使能 NWAITTFTSRAM_Handler.Init.WriteOperation=FSMC_WRITE_OPERATION_ENABLE;//存储器写使能TFTSRAM_Handler.Init.WaitSignal=FSMC_WAIT_SIGNAL_DISABLE;//等待使能位,此处未用到TFTSRAM_Handler.Init.ExtendedMode=FSMC_EXTENDED_MODE_ENABLE; //读写利用不同的时序TFTSRAM_Handler.Init.AsynchronousWait=FSMC_ASYNCHRONOUS_WAIT_DISABLE;//是否使能同步传输模式下的等待旗子暗记,此处未用到TFTSRAM_Handler.Init.WriteBurst=FSMC_WRITE_BURST_DISABLE;//禁止突发写TFTSRAM_Handler.Init.ContinuousClock=FSMC_CONTINUOUS_CLOCK_SYNC_ASYNC; //FMC 读时序掌握寄存器FSMC_ReadWriteTim.AddressSetupTime=0x0F;//地址建立韶光(ADDSET)为 16 个 HCLK 1/168M=6ns16=96nsFSMC_ReadWriteTim.AddressHoldTime=0;FSMC_ReadWriteTim.DataSetupTime=60;//数据保存韶光为 60 个 HCLK =660=360nsFSMC_ReadWriteTim.AccessMode=FSMC_ACCESS_MODE_A;//模式 A //FMC 写时序掌握寄存器FSMC_WriteTim.BusTurnAroundDuration=0;//总线周转阶段持续韶光为 0,此变量不赋值的话会莫名其妙的//自动修正为 4。
导致程序运行正常FSMC_WriteTim.AddressSetupTime=9;//地址建立韶光(ADDSET)为 9 个 HCLK =54nsFSMC_WriteTim.AddressHoldTime=0;FSMC_WriteTim.DataSetupTime=8;//数据保存韶光为 6ns9 个 HCLK=54nFSMC_WriteTim.AccessMode=FSMC_ACCESS_MODE_A; //模式 AHAL_SRAM_Init(&TFTSRAM_Handler,&FSMC_ReadWriteTim,&FSMC_WriteTim); delay_ms(50); // delay 50 ms //考试测验 9341 ID 的读取LCD_WR_REG(0XD3);lcddev.id=LCD_RD_DATA();//dummy readlcddev.id=LCD_RD_DATA();//读到 0X00lcddev.id=LCD_RD_DATA(); //读取 93lcddev.id<<=8;lcddev.id|=LCD_RD_DATA(); //读取 41if(lcddev.id!=0X9341)//非 9341,考试测验看看是不是 NT35310{LCD_WR_REG(0XD4);lcddev.id=LCD_RD_DATA();//dummy readlcddev.id=LCD_RD_DATA();//读回 0X01lcddev.id=LCD_RD_DATA();//读回 0X53lcddev.id<<=8;lcddev.id|=LCD_RD_DATA();//这里读回 0X10if(lcddev.id!=0X5310)//也不是 NT35310,考试测验看看是不是 NT35510{LCD_WR_REG(0XDA00);lcddev.id=LCD_RD_DATA();//读回 0X00LCD_WR_REG(0XDB00);lcddev.id=LCD_RD_DATA();//读回 0X80lcddev.id<<=8;LCD_WR_REG(0XDC00);lcddev.id|=LCD_RD_DATA();//读回 0X00if(lcddev.id==0x8000)lcddev.id=0x5510;//NT35510 读回的 ID 是 8000H,为方便区分,我们逼迫设置为 5510if(lcddev.id!=0X5510)//也不是 NT5510,考试测验看看是不是 SSD1963{LCD_WR_REG(0XA1);lcddev.id=LCD_RD_DATA();lcddev.id=LCD_RD_DATA();//读回 0X57lcddev.id<<=8;lcddev.id|=LCD_RD_DATA();//读回 0X61if(lcddev.id==0X5761)lcddev.id=0X1963;//SSD1963 读回的 ID 是 5761H,为方便区分,我们逼迫设置为 1963}}}printf(" LCD ID:%x\r\n",lcddev.id); //打印 LCD ID if(lcddev.id==0X9341) //9341 初始化 {……//9341 初始化寄存器序列}else if(lcddev.id==0xXXXX) //其他 LCD 初始化代码{……//其他 LCD 驱动 IC,初始化代码}LCD_Display_Dir(0);//默认为竖屏显示LCD_LED=1;//点亮背光LCD_Clear(WHITE);}

从初始化代码可以看出,LCD 初始化步骤为:

先对 FSMC 干系 IO 进行初始化,然后是 FSMC 的初始化,这个我们在前面都有先容,最

后根据读到的 LCD ID,对不同的驱动器实行不同的初始化代码,从上面的代码可以看出,这

个初始化函数可以针对十多款不同的驱动 IC 实行初始化操作,这样大大提高了全体程序的通用

性。
大家在往后的学习中该当多利用这样的办法,以提高程序的通用性、兼容性。

特殊把稳:本函数利用了 printf 来打印 LCD ID,以是,如果你在主函数里面没有初始化串

口,那么将导致程序去世在 printf 里面!

如果不想用 printf,那么请注释掉它。

LCD 驱动干系的函数就给大家讲解到这里。
接下来,我们看看主函数代码如下:

int main(void){u8 x=0;u8 lcd_id[12];//存放 LCD ID 字符串 HAL_Init(); //初始化 HAL 库 Stm32_Clock_Init(336,8,2,7); //设置时钟,168Mhzdelay_init(168); //初始化延时函数uart_init(115200); //初始化 USARTLED_Init();//初始化 LEDLCD_Init();//初始化 LCD FSMC 接口POINT_COLOR=RED;//画笔颜色:赤色sprintf((char)lcd_id,"LCD ID:%04X",lcddev.id);//将 LCD ID 打印到 lcd_id 数组。
while(1){switch(x){case 0:LCD_Clear(WHITE);break;case 1:LCD_Clear(BLACK);break;case 2:LCD_Clear(BLUE);break;case 3:LCD_Clear(RED);break;case 4:LCD_Clear(MAGENTA);break;case 5:LCD_Clear(GREEN);break;case 6:LCD_Clear(CYAN);break;case 7:LCD_Clear(YELLOW);break;case 8:LCD_Clear(BRRED);break;case 9:LCD_Clear(GRAY);break;case 10:LCD_Clear(LGRAY);break;case 11:LCD_Clear(BROWN);break;}POINT_COLOR=RED;LCD_ShowString(30,40,210,24,24,"Explorer STM32F4");LCD_ShowString(30,70,200,16,16,"TFTLCD TEST");LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(30,110,200,16,16,lcd_id);//显示 LCD IDLCD_ShowString(30,130,200,12,12,"2017/4/8");x++;if(x==12)x=0;LED0=!LED0;delay_ms(1000);}}

该部分代码将显示一些固定的字符,字体大小包括 2412、168 和 126 等三种,同时显示

LCD 驱动 IC 的型号,然后一直的切换背景颜色,每 1s 切换一次。
而 LED0 也会一直的闪烁,

指示程序已经在运行了。
个中我们用到一个 sprintf 的函数,该函数用法同 printf,只是 sprintf

把打印内容输出到指定的内存区间上,sprintf 的详细用法,请百度。

其余特殊把稳:uart_init 函数,不能去掉,由于在 LCD_Init 函数里面调用了 printf,以是

一旦你去掉这个初始化,就会去世机了!实际上,只要你的代码有用到 printf,就必须初始化串口,

否则都会去世机,即停在 usart.c 里面的 fputc 函数,出不来。

在编译通过之后,我们开始下载验证代码。

18.4 下载验证

将程序下载到探索者 STM32F4 开拓板后,可以看到 DS0 一直的闪烁,提示程序已经在运

行了。
同时可以看到 TFTLCD 模块的显示如图 18.4.1 所示:

图 18.4.1 TFTLCD 显示效果图

我们可以看到屏幕的背景是一直切换的,同时 DS0 一直的闪烁,证明我们的代码被精确的

实行了,达到了我们预期的目的。

标签:

相关文章

什么是TFT液晶屏LVDS接口_接口_暗记

LVDS输出接口采取极低的电压摆幅(约350mV ,在两根PCB走线或一对平衡电缆上通过差分进行数据传输,即低压差分旗子暗记传...

互联网 2025-01-08 阅读0 评论0