在嵌入式Linux开发中,图像采集与显示是非常典型的一类应用场景。本文将基于 ARM9(S3C2410) 平台,深入讲解如何使用 V4L2 框架从 USB 摄像头采集图像数据,并通过 Framebuffer 接口实时显示到 LCD 屏幕。内容涵盖驱动架构理解、API 调用流程、图像格式转换、调试技巧等,旨在帮助你真正吃透这两个功能模块,并能在面试时灵活讲述、准确应对。
一、系统架构概览
本项目的整体功能是:
USB 摄像头 → V4L2 图像采集 → 图像格式转换 → Framebuffer → LCD 实时显示
涉及的核心模块:
- USB 摄像头(支持 UVC 协议)
- V4L2 图像采集框架
- 图像格式转换(YUYV → RGB565)
- Framebuffer 显示驱动
- RGB565 并口 LCD 显示屏
二、开发平台与环境
- 处理器平台:Samsung S3C2410 (ARM920T架构)
- 操作系统:嵌入式 Linux 2.6.x
- 摄像头接口:USB 2.0(支持UVC)
- 显示屏接口:RGB565并口LCD
- 开发语言:C语言
- 关键驱动子系统:V4L2、Framebuffer
三、V4L2 图像采集原理与代码实现
V4L2(Video4Linux2)是 Linux 系统中用于采集视频流的标准框架,支持众多 USB 摄像头。
3.1 V4L2 工作流程
text
open() → ioctl(VIDIOC_QUERYCAP) → 设置格式(VIDIOC_S_FMT) →
申请缓冲(VIDIOC_REQBUFS) → 映射缓冲(mmap) → 开启采集(VIDIOC_STREAMON) →
循环采集:dequeue → 处理图像 → queue → 重复
3.2 V4L2 图像采集核心代码
c
int init_camera(const char* dev_name, int* width, int* height) {
int fd = open(dev_name, O_RDWR);
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = *width;
fmt.fmt.pix.height = *height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
ioctl(fd, VIDIOC_S_FMT, &fmt);
// 请求缓冲
struct v4l2_requestbuffers req;
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_REQBUFS, &req);
// 映射缓冲区并启动采集...
// 参考完整示例:V4L2官方capture.c
return fd;
}
3.3 图像格式详解:YUYV 与 MJPG

