一、常见的显示屏
1、CRT显示器
CRT 显示器学名为 "阴极射线显像管",电子枪通过加热阴极发射电子束,电子束经聚焦系统形成细束流,再通过电磁或静电偏转装置控制方向,按逐行扫描顺序在荧光屏上绘制图像。电子束撞击荧光材料,激发出可见光形成图像,彩色 CRT 通过调节红、绿、蓝三色荧光粉的发光强度实现多色显示。

2、LCD液晶显示器
LCD 液晶显示器是采用液晶控制透光度技术来实现色彩显示的设备,主要由液晶面板、背光源、偏振片、彩色滤光片和驱动电路等组成。输入的视频信号经驱动电路处理后,向液晶面板的特定单元格施加电压,使液晶单元格调整分子排列,改变光线的偏振状态,光线通过彩色滤光片后形成不同颜色,背光源发出的光透过偏振片和液晶单元,最终形成图像。


3、LED点阵显示器
LED 点阵显示器是由几万到几十万个半导体发光二极管像素点均匀排列组成,每个 LED 代表一个像素点,通常由发光二极管、驱动芯片、印刷电路板等构成。采用行扫描和列驱动的方式,控制电路接受外部信号并转化为控制信号,驱动器提供电流和电压,通过逐行激活各行,控制相应列上 LED 点的亮灭来显示图案或文字。

二、显示器的基本参数
像素:像素是组成图像的最基本单元要素,显示器的像素指它成像最小的点,LCD 显示屏的像素数量决定了图像的细腻程度,像素越多,图像就越清晰、逼真。
分辨率 :指的是显示屏水平和垂直方向上的像素点数,通常用横向像素数 × 纵向像素数来表示,如 320×240、480×272、800×480 等。分辨率越高,屏幕能显示的内容就越多,图像也就越清晰。例如,800×480 分辨率的显示屏比 320×240 分辨率的显示屏能显示更多的细节和文字。
色彩深度::表示每个像素能够显示的颜色数量,通常用位(bit)来衡量。常见的色彩深度有 1 位、4 位、8 位、16 位、24 位等。1 位色彩深度只能显示黑白两种颜色,8 位色彩深度可以显示 256 种颜色,24 位色彩深度则可以显示约 1677 万种颜色,能够呈现出非常丰富和逼真的色彩效果。
显示尺寸:指的是嵌入式 LCD 显示屏对角线的长度,通常以英寸(inch)为单位,如 3.5 英寸、4.3 英寸、7 英寸等。显示尺寸越大,屏幕能够展示的内容就越多,但同时也会增加设备的体积和功耗。
显存:用于存储显示屏要显示的图像数据。显存中的每个存储单元都对应着液晶面板的一个像素点。显存的大小决定了显示屏能够显示的图像复杂度和刷新率。在实际的应用中需要把每个像素点的数据缓存下来,再传输给液晶屏。显存一般至少要能存储液晶屏的一帧显示数据。
例如:分辨率为800*480的液晶屏,如果使用RGB888格式显示,它的一帧显示数据大小为:3(8位红+8位绿+8位蓝 = 24位=3个字节)*800*480=1152000字节;若使用RGB565格式显示,它的一帧显示数据大小为:2(5位红+6位绿+5位蓝 = 16位=2个字节)*800*480=768000字节。
三、TFT-LCD屏的控制框图
控制框图有两种,MCU内置LCD控制器和MCU外置LCD控制器
MCU内置LCD控制器:

MCU外置LCD控制器:

