【STM32】LCD液晶显示

作者:一只大喵咪1201
专栏:《STM32学习》
格言:你只管努力,剩下的交给时间!
【STM32】LCD液晶显示_第1张图片

LCD显示

  • LCD简介
  • TFTLCD驱动原理
  • ILI9341 液晶控制器简介
    • 液晶屏的信号线及 8080 时序
  • 使用 STM32 的 FSMC 模拟 8080 接口时序
  • 硬件连接
  • 代码实现
  • 字符显示
    • 英文字符显示
    • 英文字符串的显示
  • 总结

LCD简介

  • 显示器属于计算机的 I/O 设备,即输入输出设备。它是一种将特定电子信息输出到屏幕上再反射到人眼的显示工具。常见的有 CRT 显示器、液晶显示器、 LED 点阵显示器及 OLED 显示器。本喵这里使用的是CLD显示器,也就是液晶显示器。
  • 液晶显示器,简称 LCD(Liquid Crystal Display),相对于上一代 CRT 显示器 (阴极射线管显示器),LCD 显示器具有功耗低、体积小、承载的信息量大及不伤眼的优点,因而它成为了现在的主流电子显示设备,其中包括电视、电脑显示器、手机屏幕及各种嵌入式设备的显示器。液晶电视与 CRT 电视的外观对比,很明显液晶电视更薄,“时尚”是液晶电视给人的第一印象,而 CRT 电视则感觉很“笨重”。【STM32】LCD液晶显示_第2张图片
  • 液晶是一种介于固体和液体之间的特殊物质,它是一种有机化合物,常态下呈液态,但是它的分子排列却和固体晶体一样非常规则,因此取名液晶。如果给液晶施加电场,会改变它的分子排列,从而改变光线的传播方向,配合偏振光片,它就具有控制光线透过率的作用,再配合彩色滤光片,改变加给液晶电压大小,就能改变某一颜色透光量的多少,下图中液晶屏的结构就是绿色显示结构。利用这种原理,做出可控红、绿、蓝光输出强度的显示结构,把三种显示结构组成一个显示单位,通过控制红绿蓝的强度,可以使该单位混合输出不同的色彩,这样的一个显示单位被称为像素。【STM32】LCD液晶显示_第3张图片
  • 注意液晶本身是不发光的,所以需要有一个背光灯提供光源,光线经过一系列处理过程才到输出,所以输出的光线强度是要比光源的强度低很多的,比较浪费能源 (当然,比 CRT 显示器还是节能多了)。而且这些处理过程会导致显示方向比较窄,也就是它的视角较小,从侧面看屏幕会看不清它的显示内容。另外,输出的色彩变换时,液晶分子转动也需要消耗一定的时间,导致屏
    幕的响应速度低。

显示器的基本参数

不管是哪一种显示器,都有一定的参数用于描述它们的特性,各个参数介绍如下:

参数 说明
像素 像素是组成图像的最基本单元要素,显示器的像素指它成像最小的点,即前面讲解液晶原理中提到的一个显示单元。
分辨率 一些嵌入式设备的显示器常常以“行像素值 x 列像素值”表示屏幕的分辨率。如分辨率 800x480 表示该显示器的每一行有 800 个像素点,每一列有 480 个像素点,也可理解为有 800 列, 480 行。
色彩深度 色彩深度指显示器的每个像素点能表示多少种颜色,一般用“位” (bit) 来表示。如单色屏的每个像素点能表示亮或灭两种状态 (即实际上能显示 2 种颜色),用 1 个数据位就可以表示像素点的所有状态,所以它的色彩深度为 1bit,其它常见的显示屏色深为16bit、 24bit。
显示器尺寸 显示器的大小一般以英寸表示,如 5 英寸、 21 英寸、 24 英寸等,这个长度是指屏幕对角线的长度,通过显示器的对角线长度及长宽比可确定显示器的实际长宽尺寸。
点距 点距指两个相邻像素点之间的距离,它会影响画质的细腻度及观看距离,相同尺寸的屏幕,若分辨率越高,则点距越小,画质越细腻。如现在有些手机的屏幕分辨率比电脑显示器的还大,这是手机屏幕点距小的原因; LED 点阵显示屏的点距一般都比较大,所以适合远距离观看。

