你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

查看: 8637|回复: 0

【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第十八章 TFTLCD显示实验

[复制链接]

50

主题

7

回帖

0

蝴蝶豆

新手上路

最后登录
1970-1-1
发表于 2013-1-25 18:32:12 | 显示全部楼层 |阅读模式
第十八章 TFTLCD显示实验

上一章我们介绍了OLED模块及其显示,但是该模块只能显示单色/双色,不能显示彩色,而且尺寸也较小。本章我们将介绍ALIENTEK 2.8寸TFT LCD模块,该模块采用TFTLCD面板,可以显示16位色的真彩图片。在本章中,我们将使用战舰STM32开发板上的LCD接口,来点亮TFTLCD,并实现ASCII字符和彩色的显示等功能,并在串口打印LCD控制器ID,同时在LCD上面显示。本章分为如下几个部分:

18.1 TFTLCD简介

18.2 硬件设计

18.3 软件设计

18.4 下载验证

18.1 TFTLCD&FSMC简介
本章我们将通过STM32的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’3种大小的屏幕可选。

2,320×240的分辨率(3.5’分辨率为:320*480)。

3,16位真彩显示。

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

本章,我们以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模块采用2*17的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.3还列出了触摸屏芯片的接口,关于触摸屏本章我们不多介绍,后面的章节会有详细的介绍。该模块的80并口有如下一些信号线:

       CS:TFTLCD片选信号。

       WR:向TFTLCD写入数据。

       RD:从TFTLCD读取数据。

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

       RST:硬复位TFTLCD。

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

80并口在上一节我们已经有详细的介绍了,这里我们就不再介绍,需要说明的是,TFTLCD模块的RST信号线是直接接到STM32的复位脚上,并不由软件控制,这样可以省下来一个IO口。另外我们还需要一个背光控制线来控制TFTLCD的背光。所以,我们总共需要的IO口数目为21个。这里还需要注意,我们标注的DB1~DB8,DB10~DB17,是相对于LCD控制IC标注的,实际上大家可以把他们就等同于D0~D15,这样理解起来就比较简单一点。

ALIENTEK提供的2.8寸TFTLCD模块,其驱动芯片有很多种类型,比如有:ILI9320/ILI9325

/ILI9328/ILI9341/SSD1289/LGDP4531/LGDP4535/R61505/ SPFD5408/ RM68021等(具体的型号,大家可以通过下载本章实验代码,通过串口或者LCD显示查看),这里我们仅以ILI9320控制器为例进行介绍,其他的控制基本都类似,我们就不详细阐述了。

ILI9320液晶控制器自带显存,其显存总大小为172820(240*320*18/8),即18位模式(26万色)下的显存量。模块的16位数据线与显寸的对应关系为565方式,如图18.1.1.4所示:




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

最低5位代表蓝色,中间6位为绿色,最高5位为红色。数值越大,表示该颜色越深。

接下来,我们介绍一下ILI9320的几个重要命令,因为ILI9320的命令很多,我们这里不可能一一介绍,有兴趣的大家可以找到ILI9320的datasheet看看。里面对这些命令有详细的介绍。这里我们要介绍的命令列表如表18.1.1.1所示:


表18.1.1.1 ILI9320常用命令表

R0,这个命令,有两个功能,如果对它写,则最低位为OSC,用于开启或关闭振荡器。而如果对它读操作,则返回的是控制器的型号。这个命令最大的功能就是通过读它可以得到控制器的型号,而我们代码在知道了控制器的型号之后,可以针对不同型号的控制器,进行不同的初始化。因为93xx系列的初始化,其实都比较类似,我们完全可以用一个代码兼容好几个控制器。

R3,入口模式命令。我们重点关注的是I/D0、I/D1、AM这3个位,因为这3个位控制了屏幕的显示方向。

AM:控制GRAM更新方向。当AM=0的时候,地址以行方向更新。当AM=1的时候,地址以列方向更新。

I/D[1:0]:当更新了一个数据之后,根据这两个位的设置来控制地址计数器自动增加/减少1,

其关系如图18.1.1.5所示:


 

图18.1.1.5  GRAM显示方向设置图

通过这几个位的设置,我们就可以控制屏幕的显示方向了,这种方法虽然简单,但是不是很通用,比如不同的液晶,可能这里差别就比较大,有的甚至无法通用!比如9341和9320就完全不通用。

