Linux Framebuffer

一、Framebuffer 是什么

二、打开 Framebuffer

int fbfd = open("/dev/fb0", O_RDWR);
if (fbfd < 0) { perror("open fb"); return -1; }

// 1. 获取可变参数
struct fb_var_screeninfo vinfo;
ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);
printf("Resolution: %dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

// 2. 获取固定参数
struct fb_fix_screeninfo finfo;
ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo);
printf("Line length: %d, smem_len: %d\n", finfo.line_length, finfo.smem_len);

// 3. mmap 显示内存
size_t screensize = finfo.smem_len;
char *fbp = mmap(NULL, screensize, PROT_READ|PROT_WRITE, MAP_SHARED, fbfd, 0);
if (fbp == MAP_FAILED) { perror("mmap"); return -1; }

三、绘制像素

static inline void put_pixel(char *fb, int x, int y, uint32_t color)
{
    int bpp = vinfo.bits_per_pixel / 8;
    int offset = y * finfo.line_length + x * bpp;

    if (bpp == 4) {
        *((uint32_t*)(fb + offset)) = color;
    } else if (bpp == 2) {
        *((uint16_t*)(fb + offset)) = (uint16_t)color;
    }
}

四、绘制矩形 / 圆 / 文字

// 矩形
void fill_rect(char *fb, int x, int y, int w, int h, uint32_t color)
{
    for (int j = y; j < y + h; j++)
        for (int i = x; i < x + w; i++)
            put_pixel(fb, i, j, color);
}

// 圆
void draw_circle(char *fb, int cx, int cy, int r, uint32_t color)
{
    for (int y = cy - r; y <= cy + r; y++) {
        for (int x = cx - r; x <= cx + r; x++) {
            int dx = x - cx, dy = y - cy;
            if (dx*dx + dy*dy <= r*r)
                put_pixel(fb, x, y, color);
        }
    }
}

文字:用 freetype 或位图字体(自实现)。

五、双缓冲 / Page Flip

// 多缓冲
struct fb_var_screeninfo orig;
ioctl(fbfd, FBIOGET_VSCREENINFO, &orig);

// 切换 yoffset
vinfo.yoffset = offset;
ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo);

// vsync
int arg = 0;
ioctl(fbfd, FBIO_WAITFORVSYNC, &arg);

六、显示方向与 alpha

# 旋转
echo 1 > /sys/class/graphics/fbcon/rotate       # 90 度
# 或 ioctl 改 xres/yres 交换

# 半透明 / alpha
# 需要内核支持 alpha blending

七、应用

八、设备树

&lcdif {
    pinctrl-0 = <&pinctrl_lcdif>;
    pinctrl-names = "default";
    status = "okay";

    display@0 {
        bits-per-pixel = <32>;
        bus-width = <24>;
        display-timings {
            native-mode = <&timing0>;
            timing0: 480x800p {
                clock-frequency = <32000000>;
                hactive = <480>;
                vactive = <800>;
                hfront-porch = <8>;
                hback-porch = <40>;
                hsync-len = <2>;
                vfront-porch = <4>;
                vback-porch = <8>;
                vsync-len = <2>;
                hsync-active = <0>;
                vsync-active = <0>;
                de-active = <1>;
            };
        };
    };
};

九、调试

# 信息
cat /sys/class/graphics/fb0/name
cat /sys/class/graphics/fb0/bits_per_pixel
fbset -i

# 测试
cat /dev/urandom > /dev/fb0        # 雪花屏
dd if=/dev/zero of=/dev/fb0         # 黑屏

# 截图
cp /dev/fb0 /tmp/screen.raw
# 转换:使用 ffmpeg 或 Python PIL

十、性能