TFTLCD驱动原理

本喵使用的是一个4.3存的TFTLCD液晶屏。它的分辨率是480*800,通过16位并口驱动,也就是我们所说的8080并口,是一款电容触摸屏。

【STM32】LCD液晶显示_第4张图片
这是它的实物图,具体的构成本喵就不作讲解了,有兴趣的小伙伴可以查阅相关资料自行了解。

液晶显示原理:

以本喵使用的分辨率为480*800的液晶屏为例,下是它的示意图

【STM32】LCD液晶显示_第5张图片
红色框线是显示屏的边界线,里面的黑色框是一个一个的像素点,这样的像素点一共有800*480个。

它的显示过程就是将屏幕中的像素点用特定的颜色填充,比如我们在屏中显示一个字母F

【STM32】LCD液晶显示_第6张图片
将像素点从左到右从上到下按顺序填充,其中填充颜色为红色的所有点显示出来的就是一个红色的F,在液晶屏中,因为像素点间的点距很小,所以我们看到的F是是连着的,中间没有间隙。

在上面的简介中介绍过颜色深度这一个参数,这里我们使用的是RGB565格式的数据:

  • RGB 信号线的数量分别是5根,6根,5根,分别用于表示液晶屏一个像素点的红、绿、蓝颜色分量。使用红绿蓝颜色分量来表示颜色是一种通用的做法。

【STM32】LCD液晶显示_第7张图片
打开 Windows 系统自带的画板调色工具,可看到颜色的红绿蓝分量值,如图中R(红色)分量,G(绿色)分量,B(蓝色)分量,分别是50,25,30。

常见的颜色表示会在“RGB”后面附带各个颜色分量值的数据位数,如 RGB565 表示红绿蓝的数据线数分别为 5、6、 5 根,一共为 16 个数据位,可表示 216 种颜色。

显存:

  • 液晶屏中的每个像素点都是数据,在实际应用中需要把每个像素点的数据缓存起来,再传输给液晶屏,一般会使用 SRAM 或 SDRAM 性质的存储器,而这些专门用于存储显示数据的存储器,则被称为显存。
  • 显存一般至少要能存储液晶屏的一帧(一页,也就是480*800个像素点)显示数据,如分辨率为 800x480 的液晶屏,使用 RGB565 格式显示,一帧显示数据大小为: 2x800x480=768000 字节。
  • 一般来说,外置的液晶控制器会自带显存,而像 STM32F429 等集成液晶控制器的芯片可使用内部 SRAM 或外扩 SDRAM 用于显存空间。

ILI9341 液晶控制器简介

本喵使用的芯片是STM32F1系列的,它的内部SRAM是比较小的,所以需要使用扩展SRAM来缓存像素点的数据,本喵使用的LCD液晶有一个单独的控制器,这个控制器就是ILI9431芯片,它里面有一个GRAM的存储器。下面来看它的详细介绍。

【STM32】LCD液晶显示_第8张图片

  • 该芯片最主核心部分是位于中间的 GRAM(Graphics RAM),它就是显存。 GRAM 中每个存储单元都对应着液晶面板的一个像素点。它右侧的各种模块共同把 GRAM 存储单元的数据转化成液晶面板的控制信号,使像素点呈现特定的颜色,而像素点组合起来则成为一幅完整的图像。

  • 框图的左上角为 ILI9341 的主要控制信号线和配置引脚,根据其不同状态设置可以使芯片工作在不同的模式,如每个像素点的位数是 6、 16 还是 18 位;可配置使用 SPI 接口、 8080 接口还是 RGB接口与 MCU 进行通讯。

  • MCU 通过 SPI、 8080 接口或 RGB 接口与 ILI9341 进行通讯,从而访问它的控制寄存器 (CR)、地址计数器 (AC)、及 GRAM。

  • 在 GRAM 的左侧还有一个 LED 控制器 (LED Controller)。 LCD 为非发光性的显示装置,它需要借助背光源才能达到显示功能, LED 控制器就是用来控制液晶屏中的 LED 背光源。

