Linux LCD驱动开发
一、硬件准备与原理
-
LCD接口类型
- RGB并行接口:需配置时序参数(HSYNC/VSYNC/DCLK)
- LVDS:差分信号传输,需设置通道映射
- MIPI-DSI:高速串行接口,需初始化CMD/VIDEO模式
- SPI接口:用于小屏驱动(如128x64 OLED)
-
关键硬件参数
- 分辨率:如800x480
- 像素格式:RGB565、ARGB8888
- 时序参数(以RGB为例):
h_back_porch = 40; // 行后沿 h_front_porch = 5; // 行前沿 h_sync_len = 48; // 行同步脉冲宽度 v_back_porch = 13; // 场后沿 v_front_porch = 3; // 场前沿 v_sync_len = 3; // 场同步脉冲宽度
二、设备树(Device Tree)配置
&lcdif {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcdif_dat /* 数据线 */
&pinctrl_lcdif_ctrl /* 控制线 */>;
display = <&display0>;
display0: display {
bits-per-pixel = <16>; // RGB565
bus-width = <24>; // 数据线位数
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <33000000>; // 像素时钟33MHz
hactive = <800>; // 水平有效像素
vactive = <480>; // 垂直有效像素
hback-porch = <40>; // 行后沿
hfront-porch = <5>; // 行前沿
hsync-len = <48>; // 行同步脉宽
vback-porch = <13>; // 场后沿
vfront-porch = <3>; // 场前沿
vsync-len = <3>; // 场同步脉宽
hsync-active = <0>; // 同步信号极性
vsync-active = <0>;
};
};
};
};
三、驱动开发步骤
1、Framebuffer驱动框架
static struct fb_ops mylcd_fb_ops = {
.owner = THIS_MODULE,
.fb_setcolreg = mylcd_setcolreg, // 调色板设置
.fb_fillrect = cfb_fillrect, // 矩形填充
.fb_copyarea = cfb_copyarea, // 区域复制
.fb_imageblit = cfb_imageblit, // 图像绘制
};
static int mylcd_probe(struct platform_device *pdev)
{
// 1. 获取设备树参数
ret = of_get_videomode(np, &vm, OF_USE_NATIVE_MODE);
// 2. 申请framebuffer
fb_info = framebuffer_alloc(sizeof(struct mylcd_data), &pdev->dev);
// 3. 配置fb_var_screeninfo
var->xres = 800;
var->yres = 480;
var->bits_per_pixel = 16;
// 4. 映射显存
fb_info->screen_base = dma_alloc_wc(dev, fb_size, &dma_handle, GFP_KERNEL);
// 5. 注册fb设备
register_framebuffer(fb_info);
}
2、DRM驱动开发(现代内核推荐)
static const struct drm_driver mylcd_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM,
.gem_prime_export = drm_gem_prime_export,
.ioctls = mylcd_ioctls,
.fops = &mylcd_drm_fops,
};
static int mylcd_load(struct drm_device *dev)
{
// 1. 初始化显示控制器
mylcd_hw_init(dev);
// 2. 创建DRM crtc/encoder/connector
drm_crtc_init(dev, &mylcd_crtc, &mylcd_crtc_funcs);
drm_encoder_init(dev, &mylcd_encoder, &mylcd_encoder_funcs);
drm_connector_init(dev, &mylcd_connector, &mylcd_connector_funcs);
// 3. 设置显示模式
drm_mode_create(dev, &native_mode);
}
四、调试技巧
-
查看显示参数
# 查看当前显示模式 cat /sys/class/graphics/fb0/modes # 查看显存信息 dmesg | grep -i dma
-
时序验证工具
- 示波器测量:检查HSYNC/VSYNC/DCLK波形
- 逻辑分析仪:捕获RGB数据线信号
-
常见问题排查
- 花屏:检查像素格式(RGB565 vs RGB888)
- 黑屏:确认背光控制信号(GPIO/PWM)
- 画面偏移:调整前后沿参数
五、性能优化
-
使用DMA传输
// 配置DMA描述符 dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); dma_chan = dma_request_channel(mask, NULL, NULL); // 启动异步传输 struct dma_async_tx_descriptor *desc; desc = dmaengine_prep_dma_memcpy(dma_chan, dst, src, size, 0); dmaengine_submit(desc); dma_async_issue_pending(dma_chan);
-
双缓冲技术
// 在驱动中实现页面翻转 drm_mode_config_funcs.page_flip = mylcd_page_flip; // 用户空间通过ioctl触发 struct drm_mode_crtc_page_flip flip = { .fb_id = new_fb_id, .flags = DRM_MODE_PAGE_FLIP_EVENT, }; ioctl(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip);
六、完整开发流程示例
以IMX6ULL + 800x480 RGB屏为例:
-
硬件连线:
- RGB数据线:D0-D23 → LCD DB0-DB23
- 控制线:HSYNC/VSYNC/DCLK → LCD对应引脚
- 背光控制:GPIO1_IO04 → LCD_BL
-
设备树配置(见前文)
-
驱动加载:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 insmod mylcd.ko
-
用户空间测试:
# 显示颜色测试 echo 0xFF0000 > /sys/class/graphics/fb0/color