4. 屏幕驱动与背光控制:LVDS/eDP时序配置、PWM调光策略、屏幕初始化序列

屏幕驱动这块,说白了就是让SoC和屏幕面板“对上话”。8295要驱动多块屏,每块屏的接口、时序、背光都得单独伺候。我最早做车载显示时,以为时序配对了就行,结果屏幕死活不亮——后来才发现,初始化序列里少发了一条命令。

嗯,咱们今天就把这三块掰开揉碎了讲。

4.1 LVDS与eDP接口的时序配置

先看接口。LVDS和eDP是目前车载屏的主流选择。LVDS成熟、抗干扰好,但带宽有限;eDP带宽高、线束少,适合高分辨率屏。

对比项 LVDS eDP
传输方式 差分对,多对数据线+时钟 差分对,内嵌时钟,可多lane
典型带宽 单链路~1.2Gbps HBR2 ~5.4Gbps/lane
线束数量 较多(4~8对) 较少(2~4对)
适用分辨率 1920x720@60fps以内 4K@60fps及以上
初始化流程 纯硬件时序,无需握手 需AUX通道握手+Link Training

时序配置的核心,是让SoC的显示控制器输出与面板的时序要求完全匹配。我习惯把时序参数拆成三组来看:

  • 像素时钟:决定了每秒钟刷多少个像素点。算错了,画面要么撕裂,要么黑屏。
  • 同步信号:Hsync和Vsync的极性、脉宽、前后肩。这些参数决定了画面从哪里开始、到哪里结束。
  • 数据使能:DE信号的有效区间,必须和像素数据对齐。

举个例子,一块1920x720的LVDS屏,典型时序参数长这样:

// 像素时钟:74.25MHz
// Hsync:脉宽 40,前肩 88,后肩 148
// Vsync:脉宽 5,前肩 2,后肩 15
// 有效分辨率:1920 x 720

struct display_timing {
  .pixelclock = 74250000,
  .hactive = 1920,
  .hfront_porch = 88,
  .hback_porch = 148,
  .hsync_len = 40,
  .vactive = 720,
  .vfront_porch = 2,
  .vback_porch = 15,
  .vsync_len = 5,
  .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
};
注意:LVDS的极性配置不能搞反。我曾经遇到一块屏,Hsync极性反了,画面整体右移了半个屏幕。查了两天才发现是极性位配反了。

eDP的时序配置比LVDS多一步——Link Training。说白了,就是SoC和面板之间先“握个手”,确认用多少条lane、多高速率来传数据。8295的eDP控制器支持HBR2(5.4Gbps/lane),但面板不一定支持。我建议在驱动里做降级处理:先尝试HBR2,如果Training失败,自动降到HBR(2.7Gbps)。

4.2 PWM调光策略

背光控制,车载屏的刚需。白天要亮到能看清,晚上要暗到不刺眼。PWM调光是最常用的方案。

PWM调光的核心参数就两个:频率和占空比。

  • 频率:决定了人眼是否感知到闪烁。我一般设到200Hz以上,低于100Hz会有明显频闪,开车时容易头晕。
  • 占空比:决定了亮度。0%全黑,100%最亮。但实际面板的亮度-占空比曲线不是线性的,需要做Gamma校正。

8295内部有专用的PWM定时器,可以独立配置频率和极性。我常用的配置流程:

  1. 设置PWM时钟源(通常用系统主时钟分频)
  2. 配置周期寄存器(决定频率)
  3. 配置占空比寄存器(决定亮度)
  4. 使能PWM输出
// 以50kHz PWM为例,主时钟100MHz
// 周期 = 100MHz / 50kHz = 2000
// 占空比50%时,高电平时间 = 1000

pwm_set_clk(100000000);          // 设置时钟源
pwm_set_period(2000);            // 设置周期
pwm_set_duty(1000);              // 设置占空比
pwm_set_polarity(1);             // 高电平有效
pwm_enable(1);                   // 使能输出
经验之谈:车载环境对EMI敏感。PWM频率不要选在AM广播频段(530-1710kHz)附近,否则背光驱动线会变成天线。我一般避开这个区间,选200Hz~20kHz或2MHz以上。

还有一个坑:PWM与显示刷新率的同步。如果PWM频率和屏幕刷新率不是整数倍关系,屏幕上会出现滚动暗条(俗称“水波纹”)。我建议PWM频率设为刷新率的整数倍,比如60Hz刷新率配240Hz PWM。

4.3 屏幕初始化序列

屏幕初始化,是驱动开发里最容易出问题的地方。每块屏都有自己的“脾气”,初始化序列必须严格按照面板厂商提供的时序来。

初始化序列一般包含:

  • 上电时序:VDD、VGL、VGH等电源的上下电顺序和延时
  • 复位时序:RESET引脚的拉低/拉高时序
  • 寄存器配置:通过I2C/SPI写入面板控制寄存器
  • 显示使能:最后发送Display On命令

我见过最典型的错误,是上电时序没满足。某块屏要求VDD先上电,延时10ms后再上VGL。结果驱动里两个电源同时上,屏幕直接保护性黑屏。

下面是一个典型的初始化序列示例:

// 1. 上电
gpio_set(VDD_EN, 1);
mdelay(10);
gpio_set(VGL_EN, 1);
mdelay(5);
gpio_set(VGH_EN, 1);
mdelay(10);

// 2. 复位
gpio_set(RESET, 0);
mdelay(1);
gpio_set(RESET, 1);
mdelay(120);  // 等待内部初始化完成

// 3. 通过I2C配置寄存器
i2c_write(0x3C, 0x11, 0x00);  // Sleep Out
mdelay(120);
i2c_write(0x3C, 0x36, 0x70);  // MADCTL: 旋转方向
i2c_write(0x3C, 0x3A, 0x55);  // 像素格式: 16bit

// 4. 显示使能
i2c_write(0x3C, 0x29, 0x00);  // Display On
mdelay(50);
关键点:初始化序列里的延时不能随意缩短。面板内部需要时间完成电荷泵升压、Gamma校正、DCDC稳定等操作。我习惯在每步之间留20%的余量。

8295的显示控制器支持多路DSI/DPI输出,可以同时初始化多块屏。但要注意:每块屏的初始化序列是独立的。不能因为主屏初始化成功了,就认为副屏也能用同一套参数。我遇到过副屏和主屏型号不同,但用了同一份初始化代码,结果副屏颜色完全不对。

4.4 知识结构总览

下面这张图,把本章的核心逻辑串起来了:

屏幕驱动与背光控制知识结构 屏幕驱动与背光控制知识结构 LVDS/eDP时序配置 PWM调光策略 屏幕初始化序列 像素时钟 / 同步信号 / 数据使能 LVDS:纯硬件时序,无握手 eDP:需AUX握手 + Link Training 频率(200Hz~2MHz) 占空比(Gamma校正) 与刷新率同步(防滚动暗条) 上电时序(VDD/VGL/VGH) 复位时序 + 延时 寄存器配置 + Display On 核心原则:时序精确 + 调光平滑 + 序列完整 每块屏独立配置,不可复用

嗯,屏幕驱动这块,说到底就是“时序对、背光亮、初始化完整”。8295的硬件能力很强,但再强的SoC也架不住驱动代码里一个时序参数配错。我建议你在开发时,先把面板规格书里的时序参数做成结构体,然后逐项核对。别偷懒,每块屏单独测一遍。