所以说,我们只需要控制这个LIL9341控制芯片就可以了,这个芯片会自动的区控制LCD液晶屏幕,显示相应的数据。

液晶屏的信号线及 8080 时序

ILI9341 控制器根据自身的 IM[3:0] 信号线电平决定它与 MCU 的通讯方式,它本身支持 SPI 及8080 通讯方式,本示例中液晶屏的 ILI9341 控制器在出厂前就已经按固定配置好 (内部已连接硬件电路),它被配置为通过 8080 接口通讯,使用 16 根数据线的 RGB565 格式。

【STM32】LCD液晶显示_第9张图片

这是LCD液晶的硬件图和引脚。

【STM32】LCD液晶显示_第10张图片
这是LCD液晶屏PCB板引出的排针。

【STM32】LCD液晶显示_第11张图片

  • 这些信号线即 8080 通讯接口,带 X 的表示低电平有效, STM32 通过该接口与 ILI9341 芯片进行通讯,实现对液晶屏的控制。通讯的内容主要包括命令和显存数据,显存数据即各个像素点的RGB565 内容;
  • 命令是指对 ILI9341 的控制指令, MCU 可通过 8080 接口发送命令编码控制 ILI9341的工作方式,例如复位指令、设置光标指令、睡眠模式指令等等,具体的指令在《ILI9341数据手册》中有详细说明。

我们来看下8080接口的时序图:

【STM32】LCD液晶显示_第12张图片

  • 由图可知,写命令时序由片选信号 CSX 拉低开始
  • 数据/命令选择信号线 D/CX 也置低电平表示写入的是命令地址 (可理解为命令编码,如软件复位命令: 0x01)
  • 写信号 WRX 为低,读信号 RDX 为高表示数据传输方向为写入,同时,在数据线 D[17:0](或 D[15:0]) 输出命令地址,在第二个传输阶段传送的是命令的参数,所以 D/CX 要置高电平,表示写入的是命令数据,命令数据是某些指令带有的参数,如复位指令编码为 0x01,它后面可以带一个参数,该参数表示多少秒后复位 (实际的复位命令不含参数,此处只是为了讲解指令编码与参数的区别)。
  • 当需要把像素数据写入 GRAM 时,过程很类似SRAM扩展,把片选信号 CSX 拉低后,再把数据/命令选择信号线 D/CX 置为高电平,这时由 D[17:0] 传输的数据则会被 ILI9341 保存至它的 GRAM 中。

上面举例中的是8080接口的写时序,读时序和写几乎是一样的,只是方向相反。

使用 STM32 的 FSMC 模拟 8080 接口时序

ILI9341 的 8080 通讯接口时序可以由 STM32 使用普通 I/O 接口进行模拟,但这样效率太低,STM32 提供了一种特别的控制方法——使用 FSMC 接口实现 8080 时序。

FSMC的原理和使用本喵在文章【STM32】FSMC——扩展外部SRAM中详细的介绍,这里本喵就不再介绍,只介绍它是如何使用的,因为ILI9341中GRAM也是一种扩展的SRAM。

控制 LCD 时,是使用 FSMC 的 NORPSRAM 模式的,且与前面使用 FSMC 控制 SRAM 的稍有不同,控制 SRAM 时使用的是模式 A,而控制 LCD 时使用的是与 NOR FLASH 一样的模式 B,所以我们重点分析框图中 NOR FLASH 控制信号线部分。

【STM32】LCD液晶显示_第13张图片
这是FSMC模式B写时序与8080信号写时序的对比。

对比 FSMC NOR/PSRAM 中的模式 B 时序与 ILI9341液晶控制器芯片使用的 8080 时序可发现,这两个时序是十分相似的 (除了 FSMC 的地址线 A 和8080 的 D/CX 线,可以说是完全一样)。

【STM32】LCD液晶显示_第14张图片

  • 对于 FSMC 和 8080 接口,前四种信号线都是完全一样的,仅仅是 FSMC 的地址信号线 A[25:0] 与8080 的数据/命令选择线 D/CX 有区别。
  • 而对于 D/CX 线,它为高电平的时候表示数值,为低电平的时候表示命令,如果能使用 FSMC 的 A 地址线根据不同的情况产生对应的电平,那么就完全可以使用 FSMC 来产生 8080 接口需要的时序了。
  • 为了模拟出 8080 时序,我们可以把 FSMC 的 A0 地址线 (也可以使用其它 A1/A2 等地址线) 与ILI9341 芯片 8080 接口的 D/CX 信号线连接,那么当 A0 为高电平时 (即 D/CX 为高电平),数据线 D[15:0] 的信号会被 ILI9341 理解为数值,若 A0 为低电平时 (即 D/CX 为低电平),传输的信号则会被理解为命令。
  • 由于 FSMC 会自动产生地址信号,当使用 FSMC 向 0x6xxx xxx1、 0x6xxx xxx3、 0x6xxx xxx5…这些奇数地址写入数据时,地址最低位的值均为 1,所以它会控制地址线 A0(D/CX) 输出高电平,那么这时通过数据线传输的信号会被理解为数值;
  • 若向 0x6xxx xxx0 、 0x6xxx xxx2、 0x6xxx xxx4…这些偶数地址写入数据时,地址最低位的值均为 0,所以它会控制地址线 A0(D/CX) 输出低电平,
    因此这时通过数据线传输的信号会被理解为命令,见下表使用 FSMC 输出地址示例。

【STM32】LCD液晶显示_第15张图片

  • 有了这个基础,只要配置好 FSMC 外设,然后在代码中利用指针变量,向不同的地址单元写入数据,就能够由 FSMC 模拟出的 8080 接口向 ILI9341 写入控制命令或 GRAM 的数据了。

在配置结构体时,大部分与扩展SRAM的配置是一样的,只是有一些不同。

  1. FSMC_AccessMode :

本成员设置存储器访问模式,不同的模式下 FSMC 访问存储器地址时引脚输出的时序不一样,可选 FSMC_AccessMode_A/B/C/D 模式。控制异步 NORFLASH 时使用 B模式。

  1. FSMC_MemoryType :

本成员用于设置要控制的存储器类型,它支持控制的存储器类型为 SRAM、 PSRAM以及 NOR FLASH(FSMC_MemoryType_SRAM/PSRAM/NOR)。我们这里选用的是NOR FLASH。

硬件连接

在了解了LCD液晶屏与MCU的工作原理后就是来运用了。先看硬件连接图

【STM32】LCD液晶显示_第16张图片
这是TFTLCD的硬件图。

本喵在文章【STM32】FSMC——扩展外部SRAM详细的列出了FSMC的控制引脚,地址引脚以及数据引脚对应的IO口,这里本喵就不再演示了。

【STM32】LCD液晶显示_第17张图片
图中圈出来的部分就是连接 TFTLCD 模块的接口,液晶模块直接插上去即可。

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

LCD引脚 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 模块插上去就好了。

代码实现

1. 初始化通讯使用的目标引脚及端口时钟

void GPIO_Config(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	//将用到的IO口的时钟全部使能
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG,ENABLE);
	
  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);

  //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;    //PORTD复用推挽输出
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOD, &GPIO_InitStructure); 
     
	//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; //PORTE复用推挽输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOE, &GPIO_InitStructure);

	//PORTG12复用推挽输出 P0RTG0-->RS
	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); 
}

这里是将所有用到的IO口配置好。

2. 配置 FSMC 为异步 NOR FLASH 模式以仿真 8080 时序

【STM32】LCD液晶显示_第18张图片

  • 红色框是与扩展SRAM的不同点。
  • 蓝色框是设置的ADDSET和DATAST的值。