液晶显示屏的控制信号线共有7条:
|----------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| R[7:0] | 红色数据 | |
| G[7:0] | 绿色数据 | |
| B[7:0] | 蓝色数据 | |
| CLK | 像素同步时钟信号 | 液晶屏与外部使用同步通讯方式,以CLK信号作为同步时钟,在同步时钟的驱动下,每个时钟传输一个像素点数据 |
| HSYNC | 水平同步信号 | 水平同步信号HSYNC(Horizontal Sync)用于表示液晶屏一行像素数据的传输结束,每传输完成液晶屏的一行像素数据时,HSYNC会发生电平跳变,如分辨率为800x480的显示屏(800列,480行),传输一帧的图像HSYNC的电平会跳变480次 |
| VSYNC | 垂直同步信号 | 垂直同步信号VSYNC(Vertical Sync)用于表示液晶屏一帧像素数据的传输结束,每传输完成一帧像素数据时,VSYNC会发生电平跳变。其中"帧"是图像的单位,一幅图像称为一帧,在液晶屏中,一帧指一个完整屏液晶像素点。人们常常用"帧/秒"来表示液晶屏的刷新特性,即液晶屏每秒可以显示多少帧图像,如液晶屏以60帧/秒的速率运行时,VSYNC每秒钟电平会跳变60次。 |
| DE | 数据使能信号 | 数据使能信号DE(Data Enable)用于表示数据的有效性,当DE信号线为高电平时,RGB信号线表示的数据有效。 |
三、TFT-LCD显示屏驱动原理
LCD显示屏驱动的核心是:驱动LCD驱动芯片。
1、LCD驱动芯片一般使用8080时序进行控制,实现数据的写入和读取。
2、初始化序列(数组),屏幕原厂提供,初始化屏幕,不同的屏幕厂家初始化序列不完全相同。
3、实现画点函数、读点函数,等功能函数。
|------------|---------------|
| 8080底层操作函数 | 写数据、写命令、读数据 |
| 初始化LCD | 主要是发送初始化序列/数组 |
| 实现画点函数 | |
| 实现读点函数 | |
8080时序简介:
|-----------|--------|-----------|---------------------|
| CS | 片选信号线 | 低电平有效 | 选中器件,先选中,后操作 |
| WR | 写使能信号线 | 上升沿有效 | 用于数据/命令的写入 |
| RD | 读命令信号线 | 上升沿有效 | 用于数据/命令读取 |
| RS(DC) | 数据/命令 | 0=命令/1=数据 | 用于区分发送的是数据还是命令 |
| D[15:0] | 数据线 | 无 | 双向数据线,可以写入/读取驱动IC数据 |
8080写时序:

由写时序图可以看出,片选线拉低之后,在写使能(WR)的上升沿,MCU将数据(D[0:15])发送给LCD驱动器。读使能信号线(RD)全程保持高电平。
void lcd_wr_data(uint16_t data)
{
LCD_RS(1); /* 操作数据 */
LCD_CS(0); /* 选中 */
LCD_DATA_OUT(data); /* 数据 */
LCD_WR(0); /* WR低电平 */
LCD_WR(1); /* WR高电平 */
LCD_CS(1); /* 释放片选 */
}
8080读时序:

由读时序图可以看出,片选线拉低之后,在读使能(RD)的上升沿,MCU读取LCD驱动器发送的数据(D[0:15])。写使能信号线(WR)全程保持高电平。
uint16_t lcd_rd_data(void)
{
uint16_t ram; /* 定义变量 */
LCD_RS(1); /* 操作数据 */
LCD_CS(0); /* 选中 */
LCD_RD(0); /* RD低电平 */
ram = LCD_DATA_IN; /* 读取数据 */
LCD_RD(1); /* RD高电平 */
LCD_CS(1); /* 释放片选 */
return ram; /* 返回读数 */
}
四、LCD驱动芯片
LCD驱动芯片有几种常用的类型:ili9341、st7789、st7796、nt35510等
其中比较重要的驱动命令(以ili9341为例)为:
|------|----------|-------------------|
| 0xd3 | 读取驱动芯片id | 区分型号 |
| 0x36 | 访问控制 | 设置GRAM读写方向,控制显示方向 |
| 0x2a | 列地址 | 用于设置x坐标 |
| 0x2b | 页地址 | 用于设置Y坐标 |
| 0x2c | 写GRAM | 用于往LCD写GRAM数据 |
| 0x2e | 读GRAM | 用于读取LCD的GRAM |
五、TFT-LCD 显示屏的时序控制波形图分析

3.1、垂直时序 
VSYNC:垂直同步信号,每一帧数据传输完成结束时,跳变一次。
HSYNC:水平同步信号,每一行数据传输完成结束时,跳变一次。
DE:数据使能信号。
**VSW(垂直同步脉冲宽度):**VSYNC(垂直同步信号)的低电平(或者高电平,取决于屏规格),表示一帧图像的开始。让驱动电路准备好接收新的一帧数据。
**VBP(垂直后廊):**VSYNC(垂直同步信号)跳变一次结束后(即一帧数据结束后),HSYNC(水平同步信号)的无效周期(没有有效数据传输)。
**VFP(垂直前廊):**有效数据结束后,HSYNC(水平同步信号)的无效周期;它的作用是给驱动电路留出"收尾当前帧、缓存下一帧"的时间,确保画面切换平滑。
有效数据:VBP和VFP之间的部分,为有效数据。此阶段DE信号有效,并且是垂直方向真正传输图像数据的阶段,对应屏幕的实际显示行数(如 800×480 屏的 480 行)。
3.2、水平时序

**HSW(水平同步脉冲宽度):**HSYNC(水平同步信号)传输一行数据之后,HSYNC信号会出现一个低电平或者高电平信号(具体由屏而定)。这个信号表示一行像素的开始。提示驱动电路准好接收新的一行数据。
HBP(水平后廊):HSYNC(水平同步信号)结束后,时钟信号的无效周期(无有效RGB数据传输),它的作用给驱动电路留出 "处理上一行收尾、准备当前行像素" 的时间。
有效数据:水平方向真正传输像素数据的阶段,对应屏幕的实际显示列数 (如 800×480 屏的 800 列)。此阶段 DE 信号有效,RGB 数据(如 RGB [23:0] 表示 24 位真彩色)按 CLK 时钟逐点传输 (1 个 CLK 对应 1 个像素)。
**HFP(水平前廊):**有效数据结束后,CLK的无效周期。给驱动电路留出 "收尾当前行、缓存下一行" 的时间。
六、FSMC驱动LCD屏
FSMC简介之前在配置SRAM的时候已经说过,这里不再赘述。
用到FSMC的主要是为了配置FSMC模拟8080时序去控制LCD屏。
配置好FSMC,存储器当成普通外设使用。定义一个指向这些地址的指针,通过对指针操作就可以直接修改存储单元的内容,FSMC自动完成读写命令和数据访问操作,不需要程序去实现时序。
6.1、FSMC模拟8080时序
硬件连接:

从上图可以看出,
1、FSMC的NE引脚连接屏幕的片选线。
2、FSMC的A[X]即地址总线连接着屏幕的命令数据控制线。
3、FSMC的NWE引脚连接着屏幕的写使能线。
4、FSMC的NOE引脚连接着屏幕的读使能线。
5、FSMC的D[X]即双向数据总线连接着屏幕的数据位。因为屏幕刷新是并行数据,所以CPU会复用一般会复用16个引脚作为DATA的数据线。如原理图中的D0-D15是它的数据线。
6.2、FSMC存储块选用


FSMC共有4个块,每个块固定256M字节大小。LCD连接在BANK1。


每个块又分为4个区,每个区64M字节大小,FSMC_NE(x)与屏幕的片选线连接。可以从原理图中看出,片选线与NE4相连。LCD对接的是BANK1的第4个区,其对应的地址范围为0x60000000----0x63FFFFFF。
表25.1.3.3中的HADDR代表的是AHB总线,[27:26]这两个位是用来选择BANK1中的那个区,例如我们原理图中选择的区是NE4,那么这两位对应的就是11。
而[25:0]位需要注意一下,HADDR是字节地址,而存储器访问不都是按字节访问,接到存储器的地址线与其数据宽度相关。
当 Bank1 接的是 16 位宽度存储器的时候:HADDR[25:1]→FSMC_A[24:0]。
当 Bank1 接的是 8 位宽度存储器的时候:HADDR[25:0] →FSMC_A[25:0]。
当FSMC在连接存储器时,CPU的HADDR[25:1] --> FSMC的[24:0],相当于右移了一个位。所以对于16位存储设备,HADDR在发送时要先左移一个位,后续FSMC会自动右一个位,这个地址总线就对齐。
不论外部存储器接8位**/16****位宽设备,****FSMC_A[0]永远接在外部设备地址A[0]**。
6.3、FSMC读写时序

