4. fb_var_screeninfo 详解:可变参数结构体
fb_var_screeninfo 是 Framebuffer 驱动中描述显示设备可变参数的核心结构体。与固定不变的 fb_fix_screeninfo 不同,该结构体中的字段允许在运行时通过 FBIOPUT_VSCREENINFO 命令动态修改,从而实现分辨率切换、位深调整、像素格式变更以及时序参数重配等操作。本章将逐一拆解其关键字段,并给出典型配置示例。
核心概念: 可变参数结构体是用户态与内核态之间协商显示模式的桥梁。应用程序通过设置该结构体,驱动据此重新配置硬件控制器,实现显示模式的动态切换。
4.1 结构体总览与关键字段
fb_var_screeninfo 定义在 include/uapi/linux/fb.h 中,主要包含以下四大类参数:
| 参数类别 | 字段名 | 说明 | 典型值示例 |
|---|---|---|---|
| 分辨率 | xres / yres |
可见分辨率(有效显示区域) | 1024 / 768 |
xres_virtual / yres_virtual |
虚拟分辨率(含离屏缓存) | 1024 / 768 | |
| 位深与像素格式 | bits_per_pixel |
每个像素占用的比特数 | 32 |
red / green / blue / transp |
各颜色分量的位偏移与长度 | 见 4.3 节 | |
| 时序参数 | pixclock |
像素时钟周期(皮秒) | 40000 |
left_margin / right_margin / upper_margin / lower_margin / hsync_len / vsync_len |
行场消隐与同步脉冲宽度 | 见 4.4 节 | |
| 同步与标志 | sync / vmode |
同步极性、隔行/渐进模式 | FB_SYNC_HOR_HIGH_ACT |
4.2 分辨率配置:可见与虚拟
分辨率字段分为可见分辨率和虚拟分辨率两组:
xres/yres:实际显示在屏幕上的像素宽度和高度。xres_virtual/yres_virtual:Framebuffer 内存中实际分配的宽度和高度。虚拟分辨率可以大于可见分辨率,用于实现双缓冲、滚动显示等高级功能。
配置原则: 虚拟分辨率必须 ≥ 可见分辨率。当虚拟分辨率大于可见分辨率时,可通过设置
xoffset / yoffset 实现视口平移。
/* 典型配置:1024x768,虚拟分辨率与可见相同 */
info->var.xres = 1024;
info->var.yres = 768;
info->var.xres_virtual = 1024;
info->var.yres_virtual = 768;
4.3 位深与像素格式:颜色分量描述
bits_per_pixel 决定每个像素占用的比特数,常见值为 16、24、32。像素格式由 red、green、blue、transp 四个 fb_bitfield 结构体描述:
struct fb_bitfield {
__u32 offset; /* 颜色分量在像素中的起始位偏移 */
__u32 length; /* 颜色分量占用的比特数 */
__u32 msb_right;/* 0: 左对齐(MSB 在最高位),非0: 右对齐 */
};
以下为三种常见像素格式的配置对照:
| 格式 | bpp | Red (offset/length) | Green (offset/length) | Blue (offset/length) | Transp (offset/length) |
|---|---|---|---|---|---|
| RGB565 | 16 | 11 / 5 | 5 / 6 | 0 / 5 | 0 / 0 |
| RGB888 | 24 | 16 / 8 | 8 / 8 | 0 / 8 | 0 / 0 |
| ARGB8888 | 32 | 16 / 8 | 8 / 8 | 0 / 8 | 24 / 8 |
/* ARGB8888 格式配置示例 */
info->var.bits_per_pixel = 32;
info->var.red.offset = 16; info->var.red.length = 8;
info->var.green.offset = 8; info->var.green.length = 8;
info->var.blue.offset = 0; info->var.blue.length = 8;
info->var.transp.offset= 24; info->var.transp.length= 8;
4.4 时序参数:像素时钟与消隐区
时序参数决定了显示控制器如何扫描每一行和每一帧。关键字段包括:
pixclock:像素时钟周期,单位皮秒(ps)。频率 = 1e12 / pixclock (Hz)。left_margin/right_margin:行消隐前肩和后肩(像素数)。upper_margin/lower_margin:场消隐前肩和后肩(行数)。hsync_len/vsync_len:行同步脉冲宽度(像素数)和场同步脉冲宽度(行数)。
时序配置陷阱: 不同 LCD 控制器对消隐区的前后定义可能相反(HBP vs HFP)。务必参考硬件数据手册,确认 left_margin 对应的是 HBP(Horizontal Back Porch)还是 HFP(Horizontal Front Porch)。
/* 典型 1024x768 @60Hz 时序配置 */
info->var.pixclock = 40000; /* 25 MHz 像素时钟 */
info->var.left_margin = 160; /* HBP */
info->var.right_margin = 24; /* HFP */
info->var.upper_margin = 23; /* VBP */
info->var.lower_margin = 3; /* VFP */
info->var.hsync_len = 136; /* 行同步宽度 */
info->var.vsync_len = 6; /* 场同步宽度 */
4.5 同步与模式标志
sync 字段控制同步信号的极性:
| 宏定义 | 含义 |
|---|---|
FB_SYNC_HOR_HIGH_ACT |
行同步高电平有效 |
FB_SYNC_VERT_HIGH_ACT |
场同步高电平有效 |
FB_SYNC_EXT |
外部同步 |
FB_SYNC_COMP_HIGH_ACT |
复合同步高电平有效 |
vmode 字段指定扫描模式:
FB_VMODE_NONINTERLACED:渐进扫描(逐行)。FB_VMODE_INTERLACED:隔行扫描。FB_VMODE_DOUBLE:每行扫描两次。
4.6 可变参数配置流程图
以下 SVG 展示了从用户态请求到驱动配置 fb_var_screeninfo 的完整流程:
4.7 参数校验与回退机制
驱动在 fb_set_par() 中必须对用户传入的 fb_var_screeninfo 进行合法性校验:
- 分辨率范围检查:确保
xres/yres不超过硬件支持的最大值。 - 位深支持检查:确认
bits_per_pixel在硬件支持的位深列表中。 - 像素时钟范围检查:
pixclock必须在 PLL 可生成的范围内。 - 时序参数合理性检查:消隐区与同步宽度不能为负,且满足最小/最大约束。
最佳实践: 当用户传入的参数不被硬件支持时,驱动应尝试调整到最接近的合法值,并通过
FBIOPUT_VSCREENINFO 的返回值回写实际生效的参数。应用程序可通过 FBIAGET_VSCREENINFO 读取调整后的值。
/* 参数校验与回退示例 */
static int myfb_set_par(struct fb_info *info)
{
struct fb_var_screeninfo *var = &info->var;
/* 1. 检查分辨率 */
if (var->xres > 1920 || var->yres > 1080)
return -EINVAL;
/* 2. 检查位深 */
if (var->bits_per_pixel != 16 && var->bits_per_pixel != 32)
var->bits_per_pixel = 32; /* 回退到默认32bpp */
/* 3. 检查像素时钟范围 */
if (var->pixclock < 10000 || var->pixclock > 100000)
var->pixclock = 40000; /* 回退到25MHz */
/* 4. 配置硬件寄存器 */
myfb_set_timing(var);
myfb_set_format(var);
return 0;
}
4.8 常见配置错误与排查
| 错误现象 | 可能原因 | 排查方向 |
|---|---|---|
| 屏幕无显示 | pixclock 超出 PLL 范围 |
检查时钟树配置,确认 PLL 输出频率 |
| 图像偏移或撕裂 | 消隐区参数配置错误 | 核对 HBP/HFP 与数据手册是否一致 |
| 颜色显示异常 | 像素格式 offset/length 配置错误 |
确认 RGB 分量在内存中的实际排列顺序 |
| 屏幕闪烁 | 刷新率过低或 vsync_len 异常 |
计算实际帧率,调整时序参数 |
调试技巧: 在
fb_set_par() 中添加 dev_dbg() 打印所有 var 参数,与硬件数据手册中的时序表逐项比对。使用示波器测量 HSYNC/VSYNC 波形是验证时序配置最直接的方法。