ADDSET和DATAST的值这里采用的是经验值,分别是0x01和0x04,当然也可以根据时序图来计算

【STM32】LCD液晶显示_第19张图片
但是计算很繁琐,我们可以使用就行了,所以采用经验值。

3. 建立机制使用 FSMC 向液晶屏发送命令及数据

首先是需要确定访问的地址,计算地址的过程如下:

  1. 本实验使用的是 FSMC_NE4 作为 8080_CS 片选信号,所以首先可以确认地址范围,当访问0X6C00 0000 ~ 0X6FFF FFFF 地址时, FSMC 均会对外产生片选有效的访问时序;
  2. 本工程中使用 FSMC_A10 地址线作为命令/数据选择线 RS 信号,所以在以上地址范围内,再选择出使得 FSMC_A10输出高电平的地址,即可控制表示数据,选择出使得 FSMC_A10输出低电平的地址,即可控制表示命令。
  • 要使 FSMC_A10 地址线为高电平,实质是输出地址信号的第 10 位为 1 即可,使用 0X6C000000~0X6FFF FFFF 内的任意地址,作如下运算:
    设置地址的第 10 位为 1: 0X6C00 0000 |= (1«10) = 0x6C00 0200
  • 要使 FSMC_A23 地址线为低电平,实质是输出地址信号的第 10 位为 0 即可,使用 0X6C000000~0X6FFF FFFF 内的任意地址,作如下运算:
    设置地址的第 10 位为 0: 0X6C00 0000 &= ~ (1«10) = 0x6C00 0000
  1. 但是,以上方法计算的地址还不完全正确,根据《STM32 参考手册》对 FSMC 访问 NOR FLASH的说明,见下图 , STM32 内部访问地址时使用的是内部 HADDR 总线,它是需要转换到外部存储器的内部 AHB 地址线,它是字节地址 (8 位),而存储器访问不都是按字节访问,因此接到存储器的地址线依存储器的数据宽度有所不同。
    【STM32】LCD液晶显示_第20张图片
  • 在本实验中使用的是 16 位的数据访问方式,所以 HADDR 与 FSMC_A 的地址线连接关系会左移一位,如 HADDR1 与 FSMC_A0 对应、 HADDR2 与 FSMC_A1 对应。因此,当 FSMC_A0 地址线为 1 时,实际上内部地址的第 1 位为 1, FSMC_A1 地址线为 1 时,实际上内部地址的第 2 位为1,这样做是为了达到地址线A0每增加一个数,实际上跳过的是俩个字节,方便LCD访问数据。
  • 同样地,当希望 FSMC_A10 地址输出高电平或低电平时,需要重新调整计算公式:
  • 要使 FSMC_A10 地址线为高电平,实质是访问内部 HADDR 地址的第 (10+1) 位为 1 即可,使用 0X6C00 0000~0X6FFF FFFF 内的任意地址,作如下运算:
    使 FSMC_A10 地址线为高电平: 0X6C00 0000 |= (1«(10+1)) = 0x6C00 0400
  • 要使 FSMC_A10 地址线为低电平,实质是访问内部 HADDR 地址的第 (10+1) 位为 0 即可,
    使用 0X6C00 0000~0X6FFF FFFF 内的任意地址,作如下运算:
    使 FSMC_A10 地址线为低电平: 0X6C00 0000 &= ~ (1«(10+1)) = 0x6C00 0000

根据最终的计算结果,总结如下:

  • 当 STM32 访问内部的 0x6000 0400 地址时, FSMC 自动输出时序,且使得与液晶屏的数据/命令选择线 RS(即 D/CX) 相连的 FSMC_A10 输出高电平,使得液晶屏会把传输过程理解为数据传输;
  • 类似地,当 STM32 访问内部的 0X6C00 0000 地址时, FSMC自动输出时序,且使得与液晶屏的数据/命令选择线 RS(即 D/CX) 相连的 FSMC_A23 输出低电平,使得液晶屏会把传输过程理解为命令传输。

我们将计算出的地址使用宏标记出来。

