Linux Framebuffer
一、Framebuffer 是什么
- Linux 内核提供的显示驱动接口
- 应用直接读写显示内存,无需 X11 / Wayland
- 嵌入式 GUI 的基础(如 Qt Embedded、LVGL)
二、打开 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
七、应用
- PSplash:启动 logo
- Qt Embedded(Qt5)/ Qt for MCU(Qt6):基于 fbdev 后端
- LVGL:流行轻量 GUI
- SDL1.2 / SDL2:跨平台多媒体
- OpenGL ES / Mesa:硬件加速
八、设备树
&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
十、性能
- 直接写 fb 比较慢
- 大块用 memcpy
- GPU 加速(DRM/KMS)
- DRM 是现代替代:libdrm / Mesa / EGL