第三章 LCD驱动芯片手册精读:ILI9341/ST7789关键寄存器与初始化序列分析
做LCD驱动,最怕什么?
怕初始化序列写错了,屏幕死活不亮。怕寄存器配错了,颜色显示一团糟。我见过不少工程师,拿着别人的初始化代码直接复制粘贴,出了问题根本不知道从哪查起。
说白了,驱动芯片手册就是你的地图。看不懂地图,你怎么开车?
这一章,我带你把ILI9341和ST7789这两款最常用的LCD驱动芯片手册,从头到尾捋一遍。重点看关键寄存器、初始化序列,以及那些容易踩坑的地方。
3.1 芯片手册到底看什么?
拿到一份几百页的PDF,别从头读到尾。我个人的习惯是,先看这几个部分:
- 芯片概述:接口类型、支持分辨率、电压范围
- 命令表:所有可用的指令码,这是核心
- 寄存器描述:每个寄存器的位定义,尤其是控制位
- 初始化序列:官方推荐的启动流程
- 时序图:SPI通信的时序参数
你想想看,如果连芯片支持什么接口都不清楚,上来就写代码,那不是瞎忙活吗?
3.2 ILI9341 vs ST7789:核心差异对比
这两款芯片我都用过不少项目。ILI9341是经典款,240x320分辨率,市场占有率极高。ST7789是后起之秀,支持135x240、240x240等多种分辨率,在小尺寸圆形表盘上特别流行。
先看一张对比表,心里有个底:
| 对比项 | ILI9341 | ST7789 |
|---|---|---|
| 最大分辨率 | 240x320 | 240x320(常见240x240) |
| 接口类型 | SPI / 8/16位并口 | SPI / 并口 |
| GRAM大小 | 172800字节 | 172800字节 |
| 典型初始化命令数 | 约20-30条 | 约15-25条 |
| 写像素速度(SPI) | 约15-20MHz | 约20-30MHz |
| 常见坑点 | 复位时序、读ID命令 | 扫描方向、窗口设置 |
嗯,这里要注意:ST7789的SPI时钟可以跑到更高,但实际速度还受限于你的MCU和PCB走线。我在一个项目中,ST7789用18MHz SPI刷屏,画面稳定,但换到另一块板子上就花屏了——原来是走线太长,信号质量不行。
3.3 关键寄存器深度解析
不管用哪款芯片,有几个寄存器是必须搞懂的。我挑最重要的几个讲。
3.3.1 软件复位 (0x01)
这个命令很简单,就是让芯片复位到默认状态。但有个细节:复位后需要等待至少5ms,才能发其他命令。我曾经因为延时不够,导致后续配置不生效,查了半天才发现是复位没完成。
// 软件复位示例
void LCD_Reset(void) {
LCD_WriteCmd(0x01); // 软件复位
delay_ms(10); // 等待复位完成,至少5ms
}
3.3.2 睡眠模式控制 (0x10 / 0x11)
0x10是进入睡眠,0x11是退出睡眠。芯片上电后默认是睡眠状态,必须退出睡眠才能正常显示。这个顺序不能乱:先退出睡眠,再初始化其他寄存器。
3.3.3 像素格式设置 (0x3A)
这个寄存器决定了每个像素用多少位来表示。常见的有:
- 0x55:16位色(RGB565),每个像素2字节
- 0x66:18位色(RGB666),每个像素3字节
我一般用16位色,因为SPI传输效率高,而且人眼基本看不出区别。除非你的应用对色彩精度要求极高,否则没必要用18位。
3.3.4 内存访问控制 (0x36)
这个寄存器控制屏幕的扫描方向、RGB/BGR顺序等。它的每一位都有特定含义:
| 位 | 名称 | 说明 |
|---|---|---|
| 7 | MY | 行地址翻转(上下翻转) |
| 6 | MX | 列地址翻转(左右翻转) |
| 5 | MV | 行列交换(旋转90度) |
| 4 | ML | 垂直刷新方向 |
| 3 | BGR | 颜色顺序:0=RGB,1=BGR |
举个例子:如果你想让屏幕横屏显示,通常需要设置MV=1,同时调整MY和MX。我在做手表项目时,就因为这个寄存器没配好,显示的文字全是倒的,折腾了一下午。
3.3.5 列/页地址设置 (0x2A / 0x2B)
这两个命令用来设置显示窗口。0x2A设置列起始和结束地址,0x2B设置行起始和结束地址。只有在这个窗口内的像素才会被更新。
// 设置显示窗口示例
void LCD_SetWindow(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
LCD_WriteCmd(0x2A); // 列地址
LCD_WriteData(x1 >> 8);
LCD_WriteData(x1 & 0xFF);
LCD_WriteData(x2 >> 8);
LCD_WriteData(x2 & 0xFF);
LCD_WriteCmd(0x2B); // 页地址
LCD_WriteData(y1 >> 8);
LCD_WriteData(y1 & 0xFF);
LCD_WriteData(y2 >> 8);
LCD_WriteData(y2 & 0xFF);
LCD_WriteCmd(0x2C); // 开始写内存
}
这个函数是刷屏的核心。你想想看,如果窗口设置错了,画面就会偏移或者只显示一部分。
3.4 初始化序列分析
官方手册里通常会给出一个初始化序列。但说实话,那个序列不一定适合你的具体应用。我一般会做两件事:
- 精简序列:去掉不必要的命令,只保留必须的
- 调整参数:根据屏幕尺寸和方向,修改窗口和扫描方向
下面是一个典型的ILI9341初始化序列,我加了注释:
void ILI9341_Init(void) {
// 1. 硬件复位
LCD_RST_LOW();
delay_ms(10);
LCD_RST_HIGH();
delay_ms(120);
// 2. 退出睡眠模式
LCD_WriteCmd(0x11);
delay_ms(120);
// 3. 像素格式:16位色
LCD_WriteCmd(0x3A);
LCD_WriteData(0x55);
// 4. 内存访问控制:RGB顺序,正常扫描
LCD_WriteCmd(0x36);
LCD_WriteData(0x48); // 0100 1000
// 5. 帧率控制
LCD_WriteCmd(0xB1);
LCD_WriteData(0x00);
LCD_WriteData(0x18);
// 6. 显示开
LCD_WriteCmd(0x29);
delay_ms(10);
}
你看,这个序列只有6步,但已经能正常显示了。为什么?因为其他寄存器用默认值就够了。我见过有人把手册里30多步的初始化序列全抄下来,结果屏幕反而显示异常——因为有些命令的默认值就是最优的,你改了反而坏事。
3.5 知识结构图:初始化流程
下面这张图,把整个初始化流程串起来了。你可以对照着看,每一步都做了什么。
3.6 避坑指南:我踩过的那些坑
做LCD驱动,有些坑是绕不过去的。我分享几个亲身经历,希望能帮你少走弯路。
我曾经在一个项目中,硬件复位只拉了1ms就结束了。结果屏幕偶尔能亮,偶尔不亮。用示波器一看,复位信号毛刺严重。后来改成10ms,问题解决。
教训:复位时间宁长勿短,10ms是安全值。
有一次我想通过读ID来验证SPI通信是否正常,结果读回来的全是0xFF。查了半天,发现是MISO引脚没接上拉电阻。加上10kΩ上拉后,ID读取正常。
教训:SPI的MISO线在空闲时应该是高阻态,需要外部上拉。
显示红色,结果出来的是蓝色。这个问题在ILI9341上特别常见,因为它的默认颜色顺序是RGB,但很多模块厂商改成了BGR。你需要在0x36寄存器里设置BGR位。
教训:拿到新模块,先显示纯色,确认颜色顺序对不对。
3.7 小结
这一章的内容,说白了就是三件事:
- 看懂手册:知道哪些寄存器是关键,哪些可以忽略
- 写对序列:初始化步骤不能少,延时不能短
- 避开坑点:复位、读ID、颜色顺序,这三个地方最容易出问题