【STM32】LCD液晶显示_第21张图片

  • 这里必须使用volatile关键字修饰地址,否则就会在编译的时候被优化,导致实验达不到效果。
  • 这里将 0x6C00 0400中小于A10的地址都设为1,因为没有影响,但是将A0的地址设为0,所以结果就是 0x6C00 07FE
  • 将这个地址强转为定义的结构体类型的指针,此时结构体中成员变量vu16 LCD_REG的地址就是 0x6C00 07FE,而vu16 LCD_RAM的地址就是 0x6C00 8000,此时A10是0

这样一来,我们使用结构体就可以做到对LCD传送命令或者数据。

接下来需要将背光点亮

LCD_LED PBout(0) = 1;

【STM32】LCD液晶显示_第22张图片
可以使用ILI9341控制器中的D3h指令,用该指令获取LCD的ID,再通过串口打印出来。

3. 发送控制命令初始化液晶屏

这里就是将我们对GPIO的配置,FSMC的配置以及背光灯的点亮放在一个初始化函数中,这里本喵就不演示了。

4. 编写液晶屏的绘制像素点函数

为了操作方便,我们将向LCD写命令,写数据,读数据这几个操作封装为函数。

//写寄存器函数
//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屏幕的时候,我们需要给ILI9341不同的控制指令,但是每次查表又不方便,所以我们将常用到的指令放在一个结构体中。
【STM32】LCD液晶显示_第23张图片

这些命令值我们在另一给函数中挨个赋给这个结构体

【STM32】LCD液晶显示_第24张图片
通过查看手册中的相关指令给结构体变量赋值。

还要在屏幕上开一个窗口,用来显示我们要显示的内容。

【STM32】LCD液晶显示_第25张图片
此时前期的准备工作就做完了,接下来就可以画点了。

【STM32】LCD液晶显示_第26张图片

这是画点函数,我们可以多次调用改函数画各种各样的图形,文字。

看代码:

int main()
{
	u8 j;
	u32 i;
	delay_init();
	LCD_Init();
	LED_Init();		
	POINT_COLOR = RED;
	
	LCD_Clear(WHITE);
	
	for(j=75;j<100;j++)
	{
		for(i=100;i<400;i++)
		{
			LCD_DrawPoint(i,j);
		}
	}
	while(1)
	{
		LED0 = !LED0;
		delay_ms(500);
	}
}

下面是效果。


这是我们利用画点函数通过循环画出的一个矩形。

5. 利用描点函数制作各种不同的液晶显示应用

本喵使用的是正点原子家的开发板,原子家提供了很多的应用函数,我们在用到的时候直接调用就可以。

【STM32】LCD液晶显示_第27张图片

【STM32】LCD液晶显示_第28张图片

这是一个圆圈,一个矩形,还有俩条直线。

字符显示

英文字符显示

由于计算机只能识别 0 和 1,文字也只能以 0 和 1 的形式在计算机里存储,所以我们需要对文字进行编码才能让计算机处理,编码的过程就是规定特定的 01 数字符串来表示特定的文字,最简单的字符编码例子是 ASCII 码。

【STM32】LCD液晶显示_第29张图片
如上图中的ASCII码表中的部分字符,大写字母A的ASCII码值的16进制形式是0X41,空格的ASCII码值的16进制形式是0X20。

现在已经知道了,将0X41规定为字符A,那么这个A是怎么输出到屏幕的呢?

根据上面LCD的显示原理我们知道,LCD显示的过程就是将特定的像素点用特定的颜色填充的过程

【STM32】LCD液晶显示_第30张图片

  • 上图中,高度有60个像素点,宽度有30个像素点
  • 将这1800个像素点中的部分像素点用蓝色的填充,就能够得出字符A在屏幕中的样子。

这个填充过程是怎样的呢?

【STM32】LCD液晶显示_第31张图片
就像这样,从下到上,从左到右,挨个点的填充。

  • 每一个像素点对应着一个比特位,当该位为1时,填充该点,当该位为0不填充,按照这样的方式就可以显示出我们想要的字符。