R7,显示控制命令。该命令CL位用来控制是8位彩色,还是26万色。为0时26万色,为1时八位色。D1、D0、BASEE这三个位用来控制显示开关与否的。当全部设置为1的时候开启显示,全0是关闭。我们一般通过该命令的设置来开启或关闭显示器,以降低功耗。

R32,R33,设置GRAM的行地址和列地址。R32用于设置列地址(X坐标,0~239),R33用于设置行地址(Y坐标,0~319)。当我们要在某个指定点写入一个颜色的时候,先通过这两个命令设置到改点,然后写入颜色值就可以了。

R34,写数据到GRAM命令,当写入了这个命令之后,地址计数器才会自动的增加和减少。该命令是我们要介绍的这一组命令里面唯一的单个操作的命令,只需要写入该值就可以了,其他的都是要先写入命令编号,然后写入操作数。

R80~R83,行列GRAM地址位置设置。这几个命令用于设定你显示区域的大小,我们整个屏的大小为240*320,但是有时候我们只需要在其中的一部分区域写入数据,如果用先写坐标,后写数据这样的方式来实现,则速度大打折扣。此时我们就可以通过这几个命令,在其中开辟一个区域,然后不停的丢数据,地址计数器就会根据R3的设置自动增加/减少,这样就不需要频繁的写地址了,大大提高了刷新的速度。

命令部分,我们就为大家介绍到这里,我们接下来看看要如何才能驱动ALIENTEK TFTLCD模块,这里TFTLCD模块的初始化和我们前面介绍的OLED模块的初始化框图是一样的,只是初始化代码部分不同。接下来我们也是将该模块用来来显示字符和数字。通过以上介绍,我们可以得出TFTLCD显示需要的相关设置步骤如下:

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

这一步,先将我们与TFTLCD模块相连的IO口进行初始化,以便驱动LCD。这里我们用到的是FSMC,FSMC将在18.1.2节向大家详细介绍。

2)初始化TFTLCD模块。

其实这里就是上和上面OLED模块的初始化过程差不多。通过向TFTLCD写入一系列的设置,来启动TFTLCD的显示。为后续显示字符和数字做准备。

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

这里就是通过我们设计的程序,将要显示的字符送到TFTLCD模块就可以了,这些函数将在软件设计部分向大家介绍。通过以上三步,我们就可以使用ALIENTEK TFTLCD模块来显示字符和数字了, 并且可以显示各种颜色的背景。

18.1.2 FSMC简介
大容量,且引脚数在100脚以上的STM32F103芯片都带有FSMC接口,ALIENTEK战舰STM32开发板的主芯片为STM32F103ZET6,是带有FSMC接口的。

FSMC,即灵活的静态存储控制器,能够与同步或异步存储器和16位PC存储器卡接口,STM32的FSMC接口支持包括SRAM、NAND FLASH、NOR FLASH和PSRAM等存储器。FSMC的框图如图18.1.2.1所示:


图18.1.2.1 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来说,就是写命令。而FSMC写地址1的时候,A0将会变为1,对TFTLCD来说,就是写数据了。这样,就把数据和命令区分开了,他们其实就是对应SRAM操作的两个连续地址。当然RS也可以接在其他地址线上,战舰STM32开发板是把RS连接在A10上面的。

STM32的FSMC支持8/16/32位数据宽度,我们这里用到的LCD是16位宽度的,所以在设置的时候,选择16位宽就OK了。我们再来看看FSMC的外部设备地址映像,STM32的FSMC将外部存储器划分为固定大小为256M字节的四个存储块,如图18.1.2.2所示:


图18.1.2.2 FSMC存储块地址映像

从上图可以看出,FSMC总共管理1GB空间,拥有4个存储块(Bank),本章,我们用到的是块1,所以在本章我们仅讨论块1的相关配置,其他块的配置,请参考《STM32参考手册》第19章(324页)的相关介绍。

       STM32的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[24:0]。

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

