1. 什么是Framebuffer?
Framebuffer 字面意思就是帧缓存的意思,即显存,里面保存着一帧图像。事实上,对于嵌入式系统而言。没有真正意义上的显存,Framebuffer 是通过内存模拟出来的。
LCD FrameBuffer 里的若干字节表示(具体根据驱动适配),LCD 屏幕上的一个像素点。
- RGB888:32bpp,占4字节,分别是A8、R8、G8、B8,一般只用其中低24位,高8位表示透明度。
- RGB565:16bpp,占2字节,分别是R5、G6、B5,比较常用的一种颜色
- RGB555:很少用。
假设LCD屏幕分辨率是800x600,每个像素占4字节,那么framebuffer 大小就是:
800 x 600 x 4 = 960000 字节
framebuffer 显示原理如下:
假设需要设置 LCD 中坐标(x,y)处像素的颜色,首要要找到这个像素对应的内存,然后根据它的 BPP 值设置颜色。假设 fb_base 是 APP 执行 mmap 后得到的 Framebuffer 地址,如下图所示:
(x,y)像素起始地址=fb_base+(xres*bpp/8)y + xbpp/8
2. 为什么要有Frambuffer?
思考一个问题,为什么要用Framebuffer?
如图为LCD 驱动框架图:
从软件层面分析:framebuffer 起着承上启下的作用,向上,为应用层提供通用系统调用(open(),ioctl(),mmap());向下,联接LCD控制器,之前对硬件进行操作。
从硬件层面分析:用户只需要将数据写到framebuffer,硬件会自动刷新到屏幕上。
3. 常用接口和数据结构
3.1 常用接口
- 打开设备:open()系统调用。
通过 man 2 查看如下
函数说明:
- ioctl 系统调用:
函数原型:
int ioctl(int fd, unsigned long request, ...);
函数说明:
- fd :表示文件描述符;
- request:表示与驱动程序交互的命令,用不同的命令控制驱动程序输出我们需要的数据;
- … :表示可变参数arg,根据request命令,设备驱动程序返回输出的数据。
- 返回值:打开成功返回文件描述符,失败将返回-1。
3)mmap 系统调用:
函数说明:
3.2 相关数据结构
fb_var_screeninfo:包含xres, yres, bits_per_pixel等信息,在后续会经常用到。
4. 如何在LCD 上描点?
4.1 LCD 显示原理
当我们需要显示一个字母‘A’时,是通过判断点阵的每一个位数值状态,来填充颜色,达到显示字符效果。其中‘1’表示一种颜色,‘0’表示填充另一种颜色。
如下图[1]8*16的点阵,只要有这个点阵,我们就可以在LCD上面描点,达到显示字符的效果。
4.2 Framebuffer 操作说明
framebuffer 操作如下流程:
- 打开设备(open)
- 获取屏幕参数信息(ioctl)
- 分配显存(mmap)
- 描点/写数据
- 释放资源(unmmap)
- 关闭设备(close)
int fd_fb; struct fb_var_screeninfo var; /* Current var */ int screen_size; unsigned char *fbmem; unsigned int line_width; unsigned int pixel_width; int main(int argc, char *argv[]) { /*Step1: 打开设备 */ fd_fb = open("/dev/fb0", O_RDWR); if (fd_fb < 0) { printf("can't open /dev/fb0\n"); return -1; } /*Step2:获取设备参数信息 * xres:x 方向总像素 * yres:y 方向总像素 * bits_per_pixel:每个像素占多少位 */ if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) { printf("can't get var\n"); return -1; } /* Step3: 计算线宽,分配显存 */ /* line_width 每行占的字节 */ line_width = var.xres * var.bits_per_pixel / 8; pixel_width = var.bits_per_pixel / 8; screen_size = var.xres * var.yres * var.bits_per_pixel / 8; fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); if (fbmem == (unsigned char *)-1) { printf("can't mmap\n"); return -1; } /* Step4: 清屏: 全部设为黑色 */ memset(fbmem, 0, screen_size); /* Step5: 描点 */ lcd_put_ascii(var.xres/2, var.yres/2, 'A'); /*在屏幕中间显示8*16的字母A*/ /* Step6:释放资源*/ munmap(fbmem , screen_size); /* Step7:关闭设备 */ close(fd_fb); return 0; }
4.3 描点实现
描点的关键是计算点(x,y)位置对应的地址,然后直接指向fbmem即可向frambuffer 写入数据。
/********************************************************************** * 函数名称:lcd_put_pixel * 功能描述: 在LCD指定位置上输出指定颜色(描点) * 输入参数:x坐标,y坐标,颜色 * 输出参数: 无 * 返 回 值: 会 ***********************************************************************/ void lcd_put_pixel(int x, int y, unsigned int color) { /* * 最主要的就是fbmem * 计算(x,y)位置的偏移,然后指向fbmem,这块直接映射到framebuffer 内存里 */ unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width; unsigned short *pen_16; unsigned int *pen_32; unsigned int red, green, blue; pen_16 = (unsigned short *)pen_8; pen_32 = (unsigned int *)pen_8; switch (var.bits_per_pixel) { /* 8bpp*/ case 8: { *pen_8 = color; break; } /*16 bpp */ case 16: { /* 565 */ red = (color >> 16) & 0xff; green = (color >> 8) & 0xff; blue = (color >> 0) & 0xff; color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); *pen_16 = color; break; } case 32: { *pen_32 = color; break; } default: { printf("can't surport %dbpp\n", var.bits_per_pixel); break; } } }
4.4 向 LCD 写入 英文
写入英文的前提:
- 描点函数已实现;
- 具备该英文字符的点阵数据;
/********************************************************************** * 函数名称:lcd_put_ascii * 功能描述: 在LCD指定位置上显示一个8*16的字符 * 输入参数:x坐标,y坐标,ascii码 * 输出参数: 无 * 返 回 值: 无 ***********************************************************************/ void lcd_put_ascii(int x, int y, unsigned char c) { /* fontdata_8x16 8x16 英文点阵数据 */ unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16]; int i, b; unsigned char byte; /* 8 x 16 的点阵, 16行 8列*/ for (i = 0; i < 16; i++) { byte = dots[i]; for (b = 7; b >= 0; b--) { if (byte & (1<<b)) { /* show */ lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */ } else { /* hide */ lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */ } } } }
参考资料
[1]
如下图: 《嵌入式linux应用开发完全手册.韦东山》