4. Framebuffer 核心数据结构详解

在嵌入式 Linux 系统中,Framebuffer 设备通过 /dev/fbX 暴露给用户空间。应用程序通过 ioctl() 系统调用获取或设置显示参数,这些参数封装在三个核心数据结构中:struct fb_var_screeninfo(可变信息)、struct fb_fix_screeninfo(固定信息)和 struct fb_bitfield(位域描述)。理解这三个结构体是操作 Framebuffer 的基础。

4.1 struct fb_var_screeninfo:可变显示参数

该结构体描述应用程序可以动态修改的显示参数,包括分辨率、像素格式、刷新率等。每次切换显示模式或调整图层大小时,都需要操作此结构体。

成员类型说明
xres__u32可见水平分辨率(像素)
yres__u32可见垂直分辨率(像素)
xres_virtual__u32虚拟水平分辨率(支持双缓冲时大于 xres)
yres_virtual__u32虚拟垂直分辨率
xoffset__u32虚拟缓冲区到可见区的水平偏移
yoffset__u32虚拟缓冲区到可见区的垂直偏移
bits_per_pixel__u32每个像素的位数(16/24/32)
grayscale__u32灰度标志(0 为彩色)
redstruct fb_bitfield红色通道位域
greenstruct fb_bitfield绿色通道位域
bluestruct fb_bitfield蓝色通道位域
transpstruct fb_bitfield透明度通道位域
pixclock__u32像素时钟(皮秒)
vmode__u32视频模式(FB_VMODE_NONINTERLACED 等)
实战要点:在图层合成项目中,xres_virtual 和 yres_virtual 用于实现双缓冲或多缓冲。通过修改 yoffset 快速切换显示帧,避免撕裂。

4.2 struct fb_fix_screeninfo:固定显示参数

该结构体描述硬件固定的参数,在设备初始化后不再改变。应用程序通过它获取帧缓冲区的物理地址、长度和行字节对齐信息。

成员类型说明
idchar[16]驱动名称标识
smem_startunsigned long帧缓冲物理起始地址
smem_len__u32帧缓冲总长度(字节)
type__u32显示类型(FB_TYPE_PACKED_PIXELS 等)
visual__u32视觉类型(FB_VISUAL_TRUECOLOR 等)
line_length__u32一行像素占用的字节数(含对齐填充)
mmio_startunsigned longMMIO 物理起始地址
mmio_len__u32MMIO 区域长度
accel__u32硬件加速能力标志
关键注意:line_length 不一定等于 xres * (bits_per_pixel / 8)。硬件可能要求每行按 4 字节、8 字节或 16 字节对齐,因此计算像素偏移时必须使用 line_length,否则会出现显示错位。

4.3 struct fb_bitfield:颜色通道位域描述

该结构体描述每个颜色通道在像素数据中的位偏移和宽度。对于 TrueColor 模式,应用程序需要根据位域信息将 RGB 值编码到像素中。

struct fb_bitfield {
    __u32 offset;   /* 通道在像素中的起始位偏移 */
    __u32 length;   /* 通道占用的位数 */
    __u32 msb_right;/* 0 表示 MSB 在左侧,非 0 表示 MSB 在右侧 */
};

例如,常见的 RGB565 格式:红色 5 位(偏移 11)、绿色 6 位(偏移 5)、蓝色 5 位(偏移 0)。

编码示例:给定 R=31, G=63, B=31,在 RGB565 下像素值为 (R << 11) | (G << 5) | B。

4.4 数据结构关系与内存映射流程

下图展示了三个结构体在 Framebuffer 初始化与操作中的协作关系:

Framebuffer 数据结构关系图 Framebuffer 数据结构关系与内存映射流程 struct fb_var_screeninfo 可变参数:分辨率、位深、偏移 struct fb_fix_screeninfo 固定参数:地址、长度、行字节 struct fb_bitfield 通道位域:偏移、长度、MSB mmap() 映射帧缓冲到用户空间 帧缓冲内存(smem_start ~ smem_len) 按 line_length 对齐的行像素数据 流程说明: ① ioctl(FBIOGET_VSCREENINFO) 获取 var → ② ioctl(FBIOGET_FSCREENINFO) 获取 fix → ③ 根据 var.bits_per_pixel 和 fb_bitfield 编码像素 → ④ mmap(fix.smem_start, fix.smem_len) 映射 → ⑤ 直接写内存刷新显示

4.5 典型操作流程代码

以下代码展示如何获取并验证三个结构体的关键字段:

#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.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;

    // 获取可变信息
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &var)) {
        perror("FBIOGET_VSCREENINFO");
        close(fbfd);
        return -1;
    }

    // 获取固定信息
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &fix)) {
        perror("FBIOGET_FSCREENINFO");
        close(fbfd);
        return -1;
    }

    printf("分辨率: %dx%d, 位深: %d\n",
           var.xres, var.yres, var.bits_per_pixel);
    printf("行字节: %d, 总长度: %d\n",
           fix.line_length, fix.smem_len);
    printf("R: offset=%d len=%d, G: offset=%d len=%d, B: offset=%d len=%d\n",
           var.red.offset, var.red.length,
           var.green.offset, var.green.length,
           var.blue.offset, var.blue.length);

    // 计算实际像素行字节
    int expected_line = var.xres * (var.bits_per_pixel / 8);
    if (fix.line_length != expected_line) {
        printf("注意:硬件对齐,line_length=%d, 理论值=%d\n",
               fix.line_length, expected_line);
    }

    // 映射帧缓冲
    size_t screensize = var.yres_virtual * fix.line_length;
    char *fbp = (char *)mmap(NULL, screensize,
                             PROT_READ | PROT_WRITE,
                             MAP_SHARED, fbfd, 0);
    if (fbp == MAP_FAILED) {
        perror("mmap");
        close(fbfd);
        return -1;
    }

    // 使用 fbp 进行像素操作...

    munmap(fbp, screensize);
    close(fbfd);
    return 0;
}

4.6 常见像素格式与位域配置对照

格式bppR offsetR lengthG offsetG lengthB offsetB length
RGB565161155605
RGB888241688808
ARGB8888321688808
BGRA88883288168248
图层合成提示:在叠加光标或图层时,务必确认每个图层的 fb_bitfield 配置一致。如果主图层是 RGB565,光标图层也必须是 RGB565,否则合成时颜色通道会错乱。对于 32 位 ARGB 格式,transp 位域描述 Alpha 通道,用于实现透明度混合。

4.7 结构体差异对比总结

下表从多个维度对比三个结构体的核心差异:

对比维度fb_var_screeninfofb_fix_screeninfofb_bitfield
可变性可读写(应用可修改)只读(硬件固定)随 var 变化
主要用途设置分辨率、位深、双缓冲偏移获取内存地址、行对齐、总大小编码/解码像素颜色
获取方式FBIOGET_VSCREENINFO / FBIOPUT_VSCREENINFOFBIOGET_FSCREENINFO从 var 中读取
图层合成关联决定图层尺寸和像素格式决定 mmap 大小和行偏移计算决定像素编码/解码逻辑
常见错误未设置虚拟分辨率导致双缓冲失败忽略 line_length 导致显示错位位域偏移计算错误导致颜色失真
避坑指南:在嵌入式设备上,fb_var_screeninfo 的 pixclock 字段通常为 0,表示使用默认时钟。不要依赖该字段计算帧率。另外,部分驱动不支持修改 xres_virtual,修改前应先尝试读取,若失败则保持默认值。