不论外部接8位/16位宽设备,FSMC_A[0]永远接在外部设备地址A[0] 这里,TFTLCD使用的是16位数据宽度,所以HADDR[0]并没有用到,只有HADDR[25:1]是有效的,对应关系变为:HADDR[25:1]à FSMC[24:0],相当于右移了一位,这里请大家特别留意。另外,HADDR[27:26]的设置,是不需要我们干预的,比如:当你选择使用Bank1的第三个区,即使用FSMC_NE3来连接外部设备的时候,即对应了HADDR[27:26]=10,我们要做的就是配置对应第3区的寄存器组,来适应外部设备即可。STM32的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写操作时序

       从模式A的读写时序图,我们可以看出,读操作还存在额外的2个HCLK周期,用于数据存储,所以同样的配置读操作一般比写操作会慢一点。图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 +1)个HCLK时钟周期,最大为256个HCLK周期。对ILI9320来说,其实就是RD低电平持续时间,一般为150ns。而一个HCLK时钟周期为13.8ns左右(1/72Mhz),为了兼容其他屏,我们这里设置DATAST为15,也就是16个HCLK周期,时间大约是234ns(未计算数据存储的2个HCLK时间)。

       ADDSET[3:0]:地址建立时间。其建立时间为:(ADDSET+1)个HCLK周期,最大为16个HCLK周期。对ILI9320来说,这里相当于RD高电平持续时间,本来这里我们应该设置和DATAST一样,但是由于CS切换延时的存在,我们这里可以设置ADDSET为较小的值,本章我们设置ADDSET为1,即2个HCLK周期,同样可以正常使用。

最后,我们再来看看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则对应低电平和高电平持续时间,对ILI9320来说,这两个时间只需要50ns就够了,比读操作快得多。所以我们这里设置DATAST为3,即4个HCLK周期,时间约为55ns。同样由于CS切换延时的存在,我们可以设置ADDSET为0,即1个HCLK周期。

       至此,我们对STM32的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]保留,没有用到。


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

1)  指示灯DS0

2)  TFTLCD模块

TFTLCD模块的电路见图18.1.1.2,这里我们介绍TFTLCD模块与ALIETEK 战舰STM32开发板的连接,战舰STM32开发板底板的LCD接口和ALIENTEK TFTLCD模块直接可以对插,连接关系如图18.2.1所示:


 

 

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

图18.2.1中圈出来的部分就是连接TFTLCD模块的接口,板上的接口比液晶模块的插针要多2个口,液晶模块在这里是靠右插的。多出的2个口是给OLED用的,所以OLED模块在接这里的时候,是靠左插的,这个请大家注意一下。

在硬件上,TFTLCD模块与战舰STM32开发板的IO口对应关系如下:

       LCD_BL(背光控制)对应PB0;

       LCD_CS对应PG12即FSMC_NE4;

       LCD _RS对应PG0即FSMC_A10;

       LCD _WR对应PD5即FSMC_NWE;

       LCD _RD对应PD4即FSMC_NOE;

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

这些线的连接,战舰STM32开发板的内部已经连接好了,我们只需要将TFTLCD模块插上去就好了。实物连接如图18.2.2所示:


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

18.3 软件设计
软件设计我们依旧在之前的工程上面增加,首先在HARDWARE文件夹下新建一个LCD的文件夹。然后打开USER文件夹下的工程,新建一个ILI93xx.c的文件和lcd.h的头文件,保存在LCD文件夹下,并将LCD文件夹加入头文件包含路径。

在ILI93xx.c里面要输入的代码比较多,我们这里就不贴出来了,只针对几个重要的函数进行讲解。完整版的代码见光盘à4,程序源码à标准例程à实验13 TFTLCD显示实验的ILI93xx.c文件。

本实验,我们用到FSMC驱动LCD,通过前面的介绍,我们知道TFTLCD的RS接在FSMC的A10上面,CS接在FSMC_NE4上,并且是16位数据总线。即我们使用的是FSMC存储器1的第4区,我们定义如下LCD操作结构体(在lcd.h里面定义):

//LCD操作结构体

typedef struct

{

       u16 LCD_REG;

       u16 LCD_RAM;

} LCD_TypeDef;

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

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

#define LCD_BASE        ((u32)(0x6C000000 | 0x000007FE))

#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写命令/数据的时候,可以这样写:

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 ID

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

       u8    wramcmd;             //开始写gram指令

       u8  setxcmd;                //设置x坐标指令

       u8  setycmd;                //设置y坐标指令  

}_lcd_dev;

//LCD参数

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

该结构体用于保存一些LCD重要参数信息,比如LCD的长宽、LCD ID(驱动IC型号)、LCD横竖屏状态等,这个结构体虽然占用了10个字节的内存,但是却可以让我们的驱动函数支持不同尺寸的LCD,同时可以实现LCD横竖屏切换等重要功能,所以还是利大于弊的。有了以上了解,下面我们开始介绍ILI93xx.c里面的一些重要函数。

