TFTLCD显示实验_STM32F1开发指南_第十八章

原文链接:链接

 

   第十八章 TFTLCD显示实验

前言

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

而且尺寸也较小。本章我们将介绍ALIENTEK 2.8寸TFT LCD模块,该模块采用TFTLCD面板

,可以显示16位真彩色图片。

    本章将利用stm32开发板上的LCD接口,点亮LCD,并实现ASCII字符和彩色的显示等功能,

并在串口上打印LCD控制器ID,同时在LCD上面显示。

    本章分为如下几个部分:

18.1 TFTLCD和FSMC简介

18.2 硬件设计

18.3 软件设计

18.4 下载验证

18.1 TFTLCD和FSMC简介

    本章通过STM32的FSMC接口来控制TFTLCD的显示,所以本节分为两部分,分别介绍

TFTLCD和FSMC。

18.1.1 TFTLCD简介

    TFTLCD即薄膜晶体管液晶显示器(真彩液晶显示器)。与无源的TN_LCD、STN_LCD的简

单矩阵不同,它在液晶显示屏的每一个像素上都设置有一个薄膜晶体管(TFT),可有效地克

服非选通时的串扰,使液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。

    TFTLCD的特点:

    1、2.4‘、2.8’、3.5‘、4.3‘、7’ 5种大小的屏幕可选;

    2、320x240的分辨率(3.5'分辨率为320*480、4.3‘和7’分辨率为800*480);

    3、16位真彩色显示;

    4、自带触摸屏,可以用来作为控制输入。

    本章使用2.8寸的ALIENTEK TFTLCD模块为例介绍,该模块支持65k色显示,其中分辨率

为320x240,接口为16位的80并口,自带触摸屏。

    模块原理图:

    

     TFTLCD模块采用2*17的2.54公排针与外部连接,接口定义如下:

 

     从图中可以看出,ALIENTEK TFTLCD模块采用16位的并口方式与外部相连,之所以不采用

8位的方式,是因为彩屏的数据量较大,尤其是显示图像时,如果用8位数据线,会比16位方式

慢一倍以上。

    该模块的80并口如下所示:

序号管脚功能
1CSTFTLCD片选信号
2WR向TFTLCD写入数据
3RD从TFTLCD读取数据
4D[15:0]16位双向数据线
5RST硬复位TFTLCD
6RS命令/数据标志(0:读写命令;1:读写数据)

    TFTLCD模块的RST信号线是直接接到stm32的复位脚上,并不由软件控制,这样可以节省一个

IO口。另外我们还需要一个背光控制线来控制TFTLCD的背光。所以总共需要21个IO口。

    注意:我们标注的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等(具体型号,大家可以通过下载本章实验代码,通过串口或者

LCD显示查看),这里仅以ILI9341控制器为例进行介绍。

    ILI9341液晶控制器自带显存,大小为240*320*18/8 = 172800,即18位模式(26万色)下的显存量。

在16位模式下,ILI9341采用RGB565格式存储颜色数据,此时ILI9341的18位数据线与MCU的16位

数据线以及LCD_GRAM的对应关系如下:

9341总线D17D16D15D14D13D12D11D10D9D8D7D6D5D4D3D2D1D0
MCU数据线
(16位)
D15D14D13D12D11NCD10D9D8D7D6D5D4D3D2D1D0NC
LCD_GRAM
(16位)
R[4]R[3]R[2]R[1]R[0]NCG[5]G[4]G[3]G[2]G[1]G[0]B[4]B[3]B[2]B[1]B[0]NC

                                                                    16位数据线与显存的对应关系

    从图中可以看出,ILI9341在16位模式下,有用的数据线有:D17~13和D11~1,D0和D12没有使用。

    如上表所示,MCU的16位数据,最低5位表示蓝色,中间6位代表绿色,最高5位代表红色。数值

越大,颜色越深。

    注意:ILI9341所有的指令都是8位的(高8位无效),且参数除了读写GRAM时是16位,其他操作参数,

都是8位,这个和ILI9320等驱动器不一样,必须加以注意。

ILI9341几个重要命令(0xD3, 0x36, 0x2A, 0x2B, 0x2C, 0x2E):

1)读取LCD控制器的ID号:读取ID4指令:0xD3。

    0xD3后面跟着4个参数,最后两个参数,都出来的是控制器ILI9341的数字部分,从而可以判断所使用

的LCD驱动器的型号。

命令格式如下:

 

2)控制ILI9341存储器的读写方向:存储器访问控制指令:0x36.

    在连续写GRAM时,可以控制GRAM指针的增长方向,从而控制显示方式(读GRAM也是一样)。

命令格式如下:

     0x36指令后面跟着一个参数,这里主要关注3个位:MY、MX和MV。通过这三个位的设置,可

以控制整个ILI9341的全部扫描方向:

     这样,利用ILI9341显示内容时,就有很大的灵活性,比如显示BMP图片时,BMP解码数据,

就从图片左下角开始,慢慢显示到右上角。如果设置LCD扫描方向为从左到右、从下到上,则

只需要设置一次坐标,然后不停向LCD里面填充颜色数据即可。大大提高了显示速度。

3)列地址设置指令:0x2A。

    在从左到右,从上到下的扫描方式(默认)下面,该指令用于设置横坐标(x坐标)。

 

     在默认扫描方式时,该指令用于设置X坐标,该指令带有4个参数,实际是2个坐标值:SC

和EC,即列地址的起始值和结束值。SC必须小于等于EC,且0 ≤ SC/EC ≤ 239。一般在设置

X坐标时,只需要带2个参数即可,即只设置SC即可,因为如果EC未变化,只需要设置一次即

可(在初始化ILI9341时设置),从而提高速度。

4)页地址设置指令:0x2B。

    在从左到右,从上到下的扫描方式(默认)下面,该指令用于设置纵坐标(y坐标)。

    在默认扫描方式时,该指令用于设置y坐标。带4个参数,实际是2个坐标值:SP和EP,

即页地址的起始值和结束值,SP必须大于等于EP,且0 ≤ SP/EP ≤ 319。一般在设置Y坐标

时,我们只需要带2个参数即可,即只设置SP即可。因为如果EP无变化,则只需要设置一次即可

