1. LCD驱动基础:显示原理、像素与分辨率、色彩深度与RGB格式

各位同学,咱们今天正式开讲LCD驱动。说实话,我最早接触LCD驱动时,觉得这东西不就是往寄存器里写数据吗?后来被坑了几次才明白——不懂显示原理,写再多代码也是瞎调。这一章咱们把地基打牢。

1.1 LCD是怎么亮起来的?

LCD的全称是Liquid Crystal Display,液晶显示器。它不发光的,是靠背光透过液晶层来显示。液晶分子在电场作用下会扭转,控制光线通过的量——说白了就是个光阀门。

每个像素由三个子像素组成:红、绿、蓝。通过控制每个子像素的透光率,混合出你想要的颜色。我刚开始做项目时,一直以为LCD是主动发光的,后来发现背光坏了屏幕就全黑,才彻底搞明白。

核心要点: LCD = 背光 + 偏光片 + 液晶层 + 彩色滤光片。液晶本身不发光,它只是控制光线能不能通过。
LCD显示原理结构图 背光层 (Backlight) 下偏光片 液晶层 (Liquid Crystal) 上偏光片 彩色滤光片 (R/G/B子像素) 光线方向 R G B → 一个像素

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
我的经验: 选分辨率时,先算一下你的MCU内部SRAM能不能放下至少一帧的缓冲。放不下就得用外部RAM或者SPI Flash——那延迟就上来了。

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,刷新率翻了一倍。

注意: 色彩深度越高,数据量越大。在I2C这类低速总线上,高色深带来的视觉提升可能被刷新率下降抵消。要权衡。

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格式的定义。

避坑指南: 写驱动前,先确认LCD控制器的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的位置要互换。别问我怎么知道的。

小技巧: 调试阶段,先显示纯红、纯绿、纯蓝三个色块。如果颜色不对,马上就能判断是格式问题还是硬件问题。