1. LCD驱动基础:显示原理、像素与分辨率、色彩深度与RGB格式
各位同学,咱们今天正式开讲LCD驱动。说实话,我最早接触LCD驱动时,觉得这东西不就是往寄存器里写数据吗?后来被坑了几次才明白——不懂显示原理,写再多代码也是瞎调。这一章咱们把地基打牢。
1.1 LCD是怎么亮起来的?
LCD的全称是Liquid Crystal Display,液晶显示器。它不发光的,是靠背光透过液晶层来显示。液晶分子在电场作用下会扭转,控制光线通过的量——说白了就是个光阀门。
每个像素由三个子像素组成:红、绿、蓝。通过控制每个子像素的透光率,混合出你想要的颜色。我刚开始做项目时,一直以为LCD是主动发光的,后来发现背光坏了屏幕就全黑,才彻底搞明白。
1.2 像素与分辨率——你到底需要多少点?
像素是显示的最小单位。一个像素就是一个点,能显示一种颜色。分辨率就是横向和纵向各有多少个像素。比如1024×600,就是横向1024个点,纵向600个点。
我遇到过不少新手问:分辨率越高越好吗?理论上是的,但你要考虑MCU能不能扛得住。举个例子,驱动一个320×240的屏,刷新一帧需要76,800个像素的数据。如果换成1024×600,那就是614,400个像素——数据量翻了8倍。你的I2C总线速度跟得上吗?你的帧缓冲够大吗?
| 分辨率 | 总像素数 | 16位色每帧数据量 | 常见应用 |
|---|---|---|---|
| 128×64 | 8,192 | 16 KB | 小屏、字符显示 |
| 320×240 (QVGA) | 76,800 | 150 KB | 入门级彩屏 |
| 480×320 (HVGA) | 153,600 | 300 KB | 手持设备 |
| 800×480 (WVGA) | 384,000 | 750 KB | 工控、车载 |
| 1024×600 (WSVGA) | 614,400 | 1.2 MB | 中大型HMI |
1.3 色彩深度——一个像素到底能显示多少种颜色?
色彩深度,也叫色深,指的是每个像素用多少位来表示颜色。常见的有:
- 1位: 黑白,每个像素要么亮要么灭
- 8位: 256色,通常是3-3-2格式(R:3位, G:3位, B:2位)
- 16位: 65,536色,最常见的是RGB565(R:5位, G:6位, B:5位)
- 18位: 262,144色,RGB666
- 24位: 16,777,216色,RGB888,真彩色
为什么16位色最常见?因为16位正好是两个字节,MCU处理起来最方便。我早期做的一个项目,用了24位色,结果每传一个像素要3个字节,I2C总线直接跑满了,刷新率掉到10帧以下。后来改成RGB565,刷新率翻了一倍。
1.4 RGB格式——数据到底怎么排?
RGB格式就是颜色数据在内存里的排列方式。同样是16位色,不同的排列方式,驱动代码完全不同。
最常见的几种格式:
| 格式 | 位分配 | 数据示例 (16位) | 说明 |
|---|---|---|---|
| RGB565 | R[4:0] G[5:3] G[2:0] B[4:0] | 0xF800 = 纯红 | 高字节: RRRRRGGG, 低字节: GGGBBBBB |
| BGR565 | B[4:0] G[5:3] G[2:0] R[4:0] | 0x001F = 纯红 | 与RGB565字节顺序相反 |
| RGB888 | R[7:0] G[7:0] B[7:0] | 0xFF0000 = 纯红 | 每个颜色1字节,共3字节 |
| RGB666 | R[5:0] G[5:0] B[5:0] | 0xFC00 = 纯红 (18位) | 通常用3字节存储,高位补0 |
我曾经踩过一个坑:用的LCD控制器默认是BGR565,但我按RGB565传数据,结果红色和蓝色反了,显示出来人脸是紫色的。排查了半天才发现是格式没对上。所以拿到一块新屏,第一件事就是看数据手册里RGB格式的定义。
1.5 一个简单的RGB565颜色计算例子
假设你要显示一个纯红色像素,RGB值为(255, 0, 0)。在RGB565格式下:
- R: 255 → 5位,取高5位 → 11111 (0x1F)
- G: 0 → 6位 → 000000
- B: 0 → 5位 → 00000
- 组合: 高字节 = 11111 000 = 0xF8, 低字节 = 00 000000 = 0x00
- 最终16位数据: 0xF800
// RGB565 颜色转换函数
uint16_t rgb_to_565(uint8_t r, uint8_t g, uint8_t b) {
return ((r & 0xF8) << 8) | // R: 取高5位,左移8位
((g & 0xFC) << 3) | // G: 取高6位,左移3位
(b >> 3); // B: 取高5位
}
// 使用示例
uint16_t red_pixel = rgb_to_565(255, 0, 0); // 结果: 0xF800
uint16_t green_pixel = rgb_to_565(0, 255, 0); // 结果: 0x07E0
uint16_t blue_pixel = rgb_to_565(0, 0, 255); // 结果: 0x001F
这段代码我用了好多年,基本没出过问题。但要注意——如果你的LCD是BGR565格式,那R和B的位置要互换。别问我怎么知道的。