锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

每节课都是一个项目 手把手用STM32打造联网气象站-8-面子工程-学会点亮LCD屏幕

时间:2022-09-10 19:30:00 100pin矩形连接器2a3三极管通用三极管pnp面板led连接器dc绿色矩形连接器st三极管8550

目录

1. 游戏的秘密

2. LCD的魅力

3. 8080接口

3.1写入命令

3.2写入数据

4. RGB接口

5. FMSC接口

5.1 FMSC接口介绍

5.2 如何产生D/C信号?

5.3 具体代码中的地址转换

6. 初始化的屏幕驱动

6.1 GPIO配置

6.2 FSMC配置

6.2.1 理解SetupTime

6.2.2 理解AccessMode_B

6.3 BackLed_Control

6.3.关于枚举类型

6.3.2关于LED背光灯

6.4 ILI9341复位

6.5 ILI9341寄存器初始化

6.6 GRAM设置扫描方法

6.6.设置扫描方向

6.6.2 设置X Y轴向坐标

7. 通过工程验证屏幕的初始化

7.1 在屏幕上画直线

7.2 在屏幕上画长方形

7.2.1. 采用OpenWindow设置绘制矩形的起始坐标、宽度和高度;

7.2.2 采用FillColor函数,填满矩形;

7.2.3 填满长方形


完成了STM32基础三板斧,用这三板斧完成一些项目,我们需要开始面子工程,点亮LCD屏幕。

我们把LCD提前的屏幕教程主要是为了使教程更有趣。

1. 游戏的秘密

我们每个人都会沉迷于某个游戏一段时间,连续七八个小时玩游戏,熬夜到两三点,这可能是正常的。

为什么游戏有这么大的魔力?游戏的吸引力来自哪里?我们能学会让编程同样有吸引力吗?

游戏的魔力主要来自以下几点:

1.及时反馈:游戏总是每隔一段时间就会有积极的反馈,比如刷设备或获得奖励。玩游戏的人可以得到积极的反馈,而不需要花费太多的精力。这种机制吸引游戏玩家继续玩。

2. 任务简化分解:每个任务将简化分解为多个小任务,完成一个小任务将有明确的反馈。我相信玩过游戏的人会有这样的经历,如果水平太悲伤,每个人都会放弃游戏。

3. 难度适中:游戏通常设置适中的难度太困难或太简单,会使游戏变得无聊。困难很容易放弃,这很容易理解。太简单也会让游戏变得无聊。例如,有些游戏,如果充电太多,或者在插件被破解后,大大降低了游戏的难度,但使游戏缺乏吸引力。

4. 结果明确:每个游戏都会有明确的结果,比如消灭一个大游戏BOSS,或者得到一个宝藏。明确的结果使游戏进度易于衡量,结果易于反映。明确的结果也是游戏的魅力之一。

通过分析游戏的魅力,我们可以推出如何使编程非常迷人。

1. 编程教程需要及时反馈:每个教程都能及时看到结果,结果需要有吸引力;

2. 编程任务需要简化和分解:编程的复杂任务需要分解为多个简单的小任务,以便在学习过程中不断检查和理解进度;

3. 编程难度适中:如果编程代码较大,则需要将其分解为多个小任务,以简化每个任务的难度;

4. 编程结果明确:每个编程项目的结果是非常明确的,达到什么样的状态,实现什么样的功能,是非常清楚的,这样在学习过程中,减少很多犹疑和走弯路的过程。

2. LCD的魅力

把板上的ED灯看作一个像素,那么LCD就是具备了320X240=76800个LED的表达能力。因此熟练掌握LCD,对于增强设计的表现力和表达力,都是非常有帮助的。

LCD的常用接口一般包含下面这样几种:

1. SPI接口:采用SPI方式和屏幕进行通信, 其中SPI接口时Motorola 公司推出的一 种同步串行接口技术,是一种高速的,全双工,同步的 通信总线。

2. INTEL 8080接口:使用这种接口的屏幕一般是屏幕自带了驱动芯片,比如ILI9488、ILI9341、SSD1963等 。驱动芯片里面自带了显存,MCU只需要把显示数据传给驱动芯片,驱动芯片会把数据保存到显存中,最后再把显存中的数据显示到屏幕上。

3. RGB接口:大屏采用较多的接口, 屏幕不带显存,需要MCU准备充足的显存空间(因为RGB565,480*272分辨率的屏幕就需要显存480*272*2 = 255K,一般的MCU都没有这么大的RAM,所以要加外置的SRAM或SDRAM)

我们这里重点介绍的是8080接口:

液晶屏的内部结构如上图所示,其中MCU通过8080接口,控制ILI9341或SSD1963或者同类芯片。此类芯片一般内置显存GRAM。MCU将需要显示的内容,通过8080接口,写入ILI9341。ILI9341将需要显示的内容写入GRAM,并且通过RGB接口,控制液晶显示电路。

所以ILI9341这类芯片,相当于电脑中的显卡。一方面连接CPU,另一方面连接液晶屏,实现显示控制功能。

对于STM32F4系列,也和电脑类似,会内置显卡功能。对于这类内置显卡的MCU,一般需要外挂SRAM,用来存储大量的图片。

3. 8080接口

接下来我们重点了解一下8080接口,并且通过理解这个接口,进一步深入理解MCU是如何通过接口,和外部芯片进行通信的。

8080接口分成两类:数据接口和控制接口。

数据接口包含DB0~DB15,共计16位数据;

控制接口包括:CS片选,RD读信号,WR写信号,RES复位信号,D/C数据命令信号共计5根信号线;

MCU通过8080接口写入ILI9341时,会先写入地址,再写入数据。

上图为ILI9341手册,其中明确说明了,当我们需要写入ILI9341数据时,我们先将D/C设置为0,写入对应的地址信息,告诉驱动芯片,接下来的数据需要写入哪一个位置。然后再把D/C设置为1,把数据写入对应地址中。

3.1写入命令

当MCU向ILI9341写入数据时,会控制这些管脚,达到下面状态:

1. CS片选拉低,通知ILI9341,“你被选中了,马上MCU要写入数据了”

2. D/C拉低,通知ILI9341,现在写入的是命令,而不是数据。

3. WR拉低,通知ILI9341,MCU马上要写入                    数据;

4. 然后数据接口DB0~DB15会执行写入动作,先写入地址信息,再写入数据信息;

具体操作如上图所示。

3.2写入数据

写入数据和写入命令唯一区别是:写入数据时,D/C为高电平;而写入命令时,D/C为低电平。

完成了上面两个操作后,就可以把数据从MCU写入ILI9341驱动中。

例如,我们常常会写入2A这个地址,这时,MCU会通知ILI9341,具体在哪一列进行显示。

 我们也常常写入2B这个地址,这里就是通知ILI9341,具体在哪一行进行显示。

4. RGB接口

MCU将数据写入ILI9341之后,LCD并不会直接更新显示。ILI9341还需要通过RGB接口,更新ILI9341对应的数据信息。ILI9341和LCD之间的接口,就是对应的RGB接口。

比如我们在淘宝上搜索RGB 4.3,就可以看到很多屏幕数据。

点击进去,就可以看到更加详细的信息,其中就会介绍对应的接口是RGB40PIN。

 

RGB40PIN对应的管脚如上图,包含了R,G,B和水平同步,垂直同步等信息。

液晶屏有一个显示指针, 它指向将要显示的像素。显示指针的扫描方向方向从左到右、从上到下,一个像素点一个 像素点地描绘图形。这些像素点的数据通过 RGB 数据线传输至液晶屏,它们在同步时钟 CLK 的驱动下一个一个地传输到液晶屏中,交给显示指针,传输完成一行时,水平同步信 号 HSYNC 电平跳变一次,而传输完一帧时 VSYNC 电平跳变一次。所以HSYNC的频率明显高于VSYNC。

