linux驱动由浅入深系列:显示子系统之一(通过FrameBuffer在屏幕上画出图形)

时间:2021-10-19 23:34:33

显示子系统对应用层提供的接口叫做framebuffer,一般位于/dev/fb0(下文示例运行于adroid的平台位于/dev/graphics/fb0,不过它们都是一样的),它为上层提供了统一的对显卡的描述。首先要明确的是lcd显示子系统虽然复杂,但其任然是基本的字符设备,fb0就是其设备节点,主设备号29。不同之处在于,可以通过mmap(mmap将一个文件或者其它对象映射进内存。)对其进行地址映射,将内核中的显存空间直接映射到用户空间,这样用户空间填入需要显示的数据就能直接显示在lcd上。其余的参数查询与设置通过ioctl都可以完成。

 

我们先从第一个应用层测试开始讲起:

1,  保持屏幕上有一帧画面,进入fb0对应的目录(/dev/ 或/dev/graphics/)执行:

cat fb0 > fb_test

这样就可以对framebuffer的一帧原始数据进行暂存

2,  切换一帧屏幕画面,执行

cat fb_test > fb0

可以看到之前暂存的一帧画面重新出现了。adroid有自身的刷新频率,所以我们刷入的一帧画面会很快被覆盖,看到的现象可能是一闪即逝。

 

从上面的测试我们理解了fb0的基本作用,下面我们写一个测试程序画一个自己的图形:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>

int main()
{
int fbfd=0;//framebuffer 文件句柄
struct fb_var_screeninfo vinfo;
unsigned long screen_size=0;
char *fbp=0;//framebuffer 在用户空间映射的虚拟地址指针
int x=0,y=0,i=0;

fbfd=open("/dev/graphics/fb0",O_RDWR); //打开framebuffer 文件节点
if(!fbfd){
printf("error\n");
exit(1);
}

if(ioctl(fbfd,FBIOGET_VSCREENINFO,&vinfo)){ //获取屏幕可变参数
printf("error\n");
exit(1);
}

//打印屏幕可变参数:x轴像素个数,y轴像素个数,每个像素点数
printf("%dx%d,%dbpp\n",vinfo.xres,vinfo.yres,vinfo.bits_per_pixel);
screen_size=vinfo.xres*vinfo.yres*12; //framebuffer大小
fbp=(char *)mmap(0,screen_size,PROT_READ|PROT_WRITE,MAP_SHARED,fbfd,0);//映射
if((int)fbp==-1){
printf("error\n");
exit(4);
}

for(i = 0; i < screen_size/2; i++)
{
unsigned short rgb;
rgb=(31<<11)|(0<<5)|0;
*((unsigned short *)(fbp+i*2))=rgb;
}

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


分析:

上述程序比较简洁,先打开了fb0节点,获取了相关参数,然后进行framebuffer地址映射。之后直接向framebuffer中写入数据,画面就会出现在屏幕上。

其中framebuffer的大小是从驱动程序中获知的,下一篇文章将会就驱动层展开分析。

framebuffer中数据格式有rgb565、rgb888,上面示例为rgb565。运行后全屏为红色。