| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 嵌入式 -> 每节课都是一个项目 手把手用STM32打造联网气象站-8-面子工程-学会点亮LCD屏幕 -> 正文阅读 |
|
[嵌入式]每节课都是一个项目 手把手用STM32打造联网气象站-8-面子工程-学会点亮LCD屏幕 |
目录 7.2.1. 采用OpenWindow函数,设置绘制矩形的起始坐标,宽度,高度; 完成了STM32基础三板斧,用这个三板斧完成一些项目,我们接下来需要开始面子工程,点亮LCD屏幕。 我们把LCD屏幕的教程提前,主要是使得教程更加有趣味性。 1. 游戏的秘密我们每个人都会有一段时间,沉迷于某一个游戏之中,不能自拔,连续七八个小时玩游戏,熬夜到两三点,这个可能都是常态。 为何游戏有这么大的魔力?游戏的吸引力来自于哪里?我们是否可以学习,让编程也有同等吸引力呢? 游戏的魔力主要来自于这样几点: 1.及时反馈:游戏总是每隔一段时间,会有一个正反馈,比如刷到某个装备,或者得到某个奖励。玩游戏的人不需要耗费太多的精力,就能够得到一个正反馈,这种机制吸引游戏者不断玩下去。 2. 任务简化分解:每隔任务都会简化分解为多个小任务,完成一个小任务就会有明确反馈。相信玩过游戏的人,都会有这样一个体会,如果一关实在太难过了,大家就会放弃这个游戏了。 3. 难度适中:游戏一般都会设置适中的难度,过难或者过于简单,都会使得游戏变得乏味。过难很容易让人放弃,这个容易理解。过于简单也会使得游戏让人乏味。比如某些游戏,如果充值过多,或者用外挂破解之后,大大降低了游戏难度,反而使得这个游戏缺少了吸引力。 4. 结果明确:每个游戏都会有明确的结果,比如消灭一个大BOSS,或者得到某一件宝物。明确的结果使得游戏进度容易衡量,结果容易体现。明确的结果也是游戏的魅力之一。 分析了游戏的魅力,我们就可以推到出来,如何使得编程也变得非常有魅力,非常迷人。 1. 编程教程需要及时反馈:每一个教程能够及时看到结果,而且结果需要有一定吸引力; 2. 编程任务需要简化分解:编程的复杂任务需要分解为多个简单的小任务,方便学习过程中,不断检查进度,了解进度; 3. 编程难度适中:如果编程代码量较大,就需要将他分解为多个小任务,这样可以简化每一个任务的难度; 4. 编程结果明确:每个编程项目的结果是非常明确的,达到什么样的状态,实现什么样的功能,是非常清楚的,这样在学习过程中,减少很多犹疑和走弯路的过程。 2. LCD的魅力如果把板上的LED灯看作一个像素,那么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. FMSC接口5.1 FMSC接口介绍第四节提到了8080接口的访问方式,然而,在现实中,我们使用的MCU并不具备8080接口,它具备的是FMSC接口。FMSC接口全称为:Flexible Static Memory Controller 的缩写,意思为:灵活的静态存储控制器,它可以用于驱动包括 SRAM、 NOR FLASH 以及 NAND FLSAH 类型的存储器。不能驱动SDRAM这类动态存储器。(备注:静态存储和动态存储的区别是:静态存储不需要刷新,而动态存储需要不断刷新内容,因此动态存储往往需要一些专用的带有自动刷新的接口) FMSC接口时灵活通用接口,它支持多种不同外部存储的访问操作,其中就包括了8080接口。 FMSC分为多组信号,当把FMSC用作8080接口时,信号对应关系如下: FMSC接口读操作时序图 FMSC接口写操作时序图 8080接口操作时序图 把上面3个图放在一起,就可以看出:FMSC和8080基本上一一对应。
比如我们像FMSC的地址,写入一个数据,就会产生类似8080接口的时序。我们通过这个方式,将数据按照写入FMSC的方式写入8080接口。按照从FMSC读取数据的方式,从8080接口读取数据。 但是这里还有一个问题:ILI9341的8080接口中,有一个D/C信号,如何通过FMSC来产生呢? 5.2 如何产生D/C信号?FSMC对应NOR/SRAM访问地址为6000 0000 ~6FFFF FFFF这个范围。 下面捋一下地址线和访问地址之间关系:
?当我们访问地址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_BFSMC能够模拟不同类型的接口,而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设置关系如下
?因此我们就可以得出模式配置规律
?
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图片内容。 ???????? |
|
嵌入式 最新文章 |
基于高精度单片机开发红外测温仪方案 |
89C51单片机与DAC0832 |
基于51单片机宠物自动投料喂食器控制系统仿 |
《痞子衡嵌入式半月刊》 第 68 期 |
多思计组实验实验七 简单模型机实验 |
CSC7720 |
启明智显分享| ESP32学习笔记参考--PWM(脉冲 |
STM32初探 |
STM32 总结 |
【STM32】CubeMX例程四---定时器中断(附工 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 23:02:21- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |