framebuffer框架与示例
Framebuffer(帧缓冲)是计算机图形系统中用于存储图像数据的一块内存区域,这些区域直接对应屏幕上的每个像素。当图形系统需要更新屏幕显示时,会从帧缓冲中读取数据并传输到显示设备。
基本概念:
1.物理帧缓冲:直接连接到显示设备的内存区域,修改其中的数据会立即反映在屏幕上。
2.虚拟帧缓冲:在内存中创建的临时缓冲区,可用于离屏渲染(后台图形处理),之后再将结果复制到物理帧缓冲。
3.像素格式:帧缓冲中的数据通常按特点格式组织,如 RGB(红、绿、蓝)或 RGBA(包含透明度),每个颜色分量占用 1 字节(8位)。
工作原理:
1.直接映射:帧缓冲中的每个内存位置对应屏幕上的一个像素,其值决定了像素的颜色。
2.双缓冲技术:为避免屏幕闪烁,系统通常使用两个缓冲区(前缓冲和后缓冲)。渲染时先更新后缓冲,完成后再与前缓冲交换。
Framebuffer 设备文件:
在 linux 系统中,Framebuffer 设备通常映射为 /dev/fbx (x为数字,通常为 0)。s3c2440 平台上,默认设备为 /dev/fb0 ,通过操作该文件,可实现对屏幕的读写。
Framebuffer 编程接口:
1.open():打开设备文件。
2.ioctl():获取 / 设置显示参数(分辨率、位深等)。
3.mmap():将设备内存映射到用户空间。
4.write()/ read():直接读写设备文件(极少)。
应用层与内核层交互的方式:
1.系统调用
2.copy_to_user copy_from_user
3.信号
应用层向内核层的系统调用(传统字符设备驱动的流程)
流程:
应用层操作设备文件------》设备文件对应有一个主次设备号-----》根据主次设备号可以找到 cdev 结构体-----》这个结构体里有 file_operations 结构体-----》重写open、read、write、release 函数接口。
弊端:
当数据量特别大的时候,比如屏幕显示,有很多的像素点,每个像素点又有红绿蓝三种颜色,每一秒可能还要显示20帧的画面。由此,Framebuffer 就是一种针对这样的问题的一个解决方案。
解决方案:
Framebuffer 就是linux系统中为显示设备提供的一种框架,在内核层有帧缓冲区。
主要是为了解决数据频繁的搬移而引起的系统资源开销大的问题。
所以应用层就不用read、write函数了,主要用 mmap 进行内存映射。
一般来说,应用层是无法直接操作内核层的,但是通过 mmap ,我们就可以操作内核层的帧缓冲区域。
那么,再使用 Framebuffer 之前,需要做哪些准备呢?
Framebuffer 驱动架构
首先,需要让硬件设备适配 Framebuffer 驱动架构,之前注册混杂设备是用 misc_register,那么这个就主要是 Framebuffer_register 。也就是写一个硬件屏幕驱动。
但无论是硬件屏幕驱动还是Framebuffer驱动架构,都是比较复杂的,而友善之臂也已经写好了这些驱动,我们只需要学习如何调用即可,Framebuffer 在应用层是有一个标准的框架的。
linux 提供的 Framebuffer 应用层接口操作流程:
1.打开设备,通常是 /dev/fb0。
2.向驱动发送获取屏幕信息的命令(宽度、高度、格式(是RGB 565还是RGB 888))。RGB 565对应 5 位红、6 位绿、5 位蓝。共 16 个像素点,对应 2 个字节。
3.用mmap申请显存空间(宽度 * 高度 * 一个像素点所占字节数)
4.向对应的位置写入 RGB 信息,然后封装绘点、绘线、绘面、绘图、绘汉字等相关功能。
具体操作:
首先根据显示设备的不同,需要进行不同的设置。
比如我所选用的显示设备是 td35的,那么就
cp config_mini2440_td35 .config
接着
make menuconfig
直接退出再保存一下即可。
然后
make UImage
重新生成一下 UImage。
这样子,linux 系统就可以支持屏幕驱动了。
具体步骤:
1.驱动友善之臂已经写好了。
2.将内核目录顶层的所用的屏幕设备对应的 config (这里面就包含了这个屏幕的驱动)替换成 .config 。
3.make menuconfig(根据 .config 来生成对应的配置)
4.make UImage 生成带有屏幕驱动的内核。
5.将内核文件拷贝到 ~/tftpboot 目录下,因为每次内核都是重新下载的,所以就可以开机启动 uboot 下载 ~/tftpboot 目录下的 UImage。
6.现在该 linux 系统就成功带有屏幕驱动了。
启动之后:
启动完之后,出现一个小企鹅,这就是 linux 系统的一个标志。有时候会出现一个 QT pi的软件界面,需要手动关掉。
在开发板端输入:
vi /etc/init.d/rcS
找到 /bin/qtopia &,把这个用 # 注释掉,然后wq保存退出即可,不然会显示乱码:
OK,现在可以测试一下是否正常显示画面,我的这个显示设备是 240 x 320 的。首先得拷贝一下字库,不然显示不出来。
cp ziku2_w32_h32 ~/nfs/roots -rf
在 main.c 文件中,可以用以下命令:
draw_clear(0x00000000); //清屏
draw_point(100,100,0xffff); //在100x100的位置画个点
draw_x_line(5,5,5,100,0xffff) //画条线
运行完后,发现显示的太大了,需要重新取一个字库。
打开易木雨,点阵字库生成器,
1.编码选择:UNICODE
2.宋体、12.
3.高16、宽16
4.字库格式选择:DZK
5.创建
6.chmod +x ziku
7.chmod 0777 ziku
7.cp ziku ~/nfs/rootfs
8.回到内核层,./a.out 即可显示。
显示 adxl345 加速度传感器数据:
同理,可以将传感器所采集到的数据传到显示屏幕上了,如显示 adxl345 加速度传感器数据:
typedef struct {short x, y, z;
} adxl345_data_t;// 简单的文本绘制函数
void draw_text(char *text, int x, int y, unsigned int color, unsigned char *fbmem, struct fb_var_screeninfo vinfo)
{int i, j;for (i = 0; text[i] != '\0'; i++) {for (j = 0; j < 8; j++) {if (text[i] & (1 << j)) {int index = ((y * vinfo.xres) + (x + i * 8 + j)) * (vinfo.bits_per_pixel / 8);if (index < vinfo.xres * vinfo.yres * (vinfo.bits_per_pixel / 8)) {*((unsigned int *)(fbmem + index)) = color;}}}}
}int main(void)
{int fd_adxl345 = 0, fd_fb = 0;adxl345_data_t data;unsigned char buf[6] = {0};struct fb_var_screeninfo vinfo;struct fb_fix_screeninfo finfo;unsigned char *fbmem;long int screensize = 0;// 打开ADXL345设备fd_adxl345 = open("/dev/adxl345", O_RDWR);if (fd_adxl345 == -1) {perror("fail to open adxl345");return -1;}// 打开Framebuffer设备fd_fb = open("/dev/fb0", O_RDWR);if (fd_fb == -1) {perror("fail to open framebuffer");close(fd_adxl345);return -1;}// 获取Framebuffer的可变信息if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &vinfo) == -1) {perror("Error reading variable information");close(fd_adxl345);close(fd_fb);return -1;}// 获取Framebuffer的固定信息if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &finfo) == -1) {perror("Error reading fixed information");close(fd_adxl345);close(fd_fb);return -1;}// 计算Framebuffer的大小screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;// 映射Framebuffer到用户空间fbmem = (unsigned char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);if ((int)fbmem == -1) {perror("Error mapping framebuffer");close(fd_adxl345);close(fd_fb);return -1;}while (1) {// 读取ADXL345传感器数据read(fd_adxl345, buf, sizeof(buf));data.x = (buf[1] << 8 | buf[0]);data.y = (buf[3] << 8 | buf[2]);data.z = (buf[5] << 8 | buf[4]);float ax = data.x * 0.00390625f;float ay = data.y * 0.00390625f;float az = data.z * 0.00390625f;// 清屏for (int i = 0; i < screensize; i++) {fbmem[i] = 0;}// 绘制加速度值到屏幕char text[100];sprintf(text, "Acceleration: x=%.3f g, y=%.3f g, z=%.3f g", ax, ay, az);draw_text(text, 10, 10, 0xFFFFFF, fbmem, vinfo);sleep(1);}// 解除映射munmap(fbmem, screensize);// 关闭设备close(fd_adxl345);close(fd_fb);return 0;
}