(在初始化ILI9341时设置),从而提高速度。

5)写GRAM指令:0x2C。

    在发送该指令后,即可向LCD的GRAM里面写颜色数据了,该指令支持连续写。格式如下:

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

而GRAM的地址将根据MY/MX/MV设置的扫描方向自增。例如:假设设置从左到右、从上到下扫描方式,

则设置好起始坐标(通过SC、SP设置)后,每写入一个颜色值,GRAM地址就会自动增加1(SC++),如

果碰到EC,则回到SC,同时SP++,一直到坐标:EC,EP结束,期间无需设置再次坐标,大大提高写

入速度。

6)读GRAM指令:0x2E。

    用于读取ILI9341的显存(GRAM)

    注:该指令在ILI9341数据手册上的描述有误,真实输出情况如下所示:

     该指令用于读取GRAM,上图所示,ILI9341在收到该指令后,第一次输出的是dummy数据,是

无效数据;第二次开始,读取到有效GRAM数据(从坐标:SC,SP开始),输出规律为:每种颜色占8

位,一次输出2个颜色分量。比如:第一次输出R1G1,随后为:B1R2->G2B2->R3G3->B3R4->

G4B4->R5G5...以此类推。

    如果只需要读取一个点的颜色值,则只需要接收到参数3即可。如果要连续读取(利用GRAM地址

自增,方法同上),则按照上述规律接收颜色数据。

    以上就是操作ILI9341常用的几个指令,通过这几个指令,可以很好的控制ILI9341显示我们需要

显示的内容。

    一般TFTLCD模块的使用流程如下:

 

    画点流程是:设置坐标 -> 写GRAM指令 -> 写入颜色数据,然后在LCD上既可以看到写入的颜色了。

    读点流程是:设置坐标 -> 读GRAM指令 -> 读取颜色数据,这样就可以获取对应点的颜色数据了。

    以上只是最简单的操作,也是最常用的操作。接下来我们将该模块(2.8寸屏模块)用来显示字符和数字,

通过以上介绍,我们可以得出TFTLCD显示需要的相关步骤如下:

1)设置stm32f1与TFTLCD模块相连的IO

    先初始化stm32相关管脚。这里用到FSMC,在下面介绍。

2)初始化TFTLCD模块

    此处没有硬件复位,因为lcd的RST同stm32的Reset连在一起了。

    初始化序列就是向LCD控制器写入一系列设置值(比如伽马校准),这些初始化序列一般LCD供应商

会提供给客户。

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

    处理一个点:设置坐标 -> 写GRAM指令 -> 写GRAM。

    处理字符和数字:需要设计一个函数来实现。

18.1.2 FSMC简介

    大容量,且引脚数目在100以上的stm32f103芯片都带有FSMC接口,ALIENTEK战舰stm32开发板

的主芯片为stm32f103zet6,是带有FSMC接口的。

    FSMC,即灵活的静态存储控制器,能够与同步或异步存储器和16位PC存储卡连接。

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

    FSMC的框图如下所示:

    从上图可以看出,stm32的FSMC将外部设备分为3类: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

来说,就是写命令。反之就是写数据了。这样数据和命令就分开来了,他们其实就是对应SRAM

操作的两个连续地址。当然当然RS也可以接在其他地址线上,战舰STM32开发板是把RS连接在

A10上面。

    STM32的FSMC支持8/16/32位数据宽度,我们这里用到的LCD是16位宽度的,所以在设置的时

候,选择16位宽即可。

    FSMC的外部设备地址映像:

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

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

所以本章仅讨论块1的相关配置,其他块的配置,请参考《STM32参考手册》第19章(324页)

的相关介绍。

    STM32的FSMC存储块1(Bank)被分成4个区,每个区管理64M字节空间,每个区都有独立

的寄存器对应所连接的存储器进行配置。Bank1的256字节空间由28根地址线(HADDR[27:0])寻址。

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

HADDR[26:27]对4个区进行寻址。如下表所示:

Bank1所选区片选信号地址范围HADDR
[27:26][25:0]
第一区FSMC_NE10x6000 0000~0x63ff ffff00FSMC_A[25:0]
第二区FSMC_NE20x6400 0000~0x67ff ffff01
第三区FSMC_NE30x6800 0000~0x6Bff ffff10
第四区FSMC_NE40x6c00 0000~0x6fff ffff11

   注意:上表中HADDR[25:0]的对应关系:

Bank1接的存储器位宽HADDR[]和FSMC_A[]的关系
16HADDR[25:1]   -> FSMC_A[24:0]
8HADDR[25:0]   -> FSMC_A[25:0]

    不论外部接的设备位宽是8位还是16位,FSMC_A[0]永远接在外部设备的地址A[0]。这里,TFTLCD

使用的是16位数据宽度,所以HADDR[0]并没有用到,只有[25:1]有效。相当于右移一位。

    另外,HADDR[27:26]的设置,不需要我们干预,比如:当选择使用Bank1的第三区,即用FSMC_NE3

来连接外部设备的时候,HADDR[27:26]就为10。

    我们需要做的就是配置对应第三区的寄存器组,来适应外部设备即可

    STM32的FSMC各Bank配置寄存器如下表所示:

内部控制器存储块管理的地址范围支持的设备类型配置寄存器
NOR FLASH
控制器
Bank10x6000 0000~0x6fff ffffSRAM/ROM
NOR FLASH 
PSRAM
FSMC_BCR   1/2/3/4
FSMC_BTR    1/2/2/3
FSMC_BWTR 1/2/3/4
NAND FLASH/
PC CARD
控制器
Bank20X7000 0000~0X7fff ffffNAND FLASH

FSMC_PCR    2/3/4

FSMC_SR       2/3/4

FSMC_PMEM 2/3/4

FSMC_PATT   2/3/4

FSMC_PIO      4

Bank30X8000 0000~0X8fff ffff
Bank40X9000 0000~0X9fff ffffPC Card

    对于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、同步突发访问中获得第一个数据所需要的等待延迟DATLAT。

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

1、地址建立时间ADDSET;

2、数据建立时间DATAST;

3、地址保持时间ADDHLD。

    FSMC综合了SRAM/ROM 、PSRAM和NOR FLASH产品的信号特点,定义了四种不同的