先看6个简单,但是很重要的函数:

//写寄存器函数

//regval:寄存器值

void LCD_WR_REG(u16 regval)

{

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

}

//写LCD数据

//data:要写入的值

void LCD_WR_DATA(u16 data)

{                                                                                 

       LCD->LCD_RAM=data;              

}

//读LCD数据

//返回值:读到的值

u16 LCD_RD_DATA(void)

{                                                                                 

       return LCD->LCD_RAM;            

}                                    

//写寄存器

//LCD_Reg:寄存器地址

//LCD_RegValue:要写入的数据

void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue)

{     

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

       LCD->LCD_RAM = LCD_RegValue;//写入数据                    

}        

//读寄存器

//LCD_Reg:寄存器地址

//返回值:读到的数据

u16 LCD_ReadReg(u8 LCD_Reg)

{                                                                       

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

       delay_us(5);            

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

}   

//开始写GRAM

void LCD_WriteRAM_Prepare(void)

{

      LCD->LCD_REG=lcddev.wramcmd;      

}     

因为FSMC自动控制了WR/RD/CS等这些信号,所以这6个函数实现起来都非常简单,我们就不多说,实现功能见函数前面的备注,通过这几个简单函数的组合,我们就可以对LCD进行各种操作了。

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

//设置光标位置

//Xpos:横坐标

//Ypos:纵坐标

void LCD_SetCursor(u16 Xpos, u16 Ypos)

{     

      if(lcddev.id==0X9341)

       {               

              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.dir==1)Xpos=lcddev.width-1-Xpos;//横屏其实就是调转x,y坐标

              LCD_WriteReg(lcddev.setxcmd, Xpos);

              LCD_WriteReg(lcddev.setycmd, Ypos);

       }     

}

该函数实现将LCD的当前操作点设置到指定坐标(x,y)。因为9341的设置同其他屏有些不太一样,所以单独对9341进行了设置。


 
该函数先对FSMC相关IO进行初始化,然后是FSMC的初始化,这个我们在前面都有介绍,最后根据读到的LCD ID,对不同的驱动器执行不同的初始化代码,从上面的代码可以看出,这个初始化函数可以针对13款不同的驱动IC执行初始化操作,这样大大提高了整个程序的通用性。大家在以后的学习中应该多使用这样的方式,以提高程序的通用性、兼容性。

特别注意:本函数使用了printf来打印LCD ID,所以,如果你在主函数里面没有初始化串口,那么将导致程序死在printf里面!!如果不想用printf,那么请注释掉它。

保存ILI93xx.c,并将该代码加入到HARDWARE组下。在介绍完了ILI93xx.c的内容之后,然后我们在lcd.h里面输入如下内容:

#ifndef __LCD_H

#define __LCD_H         

#include "sys.h"     

#include "stdlib.h"

//LCD重要参数集

typedef struct

{                                                                        

       u16 width;                    //LCD 宽度

       u16 height;                   //LCD 高度

       u16 id;                         //LCD ID

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

       u8    wramcmd;             //开始写gram指令

       u8  setxcmd;         //设置x坐标指令

       u8  setycmd;         //设置y坐标指令  

}_lcd_dev;      

//LCD参数

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

//LCD的画笔颜色和背景色   

extern u16 POINT_COLOR;//默认红色   

extern u16 BACK_COLOR; //背景颜色.默认为白色

//////////////////////////////////////////////////////////////////////////////////  

//-----------------LCD端口定义----------------

#define    LCD_LED PBout(0) //LCD背光              PB0         

//LCD地址结构体

typedef struct

{

       u16 LCD_REG;

       u16 LCD_RAM;

} LCD_TypeDef;

//使用NOR/SRAM BANK 4,地址位HADDR[27,26]=11 A10作为数据命令区分线

//注意设置时STM32内部会右移一位对其! 111110=0X3E                        

#define LCD_BASE        ((u32)(0x6C000000 | 0x000007FE))

#define LCD             ((LCD_TypeDef *) LCD_BASE)

//////////////////////////////////////////////////////////////////////////////////

//扫描方向定义

#define L2R_U2D 0 //从左到右,从上到下

#define L2R_D2U 1 //从左到右,从下到上

#define R2L_U2D 2 //从右到左,从上到下

#define R2L_D2U 3 //从右到左,从下到上

#define U2D_L2R 4 //从上到下,从左到右

#define U2D_R2L 5 //从上到下,从右到左

