4. Framebuffer 关键数据结构详解
Framebuffer 设备驱动通过三个核心数据结构向用户空间暴露显示参数与帧缓冲信息:struct fb_var_screeninfo(可变信息)、struct fb_fix_screeninfo(固定信息)和 struct fb_cmap(颜色映射表)。理解这三个结构体是进行帧率控制、分辨率调整和色彩深度配置的基础。
核心概念:可变信息描述可动态调整的显示参数(分辨率、像素格式、刷新率);固定信息描述硬件固定的内存布局(帧缓冲物理地址、长度、步长);颜色映射表用于 8/16 位色深下的伪彩色或真彩色查色表。
4.1 struct fb_var_screeninfo — 可变显示参数
该结构体定义在 linux/fb.h 中,用于描述用户可修改的显示模式参数。帧率控制主要依赖其中的时序字段。
struct fb_var_screeninfo {
__u32 xres; /* 可见水平分辨率(像素) */
__u32 yres; /* 可见垂直分辨率(像素) */
__u32 xres_virtual; /* 虚拟水平分辨率(像素) */
__u32 yres_virtual; /* 虚拟垂直分辨率(像素) */
__u32 xoffset; /* 虚拟到可见的水平偏移 */
__u32 yoffset; /* 虚拟到可见的垂直偏移 */
__u32 bits_per_pixel; /* 每像素位数(bpp) */
__u32 grayscale; /* 非零表示灰度设备 */
struct fb_bitfield red; /* 红色位域 */
struct fb_bitfield green; /* 绿色位域 */
struct fb_bitfield blue; /* 蓝色位域 */
struct fb_bitfield transp;/* 透明度位域 */
__u32 nonstd; /* 非标准像素格式标志 */
__u32 activate; /* 模式激活标志 */
__u32 height; /* 显示器高度(mm) */
__u32 width; /* 显示器宽度(mm) */
__u32 accel_flags; /* 硬件加速标志(已废弃) */
/* 时序参数:用于帧率计算 */
__u32 pixclock; /* 像素时钟(皮秒) */
__u32 left_margin; /* 行同步前沿(像素时钟周期数) */
__u32 right_margin; /* 行同步后沿 */
__u32 upper_margin; /* 帧同步前沿(行数) */
__u32 lower_margin; /* 帧同步后沿 */
__u32 hsync_len; /* 水平同步脉冲长度 */
__u32 vsync_len; /* 垂直同步脉冲长度 */
__u32 sync; /* 同步极性标志 */
__u32 vmode; /* 视频模式标志 */
__u32 rotate; /* 旋转角度 */
__u32 reserved[5]; /* 保留字段 */
};
帧率计算公式:
其中:
帧率 (Hz) = 1 / (pixclock × 总水平像素 × 总垂直像素) × 10¹²其中:
总水平像素 = xres + left_margin + right_margin + hsync_len总垂直像素 = yres + upper_margin + lower_margin + vsync_len
4.2 struct fb_fix_screeninfo — 固定帧缓冲信息
该结构体描述硬件固定的内存布局参数,用户空间通过 FBIOGET_FSCREENINFO ioctl 获取。帧缓冲的物理地址、长度和行步长直接影响帧缓存访问效率。
struct fb_fix_screeninfo {
char id[16]; /* 设备标识字符串,如 "s3c-fb" */
unsigned long smem_start; /* 帧缓冲物理起始地址 */
__u32 smem_len; /* 帧缓冲总长度(字节) */
__u32 type; /* 显示类型:FB_TYPE_PACKED_PIXELS 等 */
__u32 type_aux; /* 辅助类型 */
__u32 visual; /* 视觉类型:真彩色、伪彩色、直接色等 */
__u16 xpanstep; /* 水平 pan 步长(像素) */
__u16 ypanstep; /* 垂直 pan 步长(像素) */
__u16 ywrapstep; /* 垂直 wrap 步长 */
__u32 line_length; /* 一行像素占用的字节数(步长) */
unsigned long mmio_start; /* 内存映射 I/O 起始地址 */
__u32 mmio_len; /* 内存映射 I/O 长度 */
__u32 accel; /* 加速器类型 */
__u16 capabilities; /* 能力标志 */
__u16 reserved[2]; /* 保留 */
};
| 字段 | 含义 | 调试用途 |
|---|---|---|
smem_start |
帧缓冲物理地址 | DMA 缓冲区映射、mmap 偏移计算 |
smem_len |
帧缓冲总大小(字节) | 确认缓冲区是否足够双缓冲 |
line_length |
一行像素占用的字节数 | 计算帧率时需考虑对齐填充 |
visual |
视觉类型(真彩色/伪彩色) | 决定是否需要颜色映射表 |
type |
像素打包类型 | 判断是否支持 packed pixels |
常见陷阱:
line_length 可能大于 xres × (bits_per_pixel / 8),因为硬件会对每行进行对齐(如 32 字节对齐)。帧率计算和内存拷贝时必须使用 line_length 而非简单乘法。
4.3 struct fb_cmap — 颜色映射表
颜色映射表用于 8/16 位色深下的颜色索引转换。用户空间通过 FBIOGETCMAP / FBIOPUTCMAP ioctl 读取或设置颜色表。
struct fb_cmap {
__u32 start; /* 起始索引 */
__u32 len; /* 颜色条目数 */
__u16 *red; /* 红色分量数组(16位精度) */
__u16 *green; /* 绿色分量数组 */
__u16 *blue; /* 蓝色分量数组 */
__u16 *transp; /* 透明度分量数组(可选) */
};
4.4 三个结构体的协作关系
在 Framebuffer 驱动中,这三个结构体通过 ioctl 系统调用协同工作,构成完整的显示参数配置链路。
| 操作 | ioctl 命令 | 涉及结构体 | 典型用途 |
|---|---|---|---|
| 获取可变信息 | FBIOGET_VSCREENINFO |
fb_var_screeninfo |
读取当前分辨率、像素时钟 |
| 设置可变信息 | FBIOPUT_VSCREENINFO |
fb_var_screeninfo |
修改分辨率、帧率、bpp |
| 获取固定信息 | FBIOGET_FSCREENINFO |
fb_fix_screeninfo |
获取物理地址、步长 |
| 获取颜色表 | FBIOGETCMAP |
fb_cmap |
读取当前调色板 |
| 设置颜色表 | FBIOPUTCMAP |
fb_cmap |
更新调色板、实现颜色动画 |
实战要点:帧率控制的核心在于修改
fb_var_screeninfo.pixclock 和时序参数。修改后需调用 FBIOPUT_VSCREENINFO 使内核重新计算并配置显示控制器。注意:部分嵌入式 LCD 控制器对 pixclock 有最小/最大值限制,超出范围会导致无显示。
4.5 用户空间操作示例
以下代码片段展示如何通过 ioctl 获取并打印三个结构体的关键字段:
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
int main() {
int fbfd = open("/dev/fb0", O_RDWR);
if (fbfd < 0) { perror("open"); return 1; }
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
struct fb_cmap cmap;
__u16 red[256], green[256], blue[256];
// 获取可变信息
ioctl(fbfd, FBIOGET_VSCREENINFO, &var);
printf("分辨率: %dx%d, %d bpp\n", var.xres, var.yres, var.bits_per_pixel);
printf("像素时钟: %u ps, 帧率: %.2f Hz\n",
var.pixclock,
1.0 / (var.pixclock *
(var.xres + var.left_margin + var.right_margin + var.hsync_len) *
(var.yres + var.upper_margin + var.lower_margin + var.vsync_len)) * 1e12);
// 获取固定信息
ioctl(fbfd, FBIOGET_FSCREENINFO, &fix);
printf("物理地址: 0x%lx, 缓冲区大小: %u bytes\n", fix.smem_start, fix.smem_len);
printf("行步长: %u bytes/line\n", fix.line_length);
// 获取颜色表(仅伪彩色模式有效)
if (fix.visual == FB_VISUAL_PSEUDOCOLOR ||
fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) {
cmap.start = 0;
cmap.len = 256;
cmap.red = red;
cmap.green = green;
cmap.blue = blue;
cmap.transp = NULL;
ioctl(fbfd, FBIOGETCMAP, &cmap);
printf("颜色表已读取,共 %u 项\n", cmap.len);
}
close(fbfd);
return 0;
}
调试建议:使用
fbset 工具可以快速查看和修改 fb_var_screeninfo 中的时序参数。例如:fbset -fb /dev/fb0 -xres 1024 -yres 768 -pixclock 15384。修改后可通过 fbset -i 验证帧率变化。