继续看我们的字符A

【STM32】LCD液晶显示_第32张图片

这样表示每一个像素点是1还是0的比特位,按照16进制形式表示出来多个数据组成的数组,叫做字模。

按照上面动图中的填充顺序,我们可以根据字符A的样子,倒推出每个像素点是0还是1,然后通过上面讲解的画点函数将这些点用颜色填充,但是如果是每个字符都这样靠我们眼睛去看它对应的点是1还是0的话就太费劲了,所以我们需要借助一些工具。

【STM32】LCD液晶显示_第33张图片
使用这样一个软件,将上面红色框中字节的宽度和高度都设置成60,因为这个尺寸是汉字的尺寸,对应的因为字符就是宽位30,高为60,其中30和60都是比特位的个数。

再点击有下脚红色框中的生成字模,就能得到我们上面分析过程中的每一个像素点是1还是0,用16进制表示出来。

【STM32】LCD液晶显示_第34张图片
下面的红色数据是我们上面说到的字符A的字模。

图
将字模直接用在程序中,按照动图中的扫描顺序对应的字模中的数据,将比特位的值为1的像素点填充,为0的不填充,得到的就是我们要的字符A。

【STM32】LCD液晶显示_第35张图片
这样一个A我们就在屏幕上显示出来了。

英文字符串的显示

单个英文字符已经成功显示出来了,那么一个英文字符串是如何在LCD上显示出来的呢?

显示原理和单个的一样,只将单个字符的显示放在一个循环里,这样就可以显示多个字符,组合在一起就成了字符串。

既然要显示字符串,那么就不能仅有一个字母的字模了,需要将ASCII码表中的所有字符的字模都生成出来,这被叫做字库。

我们可以将每个字符都用上面制作单个字模的软件制作出来,然后将这些生成的16进制数据放在一个数组中,但是这样会非常繁琐,我们可以用一个软件,直接生成英文的字库。

【STM32】LCD液晶显示_第36张图片

这样生成了所有ASCII码表中字符组成的字库了。

【STM32】LCD液晶显示_第37张图片

是一个数据量非常大的数组。

将其放在一个单独的头文件中,并且添加到工程中。

来看一下代码如何调用这个字库。

【STM32】LCD液晶显示_第38张图片

  • 首先字符串中的每个字符必须在ASCII码表的空格和‘~’之间,符号条件才进行循环显示。
  • 当一行显示满以后,要将x左边重置到开始处,但是y坐标要换到下一行。
  • 在没有超出显示区域高度后,调用显示单个字符的函数
  • 这样循环到\0后便结束显示。

【STM32】LCD液晶显示_第39张图片

  • 通过要显示字符的大小,利用公式计算出单个字模所占的字节个数。
  • 调用对应size(字体大小)的字库,这里制作了三种大小的字库,1212,1616,和24*24
  • 蓝色框中通过循环判断字库中单个字模中每一个字节的每一位的情况,如果为1,则填充像素。

我们看效果:
【STM32】LCD液晶显示_第40张图片
此时我们想要的字符串就打印出来了。

注意:

ASCII码值和是怎么和字库联系上的呢?

  1. 首先,要打印的字符与字符空格作差,得到的值是该字符的偏移量
  2. 将该偏移量与字符编码对应的字模大小相乘,得到的就是该字符的字模偏移量
  3. 在字库中通过字模的偏移量找到该字符对应字模的起始数据,挨个扫描显示出来

总结

LCD液晶显示关键有俩个点,一个就是使用FSMC模拟8080时序来控制LIL9341芯片,进而控制LCD液晶屏,另一个就是显示原理,理解了显示屏上是如何显示内容的,并且按照原理写出相应的代码就可以实现,不过在我们大部分的使用情况中,都可以使用已经写好的显示函数来操作屏幕,除非需要特定的字体,这就需要我们按照原理来制作相应的字库。

你可能感兴趣的:(STM32学习,单片机,stm32,嵌入式硬件)