#define D2U_L2R 6 //从下到上,从左到右

#define D2U_R2L 7 //从下到上,从右到左      

#define DFT_SCAN_DIR  L2R_U2D //默认的扫描方向

//画笔颜色

#define WHITE              0xFFFF

……//省略部分

#define LBBLUE           0X2B12 //浅棕蓝色(选择条目的反色)         

void LCD_Init(void);                                                                    /初始化

void LCD_DisplayOn(void);                                                          //开显示

void LCD_DisplayOff(void);                                                         //关显示

void LCD_Clear(u16 Color);                                                       //清屏

void LCD_SetCursor(u16 Xpos, u16 Ypos);                                    //设置光标

void LCD_DrawPoint(u16 x,u16 y);                                               //画点

u16 LCD_ReadPoint(u16 x,u16 y);                                             //读点

void Draw_Circle(u16 x0,u16 y0,u8 r);                                                 //画圆

void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2);                    //画线

void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2);            //画矩形

void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color);                 //填充单色

void LCD_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color);        //填充指定颜色

void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode);            //显示一个字符

void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size);            //显示一个数字

void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode);//显示 数字

void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p);

//显示一个字符串,12/16字体

void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue);

u16 LCD_ReadReg(u8 LCD_Reg);

void LCD_WriteRAM_Prepare(void);

void LCD_WriteRAM(u16 RGB_Code);              

void LCD_Scan_Dir(u8 dir);                                           //设置屏扫描方向

void LCD_Display_Dir(u8 dir);                                       //设置屏幕显示方向            

//9320/9325 LCD寄存器  

#define R0             0x00

……//省略部分

#define R229           0xE5                                                           

#endif

这段代码的两个重要结构体定义,我们都在前面有介绍,其他的相对就比较简单了。另外这段代码对颜色和驱动器的寄存器进行了很多宏定义,限于篇幅考虑,我们没有完全贴出来,省略了其中绝大部分。此部分我们就不多说了。接下来,我们在test.c里面修改main函数如下:

int main(void)

{


 
      u8 x=0;

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

      Stm32_Clock_Init(9);    //系统时钟设置

       uart_init(72,9600);      //串口初始化为9600

       delay_init(72);                  //延时初始化

       LED_Init();                       //初始化与LED连接的硬件接口

      LCD_Init();

       POINT_COLOR=RED;

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

      while(1)

       {            

              switch(x)

              {

                     case 0CD_Clear(WHITE);break;

                     case 1CD_Clear(BLACK);break;

                     case 2CD_Clear(BLUE);break;

                     case 3CD_Clear(RED);break;

                     case 4CD_Clear(MAGENTA);break;

                     case 5CD_Clear(GREEN);break;

                     case 6CD_Clear(CYAN);break;

case 7CD_Clear(YELLOW);break;

                     case 8CD_Clear(BRRED);break;

                     case 9CD_Clear(GRAY);break;

                     case 10:LCD_Clear(LGRAY);break;

                     case 11:LCD_Clear(BROWN);break;

              }

              POINT_COLOR=RED;   

              LCD_ShowString(30,50,200,16,16,"WarShip STM32 ^_^");     

              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 ID                                                 LCD_ShowString(30,130,200,16,16,"2012/9/5");         

           x++;

              if(x==12)x=0;

              LED0=!LED0;                                   

              delay_ms(1000);   

       }

}

该部分代码将显示一些固定的字符,同时显示LCD驱动IC的型号,然后不停的切换背景颜色,每1s切换一次。而LED0也会不停的闪烁,指示程序已经在运行了。其中我们用到一个sprintf的函数,该函数用法同printf,只是sprintf把打印内容输出到指定的内存区间上,sprintf的详细用法,请百度。

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


18.4 下载验证
将程序下载到战舰STM32后,可以看到DS0不停的闪烁,提示程序已经在运行了。同时可以看到TFTLCD模块的显示如图18.4.1所示:


 

图18.4.1 TFTLCD显示效果图

我们可以看到屏幕的背景是不停切换的,同时DS0不停的闪烁,证明我们的代码被正确的执行了,达到了我们预期的目的。







 

《STM32开发指南》第十八章 TFTLCD显示实验.rar

下载

1.4 MB, 下载次数: 69, 下载积分: ST金币 -1

实验13 TFTLCD显示实验.rar

下载

75.89 KB, 下载次数: 39, 下载积分: ST金币 -1

回复

使用道具 举报

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版