异步时序模型。选用不同时序模型时,需要设置不同的时序参数

    NOR FLASH控制器支持的时序模型如下表所示:

时序模型简单描述需要设置的时间参数
异步Mode1SRAM/ CRAM时序DATAST、ADDSET
ModeASRAM/ CRAM OE选通时序DATAST、ADDSET
Mode2/BNOR FLASH时序DATAST、ADDSET
ModeCNOR FLASH OE选通时序DATAST、ADDSET
ModeD延长地址保持时间的异步时序DATAST、ADDSET、ADDHLK
同步突发根据同步时钟FSMC_CK读取多个顺序单元的数据CLKDIV、DATLAT

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

写周期参数指标之间的计算关系。

    利用该计算关系和存储芯片数据手册中给定的参数指标,可以计算出FSMC所需要的各时

间参数,从而对时间参数寄存器进行合理配置。

    本章,我们使用异步模型A(ModeA)方式控制TFTLCD,

    ModeA的读操作时序如下图:

     模式A支持独立的读写时序控制,这个对我们驱动TFTLCD来说非常有用,因为TFTLCD

在读的时候,一般比较慢,而在写的时候可以比较快,如果读写用一样的时序,则只能以读

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

作完成时,在配置回写的时序,这样频繁配置,虽然不会降低写的速度,但比较麻烦。而如果

有独立的读写时序控制,则只要初始化时配置好就行了,能同时满足速度和配置步骤的要求。

    ModeA的写操作时序如下图:

     从模式A的读写时序,可以看出读操作还存在额外的2个HCLK周期,用于数据存储,

所以同样的配置都操作一般比写操作慢一点。

    上两幅图中的ADDSET和DATAST是通过不同的寄存器配置的。

    接下来讲解一下Bank1的几个控制寄存器

    1、SRAM/NOR闪存片选控制寄存器:FSMC_BCRx(x=1~4),该寄存器各位描述如下:

     本章用到的几个位如下所示:

序号位域功能
1EXTMOD扩展模式使能位,即是否允许读写不同的时序。
本章需要,所以该位为1。
2WREN写使能位。
本章要向LCD写数据,所以该位为1。
3MWID[1:0]存储器数据总线宽度。00:8位;01:16位;10和11保留。
本章用16位数据线,所以该位为01。
4MTYP[1:0]存储器类型。00:SRAM\ROM;01:PSRAM;10:NOR FLASH;11:保留。
本章把LCD当作SRAM使用,所以设置为00。
5MBKEN存储块是能位。
本章需要用该存储块控制LCD,所以使能这个存储块。

    2、SRAM/NOR闪存片选时序寄存器:FSMC_BTRx(x=1~4),该寄存器各位描述如下:

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

    如果FSMC_BCRx中使能了扩展模式,则有两个时序寄存器分别对应读(本寄存器)和写操作

(FSMC_BWTRx寄存器)。

    因为我们要求读写分开时序控制,所以EXTMODE使能了。即本寄存器是读操作时序寄存器,

控制读操作的相关时序。

    本章用到的几个位域如下所示:

序号位域功能
1ACCMOD[1:0]访问模式。
00:模式A;01:模式B;10:模式C;11:模式D。
本章用到模式A,所以设置为00.
2DATAST[7:0]数据保持时间。
0为保留设置,其他设置则代表保持时间为:DATAST个HCLK时钟周期,最大为255个HCLK周期。
对于ILI9341来说,其实就是RD低电平持续时间,一般为355ns。而一个HCLK时钟周期为13.8ns左右(1/72Mhz),为了兼容其它屏,这里设置DATAST为15,即16个HCLK周期,时间大约为234ns(未计算数据存储的2个HCLK时间,对于9341来说超频了,但实际可以正常使用)。
3ADDSET[3:0]地址建立时间。
其建立时间为:ADDSET个HCLK周期,最大为15个HCLK周期。对ILI9341来说,这里相当于RD高电平持续时间,为90ns,本来这里应该设置为和DATAST一样,但由于stm32f103 FSMC的性能问题,就算设置ADDSET为0,RD的高电平持续时间也达到了190ns以上,故此处设置ADDSET为较小值。
本章设置ADDSET为1,即2个HCLK周期,实际RD高电平大于200ns。

    3、SRAM/NOR闪存写时序寄存器:FSMC_BWTRx(x=1~4),该寄存器各位描述如下:

     该寄存器为写操作时序控制寄存器。

     本章用到的几个位域和读操作时序一模一样,如下所示:

序号位域功能
1ACCMOD[1:0]同FSMC_BTRx一样,选择模式A
2DATAST[7:0]对应低电平持续时间,设置为3.
3ADDSET[3:0]对应高电平持续时间,设置为0.

    注:对于ILI9341来说,DATAST[7:0]和ADDSET[3:0]的持续时间只需要15ns就够了,比读操作

快得多。

    所以我们这里设置DATAST为3,即4个HCLK周期,时间约55ns(因为9320等控制器,这个时间

要求比较长,要50ns)。设置ADDSET(也存在性能问题)为0,即1个HCLK周期,实际WR电平时间

大于100ns。

    至此,我们对STM32的FSMC介绍差不多了,通过上面两节,可以开始写LCD驱动代码了。

    注意:在MDK的寄存器里面,没有定义FSMC_BCRx、FSMC_BTRx、FSMC_BWTRx等这些

单独的寄存器,而是将他们进行了组合。

BTCR[8]:FSMC_BCRx和FSMC_BTRx:

BTCR[7]BTCR[6]BTCR[5]BTCR[4]BTCR[3]BTCR[2]BTCR[1]BTCR[0]
FSMC_BTR4FSMC_BCR4FSMC_BTR3FSMC_BCR3FSMC_BTR2FSMC_BCR2FSMC_BTR1FSMC_BCR1

BWTR[7]:FSMC_BWTRx:

BWTR[6]BWTR[5]BWTR[4]BWTR[3]BWTR[2]BWTR[1]BWTR[0]
FSMC_BWTR4reservedFSMC_BWTR3reservedFSMC_BWTR2reservedFSMC_BWTR1

    通过以上讲解,大家对FSMC的原理有了一个初步认识,如果还不熟悉,请搜索网络资料理解