液晶屏中的每个像素点都是数据,在实际应用中需要把每个像素点的数据缓存起来, 再传输给液晶屏,一般会使用 SRAM 或 SDRAM 性质的存储器,而这些专门用于存储显示 数据的存储器,则被称为显存。显存一般至少要能存储液晶屏的一帧显示数据,如分辨率 为 800x480 的 液 晶 屏 , 使 用 RGB888 格 式 显 示 , 它 的 一 帧 显 示 数 据 大 小 为 : 3x800x480=1152000 字 节 ; 若 使 用 RGB565 格 式 显 示 , 一 帧 显 示 数 据 大 小 为 : 2x800x480=768000 字节。其中RGB565把红色和蓝色的显示位数减少了,这样减少了整体显存的消耗。

在本期教程中,ILI9341将自动完成RGB接口通信,因此,我们只需要大致了解RGB接口的概念即可。

5. FSMC接口

5.1 FSMC接口介绍

第四节提到了8080接口的访问方式,然而,在现实中,我们使用的MCU并不具备8080接口,它具备的是FSMC接口。FSMC接口全称为:Flexible Static Memory Controller 的缩写,意思为:灵活的静态存储控制器,它可以用于驱动包括 SRAM、 NOR FLASH 以及 NAND FLSAH 类型的存储器。不能驱动SDRAM这类动态存储器。(备注:静态存储和动态存储的区别是:静态存储不需要刷新,而动态存储需要不断刷新内容,因此动态存储往往需要一些专用的带有自动刷新的接口)

FSMC接口时灵活通用接口,它支持多种不同外部存储的访问操作,其中就包括了8080接口。

FSMC分为多组信号,当把FSMC用作8080接口时,信号对应关系如下:

FSMC接口读操作时序图

FSMC接口写操作时序图

8080接口操作时序图

把上面3个图放在一起,就可以看出:FSMC和8080基本上一一对应。

信号名 FMSC 8080
片选 NE CS
写信号 WE WR
读信号 NOE RD
数据 DATA DATA
数据/命令 D/C ?

比如我们像FSMC的地址,写入一个数据,就会产生类似8080接口的时序。我们通过这个方式,将数据按照写入FMSC的方式写入8080接口。按照从FSMC读取数据的方式,从8080接口读取数据。

但是这里还有一个问题:ILI9341的8080接口中,有一个D/C信号,如何通过FSMC来产生呢?

5.2 如何产生D/C信号?

FSMC对应NOR/SRAM访问地址为6000 0000 ~6FFFF FFFF这个范围。

下面捋一下地址线和访问地址之间关系:

地址

地址二进制

高电平地址线

0x6000 0001

0001

A0

0x6000 0010

0001 0000

A4

0x6000 0100

0001 0000 0000

A8

0x6000 1000

0001 0000 0000 0000

A12

0x6001 0000

0001 0000 0000 0000 0000

A16

 当我们访问地址0x6000 0001时,对应的A0地址线为高位;

当我们访问地址0x6000 0010时,对应的A4地址线为高位;

以此类推;

当我们访问地址0X6001 0000时,对应的A16地址线为高位。

通过上面规律,就可以找到实现D/C命令访问的方式:

我们把D(数据)对应的地址和C(命令)对应的地址分开,这样写入D的时候,对应的地址线为高电平,写入C的时候,对应地址线为低电平,这样可以用地址线来模拟D/C线的控制。

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

5.3 具体代码中的地址转换

在FSMC驱动中,我们定义了两个地址:0x60000000 和0x60020000;这样,按照前面讨论内容,当FMSC往60020000中写入数据时,对应A17将变为高电平,因此,可以用A17来替代D/C信号,控制DATA写入。

不对啊,根据原理图来看,明明原理图上连接的时A16而不是A17啊?

 是的,其中还有一个核心秘密:

在本工程中使用的是 16位的数据访问方式,所以 HADDR与 FSMC_A的地址线连接关 系会左移一位,如 HADDR1 与 FSMC_A0 对应、HADDR2 与 FSMC_A1 对应。因此, 当 FSMC_A0地址线为 1时,实际上内部地址的第 1位为 1,FSMC_A1地址线为 1时, 实际上内部地址的第 2 位为 1。同样地,当希望 FSMC_A16 地址输出高电平或低电平 时,实质是访问内部 HADDR 地址的第(16+1)位为 1 即可。

因此,我们通过访问0X6002 0000这个地址,使得A16被设置为高电平,从而用A16来控制8080接口的D/C管脚,实现数据访问。

我们经过上面完整的分析,主要是让你掌握地址线和地址之间计算的逻辑:

当我们需要访问特定地址时,是通过特定地址线的高低电平来实现的。

6. 屏幕驱动初始化

下面重点讲解一下ILI9341的初始化过程。其他屏幕驱动芯片的初始化过程也非常类似。

6.1 GPIO配置

GPIO配置中,需要完成8080接口的16根数据线和6根控制线的初始化,把他们都初始化为输出。

这里需要注意,其中CS, DC, RD, WR设置为GPIO_Mode_AF_PP,而不是我们通常使用的GPIO_Mode_Out_PP,这是什么原因呢?

这时因为这几个IO是带有内部功能的IO,当我们采用这些带有内部功能的IO作为输出时,需要配置为AF_PP,也就是推挽复用输出。

6.2 FSMC配置

 FSMC初始化步骤比较复杂,这里进行简单说明,但不会详细讲解。对于大部分嵌入式软件工程师而言,是很难得去修改这里的代码的。对于驱动工程师而言,才有机会真正修改这里的代码。

6.2.1 理解SetupTime

其中AddressSetupTime和DataSetupTime是设置地址线和数据线的建立时间。所谓建立时间,就是为了确保信号能够被准确采集到,需要在采集点之前的x 纳秒,就需要把信号准备好,这样当读写操作开始时,能够把信号稳定在0或者1的数值,避免读写失误。

上图中,tdst就是写入数据的建立时间。WRX的上升沿(上图红色箭头处),MCU完成数据写入。但是在这个上升沿之前至少tdst这个时间,就需要把数据准备好,这样在上升沿写数据的时候,才能够稳定写入数据。

类似的参数时tdht,也就是数据保持时间,在WRX的上升沿之后,数据还需要保持至少tdht这个时间,这样才能够确保上升沿能够稳定的写入数据。 

这里设置AddressSetupTime和DataSetupTime就是根据ILI9341芯片的数据手册里面描述的setuptime,设置FMSC的参数,使得FSMC模拟出来的8080接口,能够稳定可靠的写入ILI9341。

我们理解这里的原理即可,不需要在这里扣的太细。

但是当我们实际需要在这里深挖的时候,我们应该具有这样的能力,能够深度挖掘这里的技术细节,并且完成相应的开发工作。

6.2.2 理解AccessMode_B

FSMC能够模拟不同类型的接口,而AccessMode_B则是模拟NOR FLASH 接口。 8080接口原本就是用在NOR FLASH上面,因此我们这里把他设置成为NOR FLASH 接口,是非常匹配的。

6.3 BackLed_Control

 

 点亮背光灯则非常简单,我们只需要通过GPIO,控制对应的GPIO为低电平即可。

6.3.1关于枚举类型

且慢,如何从代码中,看出这里的GPIO是需要设置为低电平呢?

我们之前跟着ST学写代码中,学习了typedef structure {}new_structure这种语句,这里我们学习类似的语句:

typedef enum{A=0,B=!A} new_state这样的语句。

首先我们复习一下枚举类型enum。我们在代码中常常用枚举类型,它的主要作用是:加强代码的可读性。比如Control(Enable), Control(Disable)这样的方式,总会比Control(0), Control(1)这样的方式更加友好,谁知道你的代码宇宙中,0表示Enable,还是1表示Enalbe (备注:在习惯的代码宇宙中,0表示disable, 1表示enable,但是也会有例外。)

 6.3.2关于LED背光灯

 我们的LED面板一般都内置背光灯。这个背光灯可以通过LCD_BK这个管脚控制点亮和熄灭,上面电路就包含了一个三极管,通过三极管,控制LED点亮或者熄灭。

