确保有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
mmap 是 memory 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;
}