确保有Framebuffer

ls /dev/fb*
#返回/dev/fb0

概念

Framebuffer 就是一块内存区域,里面存放着屏幕上每个像素的颜色值

LCD 控制器会周而复始地从 Framebuffer 中逐一取出每个像素的颜色,通过 RGB 数据线、时序信号(HSYNC、VSYNC、DE、DCLK)发送给 LCD 屏幕,屏幕就显示出图像。

Framebuffer不支持GPU加速,如果要使用GPU加速技术必须使用DRM,或者使用别人封装好的DRM库,比如openGL,Vulkan等

系统调用,打开文件

  • 使用<fcntl.h>头文件进行系统调用打开/dev/fb0
  • 使用<unistd.h>头文件进行系统调用关闭close(fb0);
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#define FD "/dev/fb0"
int main(void){
    int fb0 =open(FD,O_RDWR);
    if (fb0 == -1) {
        printf("打开 %s 失败\n",FD);
        return -1;
    }
    printf("打开 %s 成功\n",FD);
    close(fb0);
    return 0;
}
  • 使用 ioctl 获取屏幕参数(分辨率、BPP)
  • 使用<linux/fb.h>拿到基本信息
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fb.h>
#include <sys/ioctl.h>

int main(void)
{ 
    int fb0 = open("/dev/fb0",O_RDWR);
    if (fb0 == -1) {
        printf("打开失败\n");
        return -1;
    }
    // 2. 获取屏幕参数(分辨率、色深等)
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    ioctl(fb0, FBIOGET_VSCREENINFO, &vinfo);
    ioctl(fb0, FBIOGET_FSCREENINFO, &finfo);

    // 打印屏幕信息
    printf("分辨率: %d x %d\n", vinfo.xres, vinfo.yres);
    printf("色深: %d 位\n", vinfo.bits_per_pixel);

    close(fb0);
    return 0;
}
  • /usr/include/linux/fb.h中的内容
struct fb_var_screeninfo vinfo; //获取基本的可变显示信息,分辨率,色深等。
struct fb_fix_screeninfo finfo;
  • mmap

mmapmemory map 的缩写,它将内核中的一块内存(这里是 Framebuffer)映射到用户进程的地址空间,使得可以直接像访问普通数组一样读写这块内存,而不需要通过 read/write 系统调用。

参数详解:

	addr: 建议的映射起始地址。通常设为 NULL (或 0),让内核自动选择合适的地址。
	length: 映射区域的长度(字节)。
	prot: 保护标志,决定内存页面的访问权限:
	PROT_READ: 可读
	PROT_WRITE: 可写
	PROT_EXEC: 可执行
	PROT_NONE: 不可访问
	flags: 映射类型标志:
	MAP_SHARED: 共享映射。对内存的修改会写回文件/设备,其他映射该文件的进程可见。(Framebuffer 常用此模式)
	MAP_PRIVATE: 私有映射。对内存的修改不会写回文件,而是创建副本(Copy-on-Write)。
	fd: 文件描述符(通过 open 获得)。如果是匿名映射,设为 -1。
	offset: 文件中的偏移量,必须是页面大小(通常 4KB)的倍数。
返回值:
	成功:返回映射区域的起始地址指针。
	失败:返回 MAP_FAILED (即 (void *)-1)。
在 Framebuffer 中的作用: 它将 /dev/fb0 设备的显存直接映射到用户空间的指针 fb_buf。之后你读写 fb_buf 就像读写普通数组一样,但实际上是直接操作显卡显存。
#include <stdio.h>
#include <sys/fcntl.h>
#include <sys/unistd.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <string.h>

/**
 * @brief 映射关系
 * 
 * @return int 
 */
int main()
{ 
    int fd0 = open("/dev/fb0", O_RDWR);
    if(fd0 == -1)
    {
        printf("open fb0 error\n");
        return -1;
    }
    struct fb_var_screeninfo varinfo;
    ioctl(fd0, FBIOGET_VSCREENINFO, &varinfo);
    struct fb_fix_screeninfo finfo;
    ioctl(fd0, FBIOGET_FSCREENINFO, &finfo);

    //获取帧缓冲区内存大小 8bit的屏幕
    long screensize = varinfo.xres * varinfo.yres * varinfo.bits_per_pixel / 8;
    printf("screensize = %ld\n", screensize);//screensize = 4096000不到4mb

    //处理mmap
    char *fbp = mmap(NULL, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd0, 0);
    if(fbp == MAP_FAILED)
    {
        printf("mmap error\n");
        return -1;
    }
    printf("fbp = %p\n", fbp);
    printf("finfo.line_length = %d\n", finfo.line_length);
    
    //设置fbp开始到screensize的内存为0 用来画矩形
    memset(fbp, 0, screensize);

    //刷成蓝色
    for (int i = 0; i < screensize; i += 4) { // 32位色:4字节1像素
        fbp[i + 0] = 0xFF;  // 蓝色
        fbp[i + 1] = 0x00;  // 绿色
        fbp[i + 2] = 0x00;  // 红色
        // 透明度Alpha ARGB8888支持,RGB565不支持Alpha不能设置这个
        fbp[i + 3] = 0xFF;  //通常设为255表示不透明 
    }
    sleep(2);

    // 释放映射关系 fbp到screensize的内存
    munmap(fbp, screensize);
    close(fd0);
    
    return 0;
}
  • RGB565和ARGB8888

通过vinfo.bits_per_pixel拿到色深,16位为RGB565,32位为ARGB8888。可以类别为

ARGB8888为:

  • 16进制:0xAARRGGBB
  • 二进制:0bAAAAAAAA RRRRRRRR GGGGGGGG BBBBBBBB

RGB565为:

  • 二进制:0bRRRRR GGGGGG BBBBB

简化之前刷屏代码

// 刷成蓝色 (简化版)
uint32_t *fbp32 = (uint32_t *)fbp; // 将 char 指针转为 32位整数指针
long num_pixels = screensize / 4;  // 计算总像素数
    
// 0xFF0000FF 代表: Alpha=0xFF, Red=0x00, Green=0x00, Blue=0xFF (ARGB格式)
// 在小端序机器上,这会在内存中存储为: FF 00 00 FF (即 B=FF, G=00, R=00, A=FF)
uint32_t blue_color = 0xFF0000FF; 

for (long i = 0; i < num_pixels; i++) {
    fbp32[i] = blue_color;
}