显示驱动原理:像素与颜色深度、帧缓冲机制、显示接口详解
各位同学,今天我们来聊聊嵌入式GUI最底层的东西——显示驱动。说白了,就是屏幕怎么亮起来、怎么把图像画上去的。我刚开始做嵌入式开发时,总觉得显示驱动是硬件工程师的事,直到自己调一块LCD屏调了三天没点亮……嗯,从那以后我老实了,老老实实把原理啃了一遍。
像素与颜色深度
先问个问题:屏幕上的一个点,怎么描述它?
答案很简单——用数字。每个像素点由红、绿、蓝三个子像素组成,这就是我们常说的RGB模型。每个子像素用多少位二进制数来表示,就是颜色深度。
常见颜色深度对照表
| 颜色深度 | 总颜色数 | 每像素字节数 | 典型应用 |
|---|---|---|---|
| 1 bpp | 2 | 0.125 | 单色OLED、电子纸 |
| 8 bpp | 256 | 1 | 低端彩屏、调色板模式 |
| 16 bpp (RGB565) | 65536 | 2 | 嵌入式LCD最常用 |
| 24 bpp (RGB888) | 1677万 | 3 | 高清屏、手机屏 |
| 32 bpp (ARGB8888) | 1677万+Alpha | 4 | 带透明效果的GUI |
我个人习惯在资源受限的MCU上用RGB565。为什么?因为每个像素只占2字节,320×240的屏幕,帧缓冲才150KB,大部分MCU扛得住。RGB888虽然颜色更细腻,但3字节对齐在32位总线上反而尴尬,你想想看。
避坑指南
我曾经在一个项目里用了24位色,结果DMA传输时因为字节对齐问题,画面一直有条纹。后来改成32位(浪费一个字节做填充),问题就解决了。嗯,有时候浪费也是一种优化。
帧缓冲(FrameBuffer)机制
帧缓冲是什么?说白了就是一块内存区域,里面存着屏幕上每个像素的颜色值。CPU往这块内存里写数据,显示控制器就自动把数据刷到屏幕上。
我画了一张图,帮你理解这个流程:
帧缓冲的核心优势是什么?CPU只管往内存里写数据,不用管屏幕的刷新时序。显示控制器(LTDC或DSI Host)会自动从帧缓冲里取数据,按行、按帧发送给屏幕。
注意:帧缓冲的大小 = 屏幕宽度 × 屏幕高度 × 每像素字节数。320×240的RGB565屏,帧缓冲 = 320 × 240 × 2 = 153,600字节 ≈ 150KB。如果你的MCU只有64KB RAM,那就得用分块刷新或者外挂SDRAM。
显示接口详解
帧缓冲里的数据怎么送到屏幕上?这就靠显示接口了。嵌入式领域最常见的三种接口:SPI、RGB、MIPI DSI。我一个个说。
SPI接口
SPI接口,说白了就是串行通信。用MOSI、MISO、SCK、CS四根线,外加DC(数据/命令选择)线。速度慢,但引脚少。
SPI LCD驱动示例(伪代码)
// 写命令
void LCD_WriteCmd(uint8_t cmd) {
DC_LOW(); // DC=0 表示命令
CS_LOW(); // 片选使能
SPI_Transmit(cmd); // 发送一个字节
CS_HIGH(); // 片选释放
}
// 写数据
void LCD_WriteData(uint8_t data) {
DC_HIGH(); // DC=1 表示数据
CS_LOW();
SPI_Transmit(data);
CS_HIGH();
}
// 设置像素区域(矩形裁剪)
void LCD_SetWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
LCD_WriteCmd(0x2A); // 列地址设置
LCD_WriteData(x >> 8); LCD_WriteData(x & 0xFF);
LCD_WriteData((x+w-1) >> 8); LCD_WriteData((x+w-1) & 0xFF);
LCD_WriteCmd(0x2B); // 行地址设置
LCD_WriteData(y >> 8); LCD_WriteData(y & 0xFF);
LCD_WriteData((y+h-1) >> 8); LCD_WriteData((y+h-1) & 0xFF);
LCD_WriteCmd(0x2C); // 开始写内存
}
// 刷一帧数据(320x240 RGB565)
void LCD_Refresh(uint16_t *fb) {
LCD_SetWindow(0, 0, 320, 240);
DC_HIGH();
CS_LOW();
for (int i = 0; i < 320 * 240; i++) {
SPI_Transmit(fb[i] >> 8); // 高字节
SPI_Transmit(fb[i] & 0xFF); // 低字节
}
CS_HIGH();
}
SPI接口的瓶颈很明显——速度。SPI时钟一般20-40MHz,刷一帧320×240的RGB565图,需要传输320×240×2=153,600字节。算下来:153600 × 8 / 20MHz ≈ 61ms,也就是16帧/秒。再快就吃力了。
我的经验:SPI接口适合小屏(2.8寸以下)或者静态界面。如果你要做动画或者视频播放,别用SPI,会卡到你怀疑人生。我曾经用SPI驱动一个320×480的屏做菜单滑动,帧率只有12fps,用户体验极差。
RGB接口
RGB接口是并行的。R、G、B各用5-8根数据线,加上HSYNC(行同步)、VSYNC(帧同步)、DOTCLK(像素时钟)、DE(数据使能)。
说白了,RGB接口就是直接把帧缓冲里的像素数据,一个像素一个像素地并行发送给屏幕。速度极快,但引脚多——16位RGB需要至少18根线,24位需要26根。
三种接口对比
| 特性 | SPI | RGB | MIPI DSI |
|---|---|---|---|
| 引脚数 | 4-6 | 18-26 | 2-4(差分对) |
| 最大速度 | ~40MHz | ~100MHz | ~1Gbps/Lane |
| 传输方式 | 串行 | 并行 | 高速串行差分 |
| 典型分辨率 | 320×240以下 | 800×480以下 | 1920×1080以上 |
| 是否需要帧缓冲 | 需要(MCU端) | 需要(MCU端) | 需要(屏端或MCU端) |
| 典型应用 | 小尺寸彩屏、OLED | 中尺寸TFT | 手机、平板、高清屏 |
RGB接口的时序要求很严格。每个像素时钟DOTCLK都要精确,HSYNC和VSYNC的脉冲宽度、前后肩都要配好。我调RGB接口时,最常干的事就是拿逻辑分析仪抓时序,看HSYNC的周期对不对。
MIPI DSI接口
MIPI DSI是手机和平板上最常用的接口。它用差分信号传输,一对差分线就能跑几百Mbps到1Gbps。4对差分线(4 Lane)加起来,带宽足够驱动4K屏。
DSI有两种模式:
- 命令模式(Command Mode):类似SPI,MCU发命令和数据给屏幕,屏幕自带帧缓冲和控制器。适合低功耗场景。
- 视频模式(Video Mode):类似RGB接口,实时传输像素流,屏幕没有帧缓冲。适合高刷新率场景。
注意:MIPI DSI的物理层是差分信号,PCB布线要等长、阻抗匹配(100Ω差分阻抗)。我见过一个项目,DSI线走了10cm没做等长,结果画面有雪花点。嗯,高频信号就是这么娇气。
接口选择决策树
我根据经验总结了一个简单的选择逻辑:
好了,这一章的内容就这些。像素深度决定了颜色质量,帧缓冲是CPU和屏幕之间的桥梁,显示接口决定了传输速度和引脚开销。理解这三层,你就能看懂市面上绝大多数嵌入式显示方案了。