YUYV(YUV 4:2:2)
- 一种未压缩格式,每两个像素共用一组色度分量(U、V),结构为:Y0 U Y1 V。
- 优点:色彩保真度较高,解码简单(不需要额外解码器)。
- 缺点:数据量大,占带宽(例如 640x480 每帧约 600KB)。
- 应用:适合实时图像处理,便于 CPU 直接操作像素数据。
MJPG(Motion JPEG)
- 每一帧图像单独压缩为 JPEG 格式(有压缩损失)。
- 优点:占用带宽小、适合高分辨率高帧率传输(如 1920x1080@30fps)。
- 缺点:需要 CPU/GPU 解码,延迟略高,数据不可直接用于图像分析。
- 应用:适合录制、流媒体、远程图传等低延迟不敏感场景。
3.4 YUYV 转 RGB565 示例
c
void yuyv_to_rgb565(unsigned char* yuyv, unsigned short* rgb, int width, int height) {
for (int i = 0; i < width * height * 2; i += 4) {
unsigned char y0 = yuyv[i];
unsigned char u = yuyv[i+1];
unsigned char y1 = yuyv[i+2];
unsigned char v = yuyv[i+3];
// YUV to RGB 转换略...
// 然后转换为 RGB565:
rgb[n++] = (r>>3)<<11 | (g>>2)<<5 | (b>>3);
}
}
四、Framebuffer 显示原理与实现
Framebuffer 是 Linux 下的一种显存映射机制,常见设备为 /dev/fb0
。
4.1 显存结构
通常为连续地址,可使用 mmap
将其映射到用户空间。
4.2 显示代码示例
c
int fb_fd = open("/dev/fb0", O_RDWR);
struct fb_var_screeninfo vinfo;
ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo);
unsigned short* fbp = (unsigned short*)mmap(NULL, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
// 拷贝图像数据至屏幕
memcpy(fbp, rgb565_data, width * height * 2);
五、LCD硬件接口说明(RGB565并口)
LCD 一般通过并口连接主控,使用 16 条数据线传输每个像素数据:
- 红色:5位(R0~R4)
- 绿色:6位(G0~G5)
- 蓝色:5位(B0~B4)
控制信号包括 VSYNC、HSYNC、DE、CLK 等,由 LCD 控制器生成。
你并不需要直接控制这些信号,Framebuffer 驱动会在内核里完成所有时序控制。
六、整体流程整合(main 函数)
c
int main() {
init_camera("/dev/video0", &width, &height);
init_fb("/dev/fb0", &fbp, &vinfo);
while (1) {
capture_frame(yuyv_buffer);
yuyv_to_rgb565(yuyv_buffer, rgb_buffer);
memcpy(fbp, rgb_buffer, width * height * 2);
}
return 0;
}
七、常见问题排查
1. 摄像头无法识别:
bash
v4l2-ctl --list-devices
ls /dev/video*
dmesg | grep uvc
2. 图像花屏或颜色错误:
- 检查颜色格式是否一致
- RGB转换算法是否正确
3. LCD 无显示:
- 检查
/dev/fb0
是否存在 - 检查分辨率是否匹配(LCD物理分辨率 vs framebuffer)
为了增强这篇博文的完整性与现代平台适配性,以下是推荐添加的"设备树配置讲解"章节内容,可直接插入至文末 "### 八、项目总结" 和 "### 九、面试讲解思路建议" 之间:
八补充、设备树配置讲解(适用于新平台)
虽然 S3C2410 早期平台不依赖设备树(DTS),但在后续如 i.MX6、RK3568 等现代平台上,摄像头(CSI)和 LCD(RGB 并口)都需要通过设备树进行配置以正确初始化底层硬件。因此,理解并掌握 DTS 配置是项目迁移和面试应答的加分项。
1. 摄像头节点(MIPI-CSI 摄像头)
对于非 USB 摄像头,如 MIPI-CSI 接口模块(例如 OV5640),设备树需声明其 I²C 地址及 CSI 连接:
dts
&i2c2 {
ov5640: camera@3c {
compatible = "ovti,ov5640";
reg = <0x3c>;
...
port {
ov5640_to_csi: endpoint {
remote-endpoint = <&csi_in>;
};
};
};
};
&csi {
status = "okay";
port {
csi_in: endpoint {
remote-endpoint = <&ov5640_to_csi>;
bus-width = <8>;
data-shift = <2>;
};
};
};
2. LCD节点(RGB并口)
若使用 RGB 并口 LCD,需配置显示控制器(如 LCDIF)与屏幕时序:
dts
&lcdif {
status = "okay";
display = <&display0>;
display0: display@0 {
bits-per-pixel = <16>; // RGB565
bus-width = <16>;
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <33000000>;
hactive = <800>;
vactive = <480>;
hsync-len = <48>;
hback-porch = <88>;
hfront-porch = <40>;
vsync-len = <3>;
vback-porch = <32>;
vfront-porch = <13>;
};
};
};
};
3. framebuffer 显示绑定(可选)
将 framebuffer 与具体 LCD 控制器绑定,指定默认显示设备:
dts
&fb {
status = "okay";
display = <&display0>;
assigned-clocks = <&clks IMX6QDL_CLK_LCDIF_PIX>;
};
小结:
- USB 摄像头 不需要设备树配置,系统自动枚举;
- CSI 摄像头 和 LCD 屏幕 需精确配置 I²C 地址、总线连接、显示时序;
- 不合理的 DTS 会导致显示黑屏或摄像头无法采集。