FSMC的原理。只有理解了原理,使用库函数才可以得心应手。

FSMC相关库函数

1.FSMC初始化函数:

    根据前面讲解,初始化FSMC主要是初始化三个寄存器FSMC_BCRx、FSMC_BTRx和FSMC_BWTRx,

在固件库中怎么初始化他们的函数分别为:

 
  1. FSMC_NORSRAMInit(); //初始化NOR和SRAM

  2. FSMC_NANDInit();

  3. FSMC_PCCARDInit();

    这三个函数分别用来初始化4种类型存储器,这里根据名字很好判断。

初始化NOR和SRAM的函数定义如下:

void FSMC_NORSRAMInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct);

    这个函数只有一个入参,FSMC_NORSRAMInitTypeDef类型指针变量,其成员变量如下:

 
  1. typedefstruct

  2. {

  3. uint32_t FSMC_Bank;

  4. uint32_t FSMC_DataAddressMux;

  5. uint32_t FSMC_MemoryType;

  6. uint32_t FSMC_MemoryDataWidth;

  7. uint32_t FSMC_BurstAccessMode;

  8. uint32_t FSMC_AsynchronousWait;

  9. uint32_t FSMC_WaitSignalPolarity;

  10. uint32_t FSMC_WrapMode;

  11. uint32_t FSMC_WaitSignalActive;

  12. uint32_t FSMC_WriteOperation;

  13. uint32_t FSMC_WaitSignal;

  14. uint32_t FSMC_ExtendedMode;

  15. uint32_t FSMC_WriteBurst;

  16. FSMC_NORSRAMTimingInitTypeDef* FSMC_ReadWriteTimingStruct;

  17. FSMC_NORSRAMTimingInitTypeDef* FSMC_WriteTimingStruct;

  18. }FSMC_NORSRAMInitTypeDef;

    从这个结构体可以看出,前面有13个基本类型(uint32_t)的成员变量,用来配置片选控制寄存器

FSMC_BCRx。

    最后还有两个FSMC_NORSRAMTimingInitTypeDef指针类型的成员变量。前面讲到过,FSMC

有读时序和写时序之分,这里就是用来设置读时序和写时序的参数。这两个参数是用来配置寄存器

FSMC_BTRx和FSMC_BWTRx。

    模式A下上面的相关参数怎么配置?

序号参数设置
1FSMC_Bank设置使用到的存储块标号和区号。
此处使用的是存储块1区号4,所以选择值为FSMC_Bank1_NORSRAM4。
2FSMC_MemoryType设置存储类型。
此处使用SRAM,所以选择值为FSMC_MemoryType_SRAM。
3FSMC_MemoryDataWidth设置数据宽度。
这里用的16位,所以选择值为FSMC_MemoryDataWidth_16b。
4FSMC_WriteOperation设置写是能。
此处要向TFT写数据,所以要写使能,选择值为FSMC_WriteOperation_Enable。
5FSMC_ExtendedMode设置扩展模式使能,即是否允许读写不同的时序。
此处使用读写不同时序,所以设置值为FSMC_ExtendedMode_Enable。
6FSMC_DataAddressMux设置地址/数据复用使能,若设置为使能,则地址的低16位和数据将共用数据总线,仅对NOR和PSRAM有效。
此处设置为默认值不复用,值为FSMC_DataAddressMux_Disable。
上面这些参数都是和模式A相关。
7FSMC_BurstAccessMode
FSMC_AsynchronousWait
FSMC_WaitSignalPolarity
FSMC_WaitSignalActive
FSMC_WrapMode
FSMC_WaitSignal
FSMC_WriteBurst
这些参数在组成模式同步模式才需要设置,参考中文参考手册。
8FSMC_ReadWriteTimingStruct初始化片选控制寄存器FSMC_BTRx。
9FSMC_WriteTimingStruct初始化写操作时序寄存器FSMC_BWTRx。

FSMC_NORSRAMTimingInitTypeDef类型定义如下:

 
  1. typedefstruct

  2. {

  3. uint32_t FSMC_AddressSetupTime;

  4. uint32_t FSMC_AddressHoldTime;

  5. uint32_t FSMC_DataSetupTime;

  6. uint32_t FSMC_BusTurnAroundDuration;

  7. uint32_t FSMC_CLKDivision;

  8. uint32_t FSMC_DataLatency;

  9. uint32_t FSMC_AccessMode;

  10. }FSMC_NORSRAMTimingInitTypeDef;

    这个结构体有7个参数用来设置FSMC读写时序。这些参数主要是设计地址建立保持时间,数据建立时间等配置。

这些参数的意义在前面有讲解。

    本章读写时序不同,读写速度要求不同,所以参数FSMC_DataSetupTime设置不同的值。

2.FSMC使能函数:

    FSMC对不同的存储器类型同样提供了不同的使能函数:

 
  1. void FSMC_NORSRAMCmd(uint32_t FSMC_Bank,FunctionalStateNewState);

  2. void FSMC_NANDCmd (uint32_t FSMC_Bank,FunctionalStateNewState);

  3. void FSMC_PCCARDCmd (FunctionalStateNewState);

本章使用SRAM,所以使用第一个函数。

18.2 硬件设计

    本实验用到的硬件有:

1)指示灯DS0;

2)TFTLCD模块;

    TFTLCD模块的电路图如下所示:

    在硬件上,TFTLCD模块与开发板上单片机的IO口对应关系如下:

 
  1. LCD_BL -> PB0

  2. LCD_CS -> PG12(FSMC_NE4)

  3. LCD_RS -> PG0 (FSMC_A10)

  4. LCD_WR -> PD5 (FSMC_NWE)

  5. LCD_RD -> PD4 (FSMC_NOE)

  6. LCD_D[15:0] -> FSMC_D15:FSMC_D0

