TFTLCD显示实验_STM32F1开发指南_第十八章
来源:互联网 发布:java阅读软件 编辑:程序博客网 时间:2024/05/20 17:58
第十八章 TFTLCD显示实验
在LCD_ShowChar函数里面,我们采用快速画点函数LCD_Fast_DrawPoint来画点显示字
前言
上一章我们介绍了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硬复位TFTLCD6RS命令/数据标志(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总线D17D16D15D14D13D12D11D10D9D8D7D6D5D4D3D2D1D0MCU数据线
(16位)D15D14D13D12D11NCD10D9D8D7D6D5D4D3D2D1D0NCLCD_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位数据线与显存的对应关系(16位)D15D14D13D12D11NCD10D9D8D7D6D5D4D3D2D1D0NCLCD_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
从图中可以看出,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
PSRAMFSMC_BCR 1/2/3/4
FSMC_BTR 1/2/2/3
FSMC_BWTR 1/2/3/4NAND FLASH/
PC CARD
控制器Bank20X7000 0000~0X7fff ffffNAND FLASH
对于NOR FLASH控制器,主要是通过FSMC_BCRx、FSMC_BTRx和FSMC_BWTRx寄控制器Bank10x6000 0000~0x6fff ffffSRAM/ROM
NOR FLASH
PSRAMFSMC_BCR 1/2/3/4
FSMC_BTR 1/2/2/3
FSMC_BWTR 1/2/3/4NAND 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 ffffBank40X9000 0000~0X9fff ffffPC Card存器设置(其中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、ADDSETModeASRAM/ CRAM OE选通时序DATAST、ADDSETMode2/BNOR FLASH时序DATAST、ADDSETModeCNOR FLASH OE选通时序DATAST、ADDSETModeD延长地址保持时间的异步时序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),该寄存器各位描述如下:本章需要,所以该位为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,所以使能这个存储块。
这个寄存器包含了每个存储器块的控制信息,可以用于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),该寄存器各位描述如下: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。
该寄存器为写操作时序控制寄存器。
本章用到的几个位域和读操作时序一模一样,如下所示:
序号位域功能1ACCMOD[1:0]同FSMC_BTRx一样,选择模式A2DATAST[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,
在固件库中怎么初始化他们的函数分别为:
FSMC_NORSRAMInit(); //初始化NOR和SRAMFSMC_NANDInit();FSMC_PCCARDInit();
这三个函数分别用来初始化4种类型存储器,这里根据名字很好判断。
初始化NOR和SRAM的函数定义如下:
void FSMC_NORSRAMInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct);
这个函数只有一个入参,FSMC_NORSRAMInitTypeDef类型指针变量,其成员变量如下:
typedefstruct{uint32_t FSMC_Bank;uint32_t FSMC_DataAddressMux;uint32_t FSMC_MemoryType;uint32_t FSMC_MemoryDataWidth;uint32_t FSMC_BurstAccessMode;uint32_t FSMC_AsynchronousWait;uint32_t FSMC_WaitSignalPolarity;uint32_t FSMC_WrapMode;uint32_t FSMC_WaitSignalActive;uint32_t FSMC_WriteOperation;uint32_t FSMC_WaitSignal;uint32_t FSMC_ExtendedMode;uint32_t FSMC_WriteBurst; FSMC_NORSRAMTimingInitTypeDef* FSMC_ReadWriteTimingStruct; FSMC_NORSRAMTimingInitTypeDef* FSMC_WriteTimingStruct;}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区号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。
typedefstruct{uint32_t FSMC_AddressSetupTime;uint32_t FSMC_AddressHoldTime;uint32_t FSMC_DataSetupTime;uint32_t FSMC_BusTurnAroundDuration;uint32_t FSMC_CLKDivision;uint32_t FSMC_DataLatency;uint32_t FSMC_AccessMode;}FSMC_NORSRAMTimingInitTypeDef;
这个结构体有7个参数用来设置FSMC读写时序。这些参数主要是设计地址建立保持时间,数据建立时间等配置。
这些参数的意义在前面有讲解。
本章读写时序不同,读写速度要求不同,所以参数FSMC_DataSetupTime设置不同的值。
2.FSMC使能函数: FSMC对不同的存储器类型同样提供了不同的使能函数:
void FSMC_NORSRAMCmd(uint32_t FSMC_Bank,FunctionalStateNewState);void FSMC_NANDCmd (uint32_t FSMC_Bank,FunctionalStateNewState);void FSMC_PCCARDCmd (FunctionalStateNewState);
本章使用SRAM,所以使用第一个函数。
18.2 硬件设计
本实验用到的硬件有:
1)指示灯DS0;
2)TFTLCD模块;
TFTLCD模块的电路图如下所示:
在硬件上,TFTLCD模块与开发板上单片机的IO口对应关系如下:
LCD_BL -> PB0LCD_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
18.3 软件设计
本实验,LCD的RS接FSMC的A10上面,CS接FSMC_NE4,16位数据总线。即使用FSMC存储器
1的第四区,我们定义如下LCD操作结构体(在lcd.h里面定义):
//LCD操作结构体typedef sturct{ vu16 LCD_REG; vu16 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重要参数集typedefstruct{ u16 width;//LCD宽度 u16 height;//LCD高度 u16 id;//LCD ID u8 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横竖屏状态等,这个结构体虽然占用了10个字节的内容,但是却可以让我们的
驱动函数支持不同尺寸的LCD,同时可以实现LCD横竖屏切换等重要功能,所以还
是利大于弊的。
lcd.c里面的一些重要函数:
先看7个简单,但很重要的函数:
//写寄存器函数//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){ vu16 ram;//防止被优化 ram = LCD->LCD_RAM;return ram;}//写寄存器//LCD_Reg:寄存器地址//LCD_RegValue:要写入的数据void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue){ LCD->LCD_REG = LCD_Reg;//要写入的寄存器序号 LCD->LCD_RAM = LCD_RegValue;//要写入的数据}//读寄存器//LCD_Reg:寄存器地址//返回值:读到的数据u16 LCD_ReadReg(u16 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); } elseif(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); } elseif(lcddev.id ==0x1963) { if(lcddev.dir ==0)//X坐标需要变换 { Xpos= lcddev.width -1-Xpos;//横屏时处理 LCD_WR_REG(lcddev.setxcmd); LCD_WR_DATA(0); LCD_WR_DATA(0); LCD_WR_DATA(Xpos>>8); LCD_WR_DATA(Xpos&0xff); } else { LCD_WR_REG(lcddev.setxcmd); LCD_WR_DATA(Xpos>>8); LCD_WR_DATA(Xpos&0xff); LCD_WR_DATA((lcddev.width -1)>>8); LCD_WR_DATA((lcddev.width -1)&0xff); } LCD_WR_REG(lcddev.setycmd); LCD_WR_DATA(Ypos>>8); LCD_WR_DATA(Ypos&0XFF); LCD_WR_DATA((lcddev.height-1)>>8); LCD_WR_DATA((lcddev.height-1)&0XFF); } elseif(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),有了该函数,
我们可以在液晶上任意作图了。
这里面的lcddev.setxcmd、lcddev.setycmd、lcddev.width、lcddev.height等指令/
参数都是在LCD_Display_Dir函数里面初始化的,该函数根据lcddev.id的不同,执行不同设置。
由于9341/5310/6804/1963/5510等的设置通其他屏有些不同,故区别对待。
第九个函数:画点函数:
//画点//x,y:坐标//POINT_COLOR:此点的颜色void LCD_DrawPoint(u16 x, u16 y){ LCD_SetCursor(x, y);//设置光标位置 LCD_WriteRAM_Prepare();//开始写入GRAM LCD->LCD_RAM = POINT_COLOR;}
该函数实现比较简单,先设置坐标,再向坐标写颜色。其他几乎所有上层函数,都通过调用
这个函数实现。
其中POINT_COLOR是我们定义的一个全局变量,用于存放画笔颜色。还有另一个全局变量:
BACK_COLOR,代表背景色。
第十个函数:读点函数:
用于读取LCD的GRAM。
注:为什么OLED模块没有读GRAM函数,而此处需要?
因为OLED模块是单色的,所需要全部GRAM也就1k字节,而TFTLCD模块是彩色的,点数也比
OLED模块多很多,以16位色计算,一款320x240的液晶,需要320x240x2个字节来存储颜色值,也
就是需要150k字节,这对任何一款单片机来说,都不是小数目。
而我们在图形叠加时,可以先读回原来的值,再写入新值,在完成叠加后,又恢复原来的值。这样
在做一些简单菜单时,很有用。
这里的读点函数返回值为读到的GRAM的值。使用之前要先设置读取的GRAM地址,通过
LCD_SetCursor函数实现。
LCD_ReadPoint代码如下:
//func:读取某个点的颜色值//入参: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 || lcddev.id == 0x1963) LCD_WR_REG(0x2e); //9341、6804、3510、1963 发送读GRAM指令 else if(lcddev.id == 0x5510) LCD_WR_REG(0x2e00); //5510 发送读GRAM指令 else LCD_WR_REG(0x22); //其他IC,发送读GRAM指令 if(lcddev.id == 0x9320) opt_delay(2); //for 9320,延时2us r = LCD_RD_DATA(); //dummy Read 假读 if(lcddev.id == 0x1963) return r; //1963直接读就可以 opt_delay(2); r = LCD_RD_DATA(); //实际坐标颜色 if(lcddev.id == 0x9341 || lcddev.id == 0x5310 || lcddev.id == 0x5510) //这些LCD要分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、NT3510需要公式转换一下 else return LCD_BGR2RGB(r); //其他IC}
第十一个函数:LCD_ShowChar:
该函数同前面OLED模块的字符显示函数差不多,但此处字符显示函数多了一个功能,
即以叠加/非叠加方式显示。 叠加方式显示多用于在显示的图片上再显示字符。非叠加
方式一般用于普通的显示。
//在指定地址显示一个字符//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 - ''; //ASCII字库从空格开始取模,所以-' '即得到对应字符的字库(点阵) 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_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驱动器的初始化代码。
//初始化LCD//注:改初始化函数可以初始化各种ILI93XX液晶,但是其他函数是基于ILI9320的!!!//在其他型号的驱动芯片上没有测试!void LCD_Init(void){ GPIO_InitTypeDef GPIO_InitStruct; FSMC_NORSRAMInitTypeDef FSMC_NSInitStructure; FSMC_NORSRAMTimingInitTypeDef readWriteTiming; FSMC_NORSRAMTimingInitTypeDef writeTiming; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE); //使能 FSMC 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOD| RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOG| RCC_APB2Periph_AFIO, ENABLE); // ①使能GPIO 以及AFIO 复用功能时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PB0 推挽输出 背光 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //②初始化 PB0 //PORTD 复用推挽输出 GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5| GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_14|GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOD, &GPIO_InitStructure); //②初始化 PORTD //PORTE 复用推挽输出 GPIO_InitStructure.GPIO_Pin =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; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStructure); //②初始化 PORTE //PORTG12 复用推挽输出 A0 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_12; //PORTD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_12; //PORTD 复用推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOG, &GPIO_InitStructure); //②初始化 PORTG readWriteTiming.FSMC_AddressSetupTime = 0x01; //地址建立时间 2 个 HCLK 1 readWriteTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间模式 A 未用到 readWriteTiming.FSMC_DataSetupTime = 0x0f; // 数据保存时间为 16 个 HCLK readWriteTiming.FSMC_BusTurnAroundDuration = 0x00; readWriteTiming.FSMC_CLKDivision = 0x00; readWriteTiming.FSMC_DataLatency = 0x00; readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式 A writeTiming.FSMC_AddressSetupTime = 0x00; //地址建立时间为 1 个 HCLK writeTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间( A writeTiming.FSMC_DataSetupTime = 0x03; //数据保存时间为 4 个 HCLK writeTiming.FSMC_BusTurnAroundDuration = 0x00; writeTiming.FSMC_CLKDivision = 0x00; writeTiming.FSMC_DataLatency = 0x00; writeTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式 A FSMC_NSInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4; //这里我们使用NE4,也就对应BTCR[6],[7]。 FSMC_NSInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; //不复用数据地址 FSMC_NSInitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM; // SRAM FSMC_NSInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; //存储器数据宽度为 16bit FSMC_NSInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; FSMC_NSInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; FSMC_NSInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable; FSMC_NSInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; FSMC_NSInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; FSMC_NSInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; //存储器写使能 FSMC_NSInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; FSMC_NSInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable; // 读写使用不同的时序 FSMC_NSInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; FSMC_NSInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming; FSMC_NSInitStructure.FSMC_WriteTimingStruct = &writeTiming; //写时序 FSMC_NORSRAMInit(&FSMC_NSInitStructure); //③初始化FSMC 配置 FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE); //④使能BANK1 delay_ms(50); // delay 50 ms lcddev.id = LCD_ReadReg(0x0000); //读ID(9320\9325\9328\4531\4535等IC) if(lcddev.id < 0xff || lcddev.id == 0xffff || lcddev.id == 0x9300) //ID不正确,新增0x9300判断,因为9341在未被复位时,会被读成9300 { //尝试9341 ID的读取 LCD_WR_REG(0xd3); lcddev.id = LCD_RD_DATA(); //dummy read lcddev.id = LCD_RD_DATA(); //读到0x00 lcddev.id = LCD_RD_DATA(); //读到93 lcddev.id <<= 8; lcddev.id |= LCD_RD_DATA(); //读取41 if(lcddev.id != 0x9341) //非9341,尝试是不是6804 { LCD_WR_REG(0xBF); lcddev.id = LCD_RD_DATA(); //dummy read lcddev.id = LCD_RD_DATA(); //读到0x01 lcddev.id = LCD_RD_DATA(); //读到0xD0 lcddev.id = LCD_RD_DATA(); //读到0x68 lcddev.id <<= 8; lcddev.id |= LCD_RD_DATA(); //读取0x04 if(lcddev.id != 0x6804) //也不是6804,尝试NT35310 { LCD_WR_REG(0xd4); lcddev.id = LCD_RD_DATA(); //dummy read lcddev.id = LCD_RD_DATA(); //读到0x01 lcddev.id = LCD_RD_DATA(); //读到0x53 lcddev.id <<= 8; lcddev.id |= LCD_RD_DATA(); //读取0x10 if(lcddev.id != 0x5310) //也不是NT35310,尝试NT35110 { LCD_WR_REG(0xda00); lcddev.id = LCD_RD_DATA(); //读回0x00 LCD_WR_REG(0xdb00); lcddev.id = LCD_RD_DATA(); //读回0x80 lcddev.id <<= 8; LCD_WR_REG(0xdc00); lcddev.id |= LCD_RD_DATA(); //读回0x00 if(lcddev.id == 0x8000) lcddev.id == 0x5510; //NT35510读回的ID是8000H,为方便区分,我们强制设置为5510 if(lcddev.id != 0x5510) //也不是5510,尝试SSD1963 { LCD_WR_REG(0xa1); lcddev.id = LCD_RD_DATA(); lcddev.id = LCD_RD_DATA(); //读回0x57 lcddev.id <<= 8; lcddev.id |= LCD_RD_DATA(); //读回0x61 if(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); }
main.c:
该部分代码将显示一些固定的字符,字体大小包括24*12、16*8和12*6等三种,同时显示
LCD驱动IC的型号,然后不停地切换背景颜色,每秒切换一次。而LED0也不停地闪烁,指示
程序已经运行了。
其中用到了一个sprintf函数,该函数用法同printf,只是sprintf把打印内容输出到指定的内存
区间上,sprintf的详细用法,请百度。
int main(void){ u8 x = 0; u8 lcd_id[12]; //存放LCD ID字符串 delay_init(); //延时函数初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2 uart_init(115200); //串口初始化波特率115200 LED_Init(); //LED初始化 LCD_Init(); POINT_COLOR = RED; sprintf((char *)lcd_id, "LCD ID:%04X", lcddev.id); //将LCD ID打印到lcd_id数组 while(1) { 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,"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,12,12,"2014/5/4"); x++; if(x == 12) x = 0; LED0 = !LED0 delay_ms(1000);}
18.4下载验证
将程序下载到战舰STM32后,可以看到DS0不停地闪烁,提示程序已经在运行了。同时可以
看到TFTLCD模块显示如下图所示:
可以看到屏幕背景不停地切换,同时DS0不停地闪烁,证明我们的代码被正确地
执行了。
另外,本例程除了不支持CPLD方案的7寸屏模块,其余所有的ALIENTEK TFTLCD
模块都可以支持,直接插上去即可使用。
阅读全文
0 0
- TFTLCD显示实验_STM32F1开发指南_第十八章
- 第十八章 TFTLCD显示实验
- 内存管理实验_STM32F1开发指南_第四十二章
- FATFS实验_STM32F1开发指南_第四十四章
- SD卡实验_STM32F1开发指南_第四十三章
- USB虚拟串口实验_STM32F1开发指南_第五十三章——USB学习笔记
- 串口实验_STM32F1开发指南_第九章——串口学习笔记
- TFTLCD汉字显示
- stm32 FSMC-TFTLCD显示
- AT91SAM9263 WINCE 6.0 R2驱动开发-官方BSP包TFTLCD显示调试
- 实验10.1_显示字符串
- 实验10.3_数值显示
- STM32驱动ILI9341控制器控制TFTLCD显示
- STM32F103学习笔记 (十) TFTLCD 显示
- STM32学习笔记一一TFTLCD 显示
- linux shell编程指南第十八章------控制流结构
- linux shell编程指南第十八章------控制流结构1
- 我的学习之路_第十八章_SQL语句
- 深度优先搜索+回朔+奇偶剪枝
- 玩转 Podfile
- 配置CentOS6 yum源
- 自省
- C++String学习
- TFTLCD显示实验_STM32F1开发指南_第十八章
- 值类型和引用类型的区别
- 数据结构实验之图论六:村村通公路
- JavaScript内置对象
- Sagheer and Crossroads
- iMindMap中如何添加主题边框?
- MediaInfo.dll获取音视频时间戳
- Java中static和final的区别
- Android JNI之基本点(android studio)