这个三极管是PNP型三极管。关于如何区分NPN三极管和PNP型三极管,有这样一个小秘诀:箭头总是从P指向N; 所以如果箭头指向内部的,就是PNP管。如果箭头指向外部的,就是NPN管。根据箭头总是从P指向N这个规则,我们就可以清晰判断三极管的类型。

如果是NPN三极管,则是基极高电平导通,如果是PNP三极管,则是低电平导通。这里的记忆秘诀是:电压总是P大于N。因此对于NPN三极管,中间需要高电平,这样满足P大于N的条件,对于PNP三极管,中间需要低电平,这样才能满足P大于N的条件。

掌握上面两个记忆密码,我们就可以理解PNP和NPN三极管的工作原理。

在上面电路中,STM32的GPIO连接到LCD_BK。当GPIO设置为低电平时,LCD点亮,当GPIO设置为高电平时,LCD熄灭。

6.4 ILI9341复位

通过控制9341的RST管脚,进行相应的复位操作。

需要注意:复位后,需要Delay一段时间。由于这个Delay是在初始化的过程中,因此并不会对MCU代码运行产生影响,因为初始化只是上电短时间执行一次的操作而已。

6.5 ILI9341寄存器初始化

 9341寄存器初始化过程相对比较复杂,需要结合9341的数据手册进行查看。

比如代码中,写入0XCF,我们只需要在数据手册上,查找CF,就可以很快找到对应的内容。

从图片可以看出,代码中的设置和手册中的设置是一模一样的,这个就是芯片上电时候选择的默认设置。

 代码中ED寄存器,也就是上电寄存器,和默认值略有不同。我们这里结合手册,看看到底改了哪里。

手册中明确说了,第一参数CP1  设置为10,对应了soft start keep 1 frame选项。CP23参数对应了3rd frame enable选项。

具体对应方式为:

0x64 对应二进制为0110 0100,也就是cp1为10, cp23为10。

从工程角度来说,我们只需要能够一一对应即可,并不需要详细理解其中的每一个细节,因为:

吾生也有涯而知也无涯以有涯随无涯殆已

因此,这里我们只要知道,需要再上电前,对ILI9341的各个寄存器,完成一系列配置即可。。

6.6 GRAM扫描方式设置

6.6.1设置扫描方向

GramScan函数用来设置LCD的扫描方式。ucOption为参数,数值为0~7,也就是LCD共计有8种扫描方式。

 

对于模式0 2 4 6 , X为240, Y为320;

对于模式1 3 5 7, X方向为320, Y方向为240;

我们先把ucOption写入0x36的地址:ucOption的数值分别为000~111,左移5位后,对应数值为0000 0000~1110 0000,这样写入0x36地址后,相当于设置了0x36地址中下面的几个寄存器数值:

MY, MX, MV设置关系如下

参数  

方向

MY=0

自下向上

MY=1                

自上向下                        

MX=0

自右向左        

MX=1           

自左向右            

MV=0                 X Y保持

MV=1

X Y互换

 因此我们就可以得出模式配置规律

参数  

方向

0 0 0

Y自下向上,X自右向左,X,Y保持

0 0 1

  Y自下向上,X自右向左,X,Y互换         

0 1 0

Y自下向上,X自左向右,X,Y保持

0 1 1 Y自下向上,X自左向右,X,Y互换

 

1 0 0

Y自上向下,X自右向左,X,Y保持

1 0 1

  Y自上向下,X自右向左,X,Y互换         

1 1 0

Y自上向下,X自左向右,X,Y保持

1 1 1 Y自上向下,X自左向右,X,Y互换

6.6.2 设置X Y轴方向的坐标

 