FSMC支持异步和同步两种时序控制。
对于异步时序。FSMC 主要设置 3 个时间参数:地址建立时间(ADDSET)、数据建立时(DATAST)和地址保持时间(ADDHLD)。
例如下图ST7796的参考手册中,给出了这三个时间,后续我们在配置的时候,参考这个时间,注意单位。

并且在选用异步模式时一般会选用模式A时序,因为FSMC的OE线连接着外部存储器的读使能位,所以如果OE在片选过程中不翻转,CPU将无法读取外部存储器的数据。LCD的存储器属于RAM,所以选择模式A,较为合适。


FSMC模拟8080时序,和我们上面说的8080时序基本一样,不同的是,CPU这里只需要往地址(如0X6C000000)里面写输入数据(如0X0A0B)。之后FSMC的各个信号线会依次产生相应的电平信号,写入数据。之后FSMC控制片选信号拉低,之后地址线[25:0]会输出地址(如0X6C000000)。当写使能信号拉低之后,写入的数据会从D[15:0]信号线并行输入到外部存储器的RAM中。
另外FSMC中,D/C信号的命令或者数据,上面我讲了高电平表示数据,低电平表示信号。当选择BANK1 NE4分区时,其基地址为0X6C000000,当选则A10作为区分线时,当 A10 为低电平时表示命令,当 A10 为高电平时表示数据。其地址偏移量为2^10 = 0x400。
如0X6C000000 第10位为0,表示低电平 。
0X6C000000+0x400 = 0X6C000400第10位为1,表示高电平。
当我们想发送命令时,只需要往0x6C000000这个地址写入数据。而想要发送命令只需要往0x6C000400这个地址写入数据。
方法1:
#define FMC_ADDR_CMD 0x6c000000
#define FMC_ADDR_DATA 0x6c000400
//写命令
void lcd_wr_cmd(volatile uint16_t cmd)
{
cmd = cmd;
*(uint16_t *)(FMC_ADDR_CMD) = cmd;
}
//写数据
void lcd_wr_data(volatile uint16_t data)
{
data = data;
*(uint16_t *)(FMC_ADDR_DATA) = data;
}
//读数据
uint16_t lcd_rd_data(void)
{
volatile uint16_t ram;
ram = *(uint16_t *)(FMC_ADDR_DATA);
return ram;
}
方法二:
#define LCD_FSMC_NEX 4 // 使用NE4
#define LCD_FSMC_AX 10 // 使用A10地址线
typedef struct
{
volatile uint16_t LCD_REG;
volatile uint16_t LCD_RAM;
} LCD_TypeDef;
#define LCD_BASE (uint32_t)((0X60000000 + (0X4000000 * (LCD_FSMC_NEX - 1))) | (((1 << LCD_FSMC_AX) * 2) - 2))
#define LCD ((LCD_TypeDef *) LCD_BASE) // 强制转换为 (LCD_TypeDef *)
//写命令操作
void lcd_wr_regno(volatile uint16_t regno)
{
regno = regno;
LCD->LCD_REG = regno;
}
//写数据操作
void lcd_wr_data(volatile uint16_t data)
{
data = data;
LCD->LCD_RAM = data;
}
//读数据操作
static uint16_t lcd_rd_data(void)
{
volatile uint16_t ram; /* 防止被优化 */
lcd_opt_delay(2);
ram = LCD->LCD_RAM;
return ram;
}
七、Cubemx配置
根据原理图配置 :

地址建立时间 、数据建立时间 、数据保持时间 根据驱动芯片手册进行计算配置 。
另外,背光引脚FSMC没有配置,需要单独进行配置。

八、功能实现及代码编写(示例是驱动器是ST7796)
图片显示:

字体取模:


软件和代码自取