1. 课程导入

做嵌入式显示开发,第一件事就是搞懂屏幕到底怎么工作的。很多人上来就调Qt,结果画面出不来,连问题出在硬件还是驱动层都分不清。我刚开始做这块时也踩过这个坑——调了三天,最后发现是Framebuffer的像素格式没配对。

这一章,咱们把显示系统的几个核心概念理清楚。不搞虚的,全是实战用得上的东西。

1.1 LCD工作原理:像素是怎么亮起来的

LCD屏幕说白了就是一个像素矩阵。每个像素由红、绿、蓝三个子像素组成。控制电路给每个子像素施加不同的电压,液晶分子偏转角度不同,透光率就不一样,最终混合出我们看到的颜色。

核心流程: 应用层 → 驱动层 → 显示控制器 → LCD面板

数据从内存到屏幕,中间经过三个关键环节:

  • 帧缓冲:存放一帧画面的内存区域
  • 显示控制器:从帧缓冲读取数据,按时序发送给屏幕
  • LCD面板:接收数据并显示

这里有个关键参数——刷新率。比如60Hz,意味着每秒刷新60次。每次刷新,显示控制器都要把整帧数据从内存搬到屏幕上。分辨率越高,数据量越大,对总线带宽的要求也越高。

我个人的经验: 调试时如果屏幕出现撕裂(tearing),多半是帧缓冲的写入速度和显示控制器的读取速度不同步。解决办法是用双缓冲或者开启垂直同步。

1.2 显示接口:FBI/DSI/DPI

显示控制器和LCD面板之间怎么连?这就涉及到接口协议了。嵌入式领域最常见的是这三种:

接口类型 全称 特点 典型应用
FBI Frame Buffer Interface 并行接口,数据线多,速度快 早期小尺寸LCD
DSI Display Serial Interface 串行差分信号,线少,抗干扰强 手机、平板、车载
DPI Display Pixel Interface 并行RGB接口,直接传输像素数据 中低端嵌入式设备

FBI 其实是个比较老的概念了。它直接暴露帧缓冲的地址和数据总线给LCD控制器。优点是简单直接,缺点是占用的GPIO太多,布线麻烦。

DSI 是现在的主流。它用差分信号传输,只需要几根线就能搞定高分辨率显示。我做过一个项目,用DSI接口驱动1080p的屏幕,布线比FBI少了一半还多。

DPI 也叫RGB接口。它直接传输像素的R、G、B分量,外加行同步、场同步、时钟等信号。调试时用逻辑分析仪抓DPI的波形,能很直观地看到每一行像素的传输过程。

注意: 接口选型直接影响驱动开发难度。DSI需要配置额外的D-PHY和协议层,调试起来比DPI复杂。如果你的项目对成本敏感、分辨率不高,DPI是更稳妥的选择。

1.3 帧缓冲(Framebuffer)概念

帧缓冲,说白了就是一块内存区域。应用程序把要显示的画面数据写进去,显示控制器定时从里面读出来送给屏幕。

在Linux系统中,帧缓冲被抽象为一个设备节点——/dev/fb0。你可以像操作普通文件一样读写它。

// 打开帧缓冲设备
int fd = open("/dev/fb0", O_RDWR);
if (fd < 0) {
    perror("open fb0 failed");
    return -1;
}

// 获取屏幕参数
struct fb_var_screeninfo vinfo;
ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);

// 计算一帧数据大小
size_t screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

// 映射到用户空间
char *fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

// 画一个红色像素
*(fbp + 0) = 0x00;     // B
*(fbp + 1) = 0x00;     // G
*(fbp + 2) = 0xFF;     // R

munmap(fbp, screensize);
close(fd);

这段代码我几乎每个项目都会用到。注意几个关键点:

  • bits_per_pixel:每个像素占多少位。常见的有16位(RGB565)、24位(RGB888)、32位(ARGB8888)。
  • mmap:把内核空间的帧缓冲映射到用户空间,这样应用程序可以直接读写。
  • FBIOGET_VSCREENINFO:获取当前可变屏幕信息,包括分辨率、像素格式等。

避坑指南: 我曾经在一个项目里发现屏幕显示颜色不对,红色变成了蓝色。查了半天,原来是帧缓冲的像素格式是BGR而不是RGB。Linux内核默认的帧缓冲格式是RGB565,但有些LCD控制器硬件上用的是BGR顺序。遇到颜色错乱,先检查这个。

1.4 显示系统的整体架构

把上面几个概念串起来,整个显示系统的架构就清晰了。我画了一张图,帮你理解数据是怎么从应用程序流到屏幕上的。

嵌入式显示系统架构图 嵌入式显示系统架构 应用程序 系统调用 (open/ioctl/mmap) 帧缓冲驱动 (Framebuffer Driver) 显示控制器 (Display Controller) LCD 面板 DSI / DPI / FBI 接口协议 帧缓冲内存 (/dev/fb0) 数据流向 应用程序 → 系统调用 → 驱动 → 控制器 → 屏幕

从这张图可以清楚看到:

  • 应用程序通过系统调用操作帧缓冲设备
  • 帧缓冲驱动管理内存映射和DMA传输
  • 显示控制器按时序从帧缓冲读取数据
  • 通过DSI/DPI/FBI接口发送给LCD面板

调试思路: 画面出问题时,按这个链路逐层排查。先确认应用程序写入了正确的像素数据,再检查帧缓冲的内存映射是否成功,最后用示波器看显示控制器的输出时序。我一般从硬件层往上查,效率最高。

1.5 本章小结

这一章我们覆盖了三个核心概念:

  • LCD工作原理:像素矩阵、刷新率、颜色混合
  • 显示接口:FBI、DSI、DPI的区别和选型
  • 帧缓冲:内存映射、设备节点、像素格式