其中CoordinateX的第一个和第二个参数,对应起始坐标的高八位和第八位,这里都是0。也就是起始坐标从0开始。第三个和第四个参数,对应结束坐标的高八位和低八位,这里为前面屏幕尺寸X方向数值。

CoordinateY的参数也是类似的,第一和第二个参数为起始坐标,第三和第四个参数为结束坐标。

 

完成显示范围设置之后,就从开头,不断写入像素点,写入的方向就是按照前面ucOption设置的方向,写入的个数,就是按照前面X Y的起始点和结束点设置的个数。写入的数值,就是包含了R,G,B三元素的数值。

到这里,我们终于完成了屏幕的初始化工作。

屏幕初始化确实比较复杂,但是我们大部分时候,仅仅需要掌握如何移植这个驱动即可。而对于详细代码部分,我们需要了解,如何从Datasheet中,找到对应的数据,而不需要牢记具体的数据信息。

7. 通过工程,验证屏幕初始化

终于到了激动人心的时刻,我们来试一下屏幕的初始化功能。

7.1 在屏幕上画直线

所谓在屏幕上画直线,就是根据两个坐标,在屏幕上绘制一条连接两个点之间的直线。说起来简单,但是实际上是需要一定的算法支持的。

 因为我们的屏幕是有像素点的,所谓直线只是许多条折线的组合,只是远看起来像是一条直线而已。

假设我们起始点坐标为(0,0),结束点坐标为(50,100)我们把这个参数带进去:

1. usX1=0, usY1=0, usX2=50, usY2=100;

2. Delta_X=50, Delta_Y=100;

3. Increase_X=1, Increase_Y=1; Distance=100;

 

for循环中, 

1. 在起始点0,0画点;

2. Err_x=50, Err_Y=100;

3. 判断Err_X和Err_Y是否大于Distance;

4. Err_X=100, Err_Y=200;

5. Err_Y>Distance, Err_Y=100, y_current=1; 在(0,1)处画一个点;

7. Err_X=150, Err_Y=200, 在(1,2)处画一个点,完成后,Err_X=50, Err_Y=100;

8.继续循环在(1,3)处画一个点;

以此类推,完成从起点到终点的划线行为。

事实上,划线是一个有一定复杂度的算法,比冒泡法排序更加复杂一些。

对于有一定复杂度的算法,我们一般采用代入法,将一些特殊数值放进去,即可循序渐进,判断出对应算法逻辑。

7.2 在屏幕上画长方形

在屏幕上画长方形更加简单一些。

7.2.1. 采用OpenWindow函数,设置绘制矩形的起始坐标,宽度,高度;

下面详细看下:

 OpenWindow设置写入位置,操作的还是最初的2A和2B寄存器。具体参见6.6.2中详细描述。

7.2.2 采用FillColor函数,将矩形填满;

 首先我们需要了解一下,9341是采用RGB565的方式,控制颜色的,红色5bit 绿色6bit 蓝色5bit。

常见的RGB888和RGB565的颜色定义,可以参考下面的连接。

https://blog.csdn.net/qq_20222919/article/details/116168044

如需查看更多颜色组合,直接查看上面链接即可。

我们以黄色为例,如果需要组成黄色,就需要红色和绿色拉满,蓝色不要,这样就是FF E0,这样就可以组成黄色;如果需要组成紫色,就需要红色和蓝色拉曼,绿色不要,这就是:F8 1F,这样就把中间绿色设置为0,其他都是1,构成了紫色。

当我们需要在一个区域设置颜色时,只需要把对应的范围,直接写入上面设置的颜色信息,即可完成颜色的填充。

7.2.3 填满长方形

 先用OpenWindow设置需填充的区域,然后再往这个区域写入需要填充的颜色,即可构成填充好的长方形。

纸上得来终觉浅,绝知此事要躬行!

马上下载测试代码,看看效果。

 https://download.csdn.net/download/book_drabit/86103895

代码执行效果详细见下面GIF图片内容。

        

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章