18.3 软件设计

    本实验,LCD的RS接FSMC的A10上面,CS接FSMC_NE4,16位数据总线。即使用FSMC存储器

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

 
  1. //LCD操作结构体

  2. typedef sturct

  3. {

  4. vu16 LCD_REG;

  5. vu16 LCD_RAM;

  6. }LCD_TypeDef;

  7. //使用NOR/SRAM的Bank1.sector4,地址位HADDR[27,26] = 11,A10作为数据命令区分线

  8. //注意:16位数据总线时,stm32内部地址会右移一位对齐!

  9. #define LCD_BASE ((u32)(0x6C000000 | 0x000007fe))

  10. #define LCD ((LCD_TypeDef*)LCD_BASE)

    其中,LCD_BASE,必须根据外部电路的连接确定,我们使用Bank1.sector4就是从

地址0x6c000000开始,而0x000007fe,则是A10的偏移量。我们把这个地址强制转换成

LCD_TypeDef结构体地址,则可以得到LCD->LCD_REG的地址就是0x6c00, 07fe,对

应A10的状态为0(即RS=0),而LCD->LCD_RAM的地址就是0x6c00 0800(结构体地址

自增),对应A10的状态为1(即RS=1)。

    所以,有了这个定义,当我们向LCD写命令/数据时,可以这样写:

 
  1. LCD->LCD_REG = CMD;//写命令

  2. LCD->LCD_RAM = DATA;//写数据

    而读时则反过来:

 
  1. CMD = LCD->LCD_REG;//读LCD寄存器

  2. DATA = LCD->LCD_RAM;//读LCD数据

    这其中,CS、WR、RD和IO口方向都是由FSMC控制的,不需要手动设置。

 

    lcd.h中另一个重要的结构体:

 
  1. //LCD重要参数集

  2. typedefstruct

  3. {

  4. u16 width;//LCD宽度

  5. u16 height;//LCD高度

  6. u16 id;//LCD ID

  7. u8 dir;//横屏还是竖屏控制:0,竖屏;1:横屏。

  8. u16 wramcmd;//开始写gram指令

  9. u16 setxcmd;//设置x坐标指令

  10. u16 setycmd;//设置y坐标指令

  11. }_lcd_dev;

  12. //LCD参数

  13. extern _lcd_dev lcddev;//管理LCD重要参数

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

LCD横竖屏状态等,这个结构体虽然占用了10个字节的内容,但是却可以让我们的

驱动函数支持不同尺寸的LCD,同时可以实现LCD横竖屏切换等重要功能,所以还

是利大于弊的。

 

    lcd.c里面的一些重要函数:

先看7个简单,但很重要的函数

 
  1. //写寄存器函数

  2. //regval:寄存器值

  3. void LCD_WR_REG(u16 regval)

  4. {

  5. LCD->LCD_REG = regval;//写入要写的寄存器序号

  6. }

  7. //写LCD数据

  8. //data:要写入的数据

  9. void LCD_WR_DATA(u16 data)

  10. {

  11. LCD->LCD_RAM = data;

  12. }

  13. //读LCD数据

  14. //返回值:读到的数据

  15. u16 LCD_RD_DATA(void)

  16. {

  17. vu16 ram;//防止被优化

  18. ram = LCD->LCD_RAM;

  19. return ram;

  20. }

  21. //写寄存器

  22. //LCD_Reg:寄存器地址

  23. //LCD_RegValue:要写入的数据

  24. void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue)

  25. {

  26. LCD->LCD_REG = LCD_Reg;//要写入的寄存器序号

  27. LCD->LCD_RAM = LCD_RegValue;//要写入的数据

  28. }

  29. //读寄存器

  30. //LCD_Reg:寄存器地址

  31. //返回值:读到的数据

  32. u16 LCD_ReadReg(u16 LCD_Reg)

  33. {

  34. LCD_WR_REG(LCD_Reg);//写入要读的寄存器序号

  35. delay_us(5);

  36. return LCD_RD_DATA();//返回读到的值

  37. }

  38. //开始写GRAM

  39. void LCD_WriteRAM_Prepare(void)

  40. {

  41. LCD->LCD_REG = lcddev.wramcmd;

  42. }

  43. //LCD写GRAM

  44. //RGB_Code:颜色值

  45. void LCD_WriteRAM(u16 RGB_Code)

  46. {

  47. LCD->LCD_RAM = RGB_Code;//写十六位GRAM

  48. }

    因为FSMC自动控制了WR/RD/CS等这些信号,所以这7个函数实现起来很简单。

    注意:上面有几个函数,我们添加了一些对MDK-O2优化的支持,去掉的话,在-O2

优化时会出问题。

    通过这几个简单函数的组合,我们可以对LCD进行各种操作了。

    第八个函数,坐标设置函数

 
  1. //设置光标位置

  2. //Xpos:横坐标

  3. //Ypos:纵坐标

  4. void LCD_SetCursor(u16 Xpos, u16 Ypos)

  5. {

  6. if(lcddev.id ==0x9341|| lcddev.id ==0x5310)

  7. {

  8. LCD_WR_REG(lcddev.setxcmd);

  9. LCD_WR_DATA(Xpos>>8);

  10. LCD_WR_DATA(Xpos&0xff);

  11. LCD_WR_REG(lcddev.setycmd);

  12. LCD_WR_DATA(Ypos>>8);

  13. LCD_WR_DATA(Ypos&0xff);

  14. }

  15. elseif(lcddev.id ==0x6804)

  16. {

  17. if(lcddev.dir ==1)

  18. Xpos= lcddev.width -1-Xpos;//横屏时处理

  19. LCD_WR_REG(lcddev.setxcmd);

  20. LCD_WR_DATA(Xpos>>8);

  21. LCD_WR_DATA(Xpos&0xff);

  22. LCD_WR_REG(lcddev.setycmd);

  23. LCD_WR_DATA(Ypos>>8);

  24. LCD_WR_DATA(Ypos&0xff);

  25. }

  26. elseif(lcddev.id ==0x1963)

  27. {

  28. if(lcddev.dir ==0)//X坐标需要变换

  29. {

  30. Xpos= lcddev.width -1-Xpos;//横屏时处理

  31. LCD_WR_REG(lcddev.setxcmd);

  32. LCD_WR_DATA(0);

  33. LCD_WR_DATA(0);

  34. LCD_WR_DATA(Xpos>>8);

  35. LCD_WR_DATA(Xpos&0xff);

  36. }

  37. else

  38. {

  39. LCD_WR_REG(lcddev.setxcmd);

  40. LCD_WR_DATA(Xpos>>8);

  41. LCD_WR_DATA(Xpos&0xff);

  42. LCD_WR_DATA((lcddev.width -1)>>8);

  43. LCD_WR_DATA((lcddev.width -1)&0xff);

  44. }

  45. LCD_WR_REG(lcddev.setycmd);

  46. LCD_WR_DATA(Ypos>>8);

  47. LCD_WR_DATA(Ypos&0XFF);

  48. LCD_WR_DATA((lcddev.height-1)>>8);

  49. LCD_WR_DATA((lcddev.height-1)&0XFF);

  50. }

  51. elseif(lcddev.id ==0x5510)

  52. {

  53. LCD_WR_REG(lcddev.setxcmd);

  54. LCD_WR_DATA(Xpos>>8);

  55. LCD_WR_REG(lcddev.setxcmd+1);

  56. LCD_WR_DATA(Xpos&0XFF);

  57. LCD_WR_REG(lcddev.setycmd);

  58. LCD_WR_DATA(Ypos>>8);

  59. LCD_WR_REG(lcddev.setycmd+1);

  60. LCD_WR_DATA(Ypos&0XFF);

  61. }

  62. else

  63. {

  64. if(lcddev.dir==1)

  65. Xpos=lcddev.width-1-Xpos;//横屏其实就是调转 x,y 坐标

  66. LCD_WriteReg(lcddev.setxcmd,Xpos);

  67. LCD_WriteReg(lcddev.setycmd,Ypos);

  68. }

  69. }

    该函数非常重要,其实现了将LCD的当前操作点设置到指定坐标(x,y),有了该函数,

