Framebuffer是把内存中视频数据输出的设备驱动程序,Linux的framebuffer是独立的硬件抽象层,它可以把显示设备抽象为帧缓冲区,并把自己当作显示内存的一个映像,开发者可以通过这个映像映射到进程地址空间,直接进行读写操作。简单的说它把硬件抽象化后,可使上层不再关心硬件是如何操作,只是完成图像的显示功能,该驱动的设备文件一般是/dev/fb0、/dev/fb1,framebuffer最多支持32个设备。下图说明了Framebuffer在Linux系统的位置,它高于一般的驱动程序,在中间屏蔽下层驱动功能。
但是对程序员和Linux系统来是,framebuffer设备与其它的文件没有区别,可以通过配置对framebuffer设备文件完成对硬件的参数设置,framebuffer映射可以通过read()和write()进行数据的读取,或者通过mmap()函数将内部数据映射到应用程序空间,通过ioctl()进行其它操作或者设置其参数,用mmap()函数把内存中的图像数据直接映射到framebuffer并显示出来耗时短、效率高。内存中图像数据出来完后就可通过mmap()进行映射。
Framebuffer在Linux系统中的位置
图像显示过程
屏幕显示调用的是framebuffer,首先建立一个fb_dev结构体,里边有设备指针、内存空间指针、横竖像素数、颜色位数等信息:
struct fb_dev { int fb; void *fb_mem; int fb_width, fb_height, fb_line_len, fb_size; int fb_bpp; } fbdev; |
初始化framebuffer设备,并进行内存地址映射,可以直接把处理后的数据映射到framebuffer的缓存中进行显示:
int fb; if ((fb = open("/dev/fb0", O_RDWR)) < 0) { perror(__func__); return (-1); } fb_stat(fb); fbdev.fb_mem = mmap (NULL, fbdev.fb_size, PROT_READ|PROT_WRITE,MAP_SHARED,fb,0); fbdev.fb = fb; |
其中fb_stat(fb)函数如下定义,得到framebuffer的长、宽和位宽,成功则返回0,失败返回-1:
int fb_stat(int fd) { struct fb_fix_screeninfo fb_finfo; struct fb_var_screeninfo fb_vinfo; if (ioctl(fd, FBIOGET_FSCREENINFO, &fb_finfo)) { perror(__func__); return (-1); } if (ioctl(fd, FBIOGET_VSCREENINFO, &fb_vinfo)) { perror(__func__); return (-1); } fbdev.fb_width = fb_vinfo.xres; fbdev.fb_height = fb_vinfo.yres; fbdev.fb_bpp = fb_vinfo.bits_per_pixel; bdev.fb_line_len = fb_finfo.line_length; fbdev.fb_size = fb_finfo.smem_len; return (0); } |
在屏幕上显示数据,需要直接对像素点上直接显示像素颜色,因为原始解码的数据是24位RGB数据,而屏幕的颜色位数是16位,则需要将RGB888转换为RGB565数据:主要是对不用颜色位数进行移位。
代码如下
color=(unsigned short) (((buffer[x3s]<<8) & 0xF800)|((buffer[x3s+1]<<3) & 0x07E0)|((buffer[x3s+2] >> 3) & 0x001F)); |
因为开发板自带的NEC 3.5寸液晶屏分辨率是240×320,而摄像头输出的数据为320×240,故显示时需进行矩阵转置,由fbmem基址 + x * width + y确定:
if ((x > width) || (y > height) return (-1); unsigned short *dst = ((unsigned short *) fbmem + x * width + y); *dst = color; |
在程序最后,需要解除内存映射和关闭设备:
munmap(fbdev.fb_mem,fbdev.fb_size); close(fb); |