嵌入式 Linux— LCD 与 Framebuffer 驱动开发详解
一、LCD 显示基础知识
1.1 分辨率(Resolution)
LCD 显示器由一个个像素(Pixel)组成,分辨率表示横向像素 × 纵向像素的总量:
-
示例:
- 800×480 表示一行 800 像素,共 480 行
- 1920×1080(1080p) 是高清分辨率
1.2 像素格式(Pixel Format)
每个像素通常由 RGB 三个颜色通道 + 可选的 Alpha 通道组成:
-
常见格式:
RGB565
:16 bit(R:5, G:6, B:5)RGB888
:24 bit(R:8, G:8, B:8)ARGB8888
:32 bit(包含透明通道)
-
每像素字节数决定了显存大小和传输带宽
1.3 LCD 接口类型(以 NXP i.MX6ULL 为例)
i.MX6ULL 支持的接口为 并行 RGB 接口:
-
RGB 数据线:24 位(每种颜色各占 8 bit)
-
控制信号线:
HSYNC
:行同步VSYNC
:帧同步DE
(Data Enable):数据使能PCLK
:像素时钟,用于采样像素数据
通常需配置约 28 根引脚。
1.4 显存(Framebuffer)
Framebuffer 是存储当前图像像素数据的内存区域,供 LCD 控制器读取并显示。
-
显存大小 = 宽 × 高 × 每像素字节数
示例:800 × 480 × 4(ARGB8888)= 1.5 MB -
驱动会将显存通过
mmap
映射给用户态程序
1.5 LCD 时序参数
LCD 一帧图像由若干行构成,每行数据也有对应同步信号:
📌 水平时序(每行)
HSYNC
:行开始HBP
(Back Porch):同步后延时HFP
(Front Porch):数据发送完后延时HActive
:一行有效像素
📌 垂直时序(每帧)
VSYNC
:帧开始VBP
、VFP
:上下延时VActive
:有效显示的行数
🧮 帧率计算公式:
PCLK = (HActive + HBP + HFP + HSYNC) × (VActive + VBP + VFP + VSYNC) × 帧率
1.6 LCD模型
1.7 LCD控制器时序图
二、LCD 驱动原理与设备树配置
2.1 驱动控制器:eLCDIF
i.MX6ULL
内部集成 Enhanced LCD Interface (eLCDIF) 控制器- 功能包括:数据缓冲、格式转换、同步信号生成
2.2 设备树配置关键项
1)IOMUXC:引脚复用
为 LCD 分配所需引脚:
pinctrl_lcd: lcdgrp {
fsl,pins = <
MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79
MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79
...
>;
};
2)LCD 控制器节点配置
lcdif: lcdif@021c8000 {
compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif";
reg = <0x021c8000 0x4000>;
...
display = <&display0>;
};
3)display-timings 子节点(核心部分)
display0: display0 {
bits-per-pixel = <32>;
bus-width = <24>;
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <33264000>;
hactive = <800>;
vactive = <480>;
hback-porch = <40>;
hfront-porch = <40>;
vback-porch = <29>;
vfront-porch = <13>;
hsync-len = <48>;
vsync-len = <3>;
...
};
};
};
4)背光控制节点(PWM/GPIO)
backlight: backlight {
compatible = "pwm-backlight";
pwms = <&pwm1 0 5000000>;
brightness-levels = <0 64 128 255>;
default-brightness-level = <2>;
};
三、Framebuffer 子系统与驱动框架
3.1 什么是 Framebuffer
Framebuffer 是 Linux 提供的一种显示抽象接口,表现为字符设备 /dev/fb0
。
- 用户可以直接读写 framebuffer,显示图像
- 内核通过
fb_info
与硬件交互
3.2 fb_info 结构体关键字段
struct fb_info {
struct fb_var_screeninfo var; // 分辨率、像素格式
struct fb_fix_screeninfo fix; // 显存起始地址、长度
u8 *screen_base; // 显存虚拟地址
struct fb_ops *fbops; // 操作函数集
};
3.3 fb_ops 操作函数集
struct fb_ops {
int (*fb_open)(...);
ssize_t (*fb_read)(...);
ssize_t (*fb_write)(...);
int (*fb_ioctl)(...);
void (*fb_fillrect)(...); // 填充矩形
void (*fb_imageblit)(...); // 显示位图
};
四、自定义 Framebuffer 驱动开发流程
Step 1:分配 fb_info
struct fb_info *info = framebuffer_alloc(0, &pdev->dev);
Step 2:配置变量参数
info->var.xres = 800;
info->var.yres = 480;
info->var.bits_per_pixel = 32;
Step 3:申请显存并映射
info->screen_base = dma_alloc_wc(...); // 物理显存
info->fix.smem_start = virt_to_phys(info->screen_base);
Step 4:设置 fb_ops
info->fbops = &my_fb_ops;
Step 5:注册 framebuffer
register_framebuffer(info);
五、LCD 控制器初始化流程
在 LCD 控制器的初始化函数中需完成以下步骤:
- IO 引脚配置(复用 + 驱动能力)
- 设置时钟源:分频后供 LCDIF 控制器
- 设置同步参数(VSYNC/HSYNC/Porch 等)
- 绑定显存地址(从 Framebuffer 获取物理地址)
- 启动控制器输出数据
六、用户空间访问 framebuffer 原理
6.1 方式一:直接写入
cat image.raw > /dev/fb0
6.2 方式二:mmap 映射
fb = open("/dev/fb0", O_RDWR);
fbp = mmap(NULL, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
memcpy(fbp, buffer, screensize); // 直接写入显存
七、常见问题与调试建议
问题 | 检查点 |
---|---|
屏幕无显示 | 背光电源是否开启? |
图像错位 | 时序参数是否匹配? |
色彩异常 | 像素格式是否一致? |
无法写入 /dev/fb0 | 权限、驱动是否注册? |
工具命令推荐:
fbset # 查看 framebuffer 参数
cat /dev/fb0 > dump.raw # 导出显存数据
hexdump /dev/fb0 # 查看像素数据