我们可以在液晶上任意作图了。

    这里面的lcddev.setxcmd、lcddev.setycmd、lcddev.width、lcddev.height等指令/

参数都是在LCD_Display_Dir函数里面初始化的,该函数根据lcddev.id的不同,执行不同设置。

    由于9341/5310/6804/1963/5510等的设置通其他屏有些不同,故区别对待。

第九个函数:画点函数

 
  1. //画点

  2. //x,y:坐标

  3. //POINT_COLOR:此点的颜色

  4. void LCD_DrawPoint(u16 x, u16 y)

  5. {

  6. LCD_SetCursor(x, y);//设置光标位置

  7. LCD_WriteRAM_Prepare();//开始写入GRAM

  8. LCD->LCD_RAM = POINT_COLOR;

  9. }

    该函数实现比较简单,先设置坐标,再向坐标写颜色。其他几乎所有上层函数,都通过调用

这个函数实现。

    其中POINT_COLOR是我们定义的一个全局变量,用于存放画笔颜色。还有另一个全局变量:

BACK_COLOR,代表背景色。

第十个函数:读点函数:

    用于读取LCD的GRAM。

    注:为什么OLED模块没有读GRAM函数,而此处需要?

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

OLED模块多很多,以16位色计算,一款320x240的液晶,需要320x240x2个字节来存储颜色值,也

就是需要150k字节,这对任何一款单片机来说,都不是小数目。

    而我们在图形叠加时,可以先读回原来的值,再写入新值,在完成叠加后,又恢复原来的值。这样

在做一些简单菜单时,很有用。

    这里的读点函数返回值为读到的GRAM的值。使用之前要先设置读取的GRAM地址,通过

LCD_SetCursor函数实现。

    LCD_ReadPoint代码如下:

 
  1. //func:读取某个点的颜色值

  2. //入参:x, y:坐标

  3. //出参:此点的颜色

  4. u16 LCD_ReadPoint(u16 x, u16 y)

  5. {

  6. vu16 r = 0, g = 0, b = 0;

  7. if(x >= lcddev.width || y >= lcddev.height)

  8. return 0; //超过了范围,直接返回

  9. LCD_SetCursor(x, y);

  10. if(lcddev.id == 0x9341 || lcddev.id == 0x6804 ||

  11. lcddev.id == 0x5310 || lcddev.id == 0x1963)

  12. LCD_WR_REG(0x2e); //9341、6804、3510、1963 发送读GRAM指令

  13. else if(lcddev.id == 0x5510)

  14. LCD_WR_REG(0x2e00); //5510 发送读GRAM指令

  15. else

  16. LCD_WR_REG(0x22); //其他IC,发送读GRAM指令

  17.  
  18. if(lcddev.id == 0x9320)

  19. opt_delay(2); //for 9320,延时2us

  20. r = LCD_RD_DATA(); //dummy Read 假读

  21.  
  22. if(lcddev.id == 0x1963)

  23. return r; //1963直接读就可以

  24. opt_delay(2);

  25. r = LCD_RD_DATA(); //实际坐标颜色

  26. if(lcddev.id == 0x9341 || lcddev.id == 0x5310 || lcddev.id == 0x5510) //这些LCD要分2次读出

  27. {

  28. opt_delay(2);

  29. b = LCD_RD_DATA();

  30. g = r & 0xff; //对于9341、5310、5510,第一次读取的是RG值,先R后G,各占8位

  31. g <<= 8;

  32. }

  33. if(lcddev.id == 0x9325 || lcddev.id == 0x4535 || lcddev.id == 0x4531 ||

  34. lcddev.id == 0xb505 || lcddev.id == 0xc505)

  35. return r; //这几种IC,直接返回颜色值

  36. else if(lcddev.id == 0x9341 || lcddev.id == 0x5310 || lcddev.id == 0x5510)

  37. return (((r >> 11) << 11) | ((g >> 10) << 5) | (b >> 11)); //ILI9341、NT35310、NT3510需要公式转换一下

  38. else

  39. return LCD_BGR2RGB(r); //其他IC

  40. }

第十一个函数:LCD_ShowChar: 

    该函数同前面OLED模块的字符显示函数差不多,但此处字符显示函数多了一个功能,

即以叠加/非叠加方式显示。    叠加方式显示多用于在显示的图片上再显示字符。非叠加

方式一般用于普通的显示。

 
  1. //在指定地址显示一个字符

  2. //x,y:起始坐标

  3. //num:要显示的字符:“ ”--->"~"

  4. //size:字体大小 12/16/24

  5. //mode:叠加方式(1),或非叠加方式(0)

  6. void LCD_ShowChar(u16 x, u16 y, u8 num, u8 size, u8 mode)

  7. {

  8. u8 temp, t1, t;

  9. u16 y0 = y;

  10. u8 csize = (size/8 + ((size % 8) ? 1:0) * (size/2)); //得到字体一个字符对应点阵集所占的字节数

  11. num = num - ''; //ASCII字库从空格开始取模,所以-' '即得到对应字符的字库(点阵)

  12. for(t = 0; t < csize; t++)

  13. {

  14. if(size == 12)

  15. temp = asc2_1206[num][t]; //调用1206字体

  16. else if(size == 16)

  17. temp = asc2_1608[num][t]; //调用1608字体

  18. else if(size == 24)

  19. temp = asc2_2412[num][t]; //调用2412字体

  20. else

  21. return; //没有字库

  22. for(t1 = 0; t1 < 8; t1++)

  23. {

  24. if(temp & 0x80)

  25. LCD_Fast_DrawPoint(x, y, POINT_COLOR);

  26. else if(mode == 0)

  27. LCD_Fast_DrawPoint(x, y, BACK_COLOR);

  28. temp <<= 1;

  29. y++;

  30. if(y >= lcddev.height)

  31. return; //超区域了

  32. if((y - y0) == size)

  33. {

  34. y = y0;

  35. x++;

  36. if(x >= lcddev.width)

  37. return; //超区域了

  38. break;

  39. }

  40. }

  41. }

  42. }

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

符。该函数同LCD_DrawPoint一样,只是带了颜色参数,且减少了函数的调用时间。该代码

在我们用到的3个字符集点阵数据数组asc2_2412\asc2_1206\asc2_1608,这几个字符集的点

阵数据的提取方式,同17章相同。

最后,介绍TFTLCD模块的初始化函数LCD_Init:

    该函数先初始化stm32与TFTLCD连接的IO口,并配置FSMC控制器,然后读取LCD控制器

的型号,根据型号来执行不同的初始化代码。

      从初始化代码可以看出,LCD初始化步骤为①~⑤在代码中的标注:

① GPIO、FSMC、AFIO时钟使能;

② GPIO初始化:GPIO_Init()函数;

③ FSMC初始化:FSMC_NORSRAMInit()函数;

④ FSMC使能:FSMC_NORSRAMCmd()函数;

⑤ 不同的LCD驱动器的初始化代码。

 
  1. //初始化LCD

  2. //注:改初始化函数可以初始化各种ILI93XX液晶,但是其他函数是基于ILI9320的!!!

  3. //在其他型号的驱动芯片上没有测试!

  4. void LCD_Init(void)

  5. {

  6. GPIO_InitTypeDef GPIO_InitStruct;

  7. FSMC_NORSRAMInitTypeDef FSMC_NSInitStructure;

  8. FSMC_NORSRAMTimingInitTypeDef readWriteTiming;

  9. FSMC_NORSRAMTimingInitTypeDef writeTiming;

  10. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE); //使能 FSMC 时钟

  11. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOD|

  12. RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOG|

  13. RCC_APB2Periph_AFIO, ENABLE); // ①使能GPIO 以及AFIO 复用功能时钟

  14. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PB0 推挽输出 背光

  15. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

  16. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  17. GPIO_Init(GPIOB, &GPIO_InitStructure); //②初始化 PB0

  18. //PORTD 复用推挽输出

  19. GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5|

  20. GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_14|GPIO_Pin_15;

  21. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出

  22. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  23. GPIO_Init(GPIOD, &GPIO_InitStructure); //②初始化 PORTD

  24. //PORTE 复用推挽输出

  25. GPIO_InitStructure.GPIO_Pin =GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|

  26. GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;

  27. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出

  28. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  29. GPIO_Init(GPIOE, &GPIO_InitStructure); //②初始化 PORTE

  30. //PORTG12 复用推挽输出 A0

  31. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_12; //PORTD

  32. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_12; //PORTD 复用推挽输出

  33. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出

  34. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  35. GPIO_Init(GPIOG, &GPIO_InitStructure); //②初始化 PORTG

  36. readWriteTiming.FSMC_AddressSetupTime = 0x01; //地址建立时间 2 个 HCLK 1

  37. readWriteTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间模式 A 未用到

  38. readWriteTiming.FSMC_DataSetupTime = 0x0f; // 数据保存时间为 16 个 HCLK

  39. readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;

  40. readWriteTiming.FSMC_CLKDivision = 0x00;

  41. readWriteTiming.FSMC_DataLatency = 0x00;

  42. readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式 A

  43.  
  44. writeTiming.FSMC_AddressSetupTime = 0x00; //地址建立时间为 1 个 HCLK

  45. writeTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间( A

  46. writeTiming.FSMC_DataSetupTime = 0x03; //数据保存时间为 4 个 HCLK

  47. writeTiming.FSMC_BusTurnAroundDuration = 0x00;

  48. writeTiming.FSMC_CLKDivision = 0x00;

  49. writeTiming.FSMC_DataLatency = 0x00;

  50. writeTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式 A

  51. FSMC_NSInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4; //这里我们使用NE4,也就对应BTCR[6],[7]。

  52. FSMC_NSInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; //不复用数据地址

  53. FSMC_NSInitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM; // SRAM

  54. FSMC_NSInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; //存储器数据宽度为 16bit

  55. FSMC_NSInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;

  56. FSMC_NSInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;

  57. FSMC_NSInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;

  58. FSMC_NSInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;

  59. FSMC_NSInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;

  60. FSMC_NSInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; //存储器写使能

  61. FSMC_NSInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;

  62. FSMC_NSInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable;

  63. // 读写使用不同的时序

  64. FSMC_NSInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;

  65. FSMC_NSInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;

  66. FSMC_NSInitStructure.FSMC_WriteTimingStruct = &writeTiming; //写时序

  67. FSMC_NORSRAMInit(&FSMC_NSInitStructure); //③初始化FSMC 配置

  68. FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE); //④使能BANK1

  69. delay_ms(50); // delay 50 ms

  70.  
  71. lcddev.id = LCD_ReadReg(0x0000); //读ID(9320\9325\9328\4531\4535等IC)

  72. if(lcddev.id < 0xff || lcddev.id == 0xffff || lcddev.id == 0x9300)

  73. //ID不正确,新增0x9300判断,因为9341在未被复位时,会被读成9300

  74. {

  75. //尝试9341 ID的读取

  76. LCD_WR_REG(0xd3);

  77. lcddev.id = LCD_RD_DATA(); //dummy read

  78. lcddev.id = LCD_RD_DATA(); //读到0x00

  79. lcddev.id = LCD_RD_DATA(); //读到93

  80. lcddev.id <<= 8;

  81. lcddev.id |= LCD_RD_DATA(); //读取41

  82. if(lcddev.id != 0x9341) //非9341,尝试是不是6804

  83. {

  84. LCD_WR_REG(0xBF);

  85. lcddev.id = LCD_RD_DATA(); //dummy read

  86. lcddev.id = LCD_RD_DATA(); //读到0x01

  87. lcddev.id = LCD_RD_DATA(); //读到0xD0

  88. lcddev.id = LCD_RD_DATA(); //读到0x68

  89. lcddev.id <<= 8;

  90. lcddev.id |= LCD_RD_DATA(); //读取0x04

  91. if(lcddev.id != 0x6804) //也不是6804,尝试NT35310

  92. {

  93. LCD_WR_REG(0xd4);

  94. lcddev.id = LCD_RD_DATA(); //dummy read

  95. lcddev.id = LCD_RD_DATA(); //读到0x01

  96. lcddev.id = LCD_RD_DATA(); //读到0x53

  97. lcddev.id <<= 8;

  98. lcddev.id |= LCD_RD_DATA(); //读取0x10

  99. if(lcddev.id != 0x5310) //也不是NT35310,尝试NT35110

  100. {

  101. LCD_WR_REG(0xda00);

  102. lcddev.id = LCD_RD_DATA(); //读回0x00

  103. LCD_WR_REG(0xdb00);

  104. lcddev.id = LCD_RD_DATA(); //读回0x80

  105. lcddev.id <<= 8;

  106. LCD_WR_REG(0xdc00);

  107. lcddev.id |= LCD_RD_DATA(); //读回0x00

  108. if(lcddev.id == 0x8000)

  109. lcddev.id == 0x5510; //NT35510读回的ID是8000H,为方便区分,我们强制设置为5510

  110. if(lcddev.id != 0x5510) //也不是5510,尝试SSD1963

  111. {

  112. LCD_WR_REG(0xa1);

  113. lcddev.id = LCD_RD_DATA();

  114. lcddev.id = LCD_RD_DATA(); //读回0x57

  115. lcddev.id <<= 8;

  116. lcddev.id |= LCD_RD_DATA(); //读回0x61

  117. if(lcddev.id == 0x5761)

  118. lcddev.id == 0x1963; //SSD1963读回的ID是5761H,为方便区分,我们强制设置为1963

  119. }

  120. }

  121. }

  122. }

  123. }

  124. printf(" LCD ID:%x\r\n",lcddev.id); //打印 LCD ID

  125. if(lcddev.id==0X9341) //9341 初始化

  126. {

  127. ……//9341 初始化代码

  128. }

  129. else if(lcddev.id==0xXXXX) //其他 LCD 初始化代码

  130. {

  131. ……//其他 LCD 驱动 IC,初始化代码

  132. }

  133. LCD_Display_Dir(0); //默认为竖屏显示

  134. LCD_LED=1; //点亮背光

  135. LCD_Clear(WHITE);

  136. }

main.c:

    该部分代码将显示一些固定的字符,字体大小包括24*12、16*8和12*6等三种,同时显示

LCD驱动IC的型号,然后不停地切换背景颜色,每秒切换一次。而LED0也不停地闪烁,指示

程序已经运行了。

    其中用到了一个sprintf函数,该函数用法同printf,只是sprintf把打印内容输出到指定的内存

区间上,sprintf的详细用法,请百度。

 
  1. int main(void)

  2. {

  3. u8 x = 0;

  4. u8 lcd_id[12]; //存放LCD ID字符串

  5. delay_init(); //延时函数初始化

  6. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2

  7. uart_init(115200); //串口初始化波特率115200

  8. LED_Init(); //LED初始化

  9. LCD_Init();

  10. POINT_COLOR = RED;

  11. sprintf((char *)lcd_id, "LCD ID:%04X", lcddev.id); //将LCD ID打印到lcd_id数组

  12. while(1)

  13. {

  14. case 0:LCD_Clear(WHITE); break;

  15. case 1:LCD_Clear(BLACK); break;

  16. case 2:LCD_Clear(BLUE); break;

  17. case 3:LCD_Clear(RED); break;

  18. case 4:LCD_Clear(MAGENTA); break;

  19. case 5:LCD_Clear(GREEN); break;

  20. case 6:LCD_Clear(CYAN); break;

  21. case 7:LCD_Clear(YELLOW); break;

  22. case 8:LCD_Clear(BRRED); break;

  23. case 9:LCD_Clear(GRAY); break;

  24. case 10:LCD_Clear(LGRAY); break;

  25. case 11:LCD_Clear(BROWN); break;

  26. }

  27. POINT_COLOR=RED;

  28. LCD_ShowString(30,40,210,24,24,"WarShip STM32 ^_^");

  29. LCD_ShowString(30,70,200,16,16,"TFTLCD TEST");

  30. LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");

  31. LCD_ShowString(30,110,200,16,16,lcd_id); //显示 LCD ID

  32. LCD_ShowString(30,130,200,12,12,"2014/5/4");

  33. x++;

  34. if(x == 12)

  35. x = 0;

  36. LED0 = !LED0

  37. delay_ms(1000);

  38. }

18.4下载验证

    将程序下载到战舰STM32后,可以看到DS0不停地闪烁,提示程序已经在运行了。同时可以

看到TFTLCD模块显示如下图所示:

     可以看到屏幕背景不停地切换,同时DS0不停地闪烁,证明我们的代码被正确地

执行了。

    另外,本例程除了不支持CPLD方案的7寸屏模块,其余所有的ALIENTEK TFTLCD

模块都可以支持,直接插上去即可使用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值