目录
概念
使用
struct fb_fix_screeninfo{ }
struct fb_bitfield { }
struct fb_var_screeninfo{ }
例子1
例子2
例子3
jpeg库
步骤
概念
framebuffer 是一种很底层的机制,在 Linux 系统中,为了能够屏蔽 各种不同的显示设备的具体细节,Linux 内核提供的一个覆盖于显示芯片之上的虚拟层,将 显卡或者显存设备抽象掉,提供给一个统一干净又抽象的编程接口,使得内核可以很方便地 将显卡硬件抽象成一块可直接操作的内存,而且还提供了封装好的各种操作和设置,大大提 高内核开发的效率。因此 framebuffer 的存在是为了方便显卡驱动的编写,而有时我们会 将这个术语用在诸多涉及 Linux 视频输出的场合
在用户层层面,我们更加不用关心具体的显存位置、显卡型号、换页机制等等细节,而 是直接基于 frame-buffer 来映射显存,frame-buffer 就是所谓的帧缓冲机制
LCD 显示器一般对应的设备节点文件是/dev/fb0,当然如果系统有多个显示设备的话,还可能有/dev/fb1、/deb/fb2 等,这些文件是读写显示设备的入口
们可以 将 frame-buffer 所抽象的内核物理显存(如果机器没有显卡,那么就是系统分配的一段充 当显存的物理内存)映射到用户空间的虚拟内存上,这样一来,我们就可以在应用程序直接 写屏了
使用
要使用 frame-buffer,需要先理解以下的结构体,他们在/usr/inlucde/linux/fb.h 中被定义
struct fb_fix_screeninfo{ }
这个结构体保存显示设备不能被修改的信息,比如显存(或起到显存作用的内存)的起 始物理地址、扫描线尺寸、显卡加速器类别等
150 struct fb_fix_screeninfo {
151 char id[16];
152 unsigned long smem_start; // 显存起始地址(实际物理地址)
153
154 __u32 smem_len; /* 显存大小 */
155 __u32 type; /* 像素构成 */
156 __u32 type_aux; /* 交叉扫描方案 */
157 __u32 visual; /* 色彩构成 */
158 __u16 xpanstep; /* x 轴平移步长(若支持)*/
159 __u16 ypanstep; /* y 轴平移步长(若支持)*/
160 __u16 ywrapstep; /* y 轴循环步长(若支持)*/
161 __u32 line_length; /* 扫描线大小(字节)*/
162 unsigned long mmio_start; /* 缺省映射内存地址 */
163 /* (物理地址) */
164 __u32 mmio_len; /* 缺省映射内存大小 */
165 __u32 accel; /* 当前显示加速器芯片 */
166 __u16 reserved[3]; /* 保留 */
167 }
由驱动程序根据硬件配置决定的,应用程序无法修改,应用程序应该根据该 结构体提供的具体信息来构建和操作 frame-buffer 映射内存,比如扫描线的大小,即一行 的字节数。这个大小决定了映射内存的宽度
struct fb_bitfield { }
180 struct fb_bitfield {
181 __u32 offset; /* 色彩位域偏移量 */
182 __u32 length; /* 色彩位域长度 */
183 __u32 msb_right;
184 };
struct fb_var_screeninfo{ }
体保存显示设备可以被调整的信息,比如可见显示区 X/Y 轴分辨率、虚拟显 示区 X/Y 轴分辨率、色彩深度、色彩构成等等
232
233 struct fb_var_screeninfo {
234 __u32 xres; /* 可见区的宽度分辨率 */
235 __u32 yres; /* 可见区的高度分辨率 */
236 __u32 xres_virtual; /* 虚拟区的宽度分辨率 */
237 __u32 yres_virtual; /* 虚拟区的高度分辨率 */
238 __u32 xoffset; /* 虚拟区到可见区的宽度偏移量 */
239 __u32 yoffset; /* 虚拟区到可见区的高度偏移量 */
240
241 __u32 bits_per_pixel; /* 色彩深度 */
242 __u32 grayscale; /* 灰阶(若为非 0) */
243
244 struct fb_bitfield red; /* 红色色彩位域构成 */
245 struct fb_bitfield green; /* 绿色色彩位域构成 */
246 struct fb_bitfield blue; /* 蓝色色彩位域构成 */
247 struct fb_bitfield transp; /* 透明属性 */
248
249 __u32 nonstd; /* 非标准像素格式(若为非 0) */
250
251 __u32 activate; /* 设置参数合适生效 */
252
253 __u32 height; /* 图片高度(单位毫米) */
254 __u32 width; /* 图片宽度(单位毫米) */
255
256 __u32 accel_flags; /* 显示卡选项 */
257
.....
各种分辨率和 X 轴和 Y 轴偏移量,他们的关系决定了 LCD 显示器 上显示的效果
例子1
以群创 AT070TN92-7 英寸液晶显示屏为例,写一个测试代码,将 LCD 的具体细节显 示出来,并且将一张图片显示在可见区,然后调整 yoffset 改变显示效果
1 #include <stdio.h>
2 #include <signal.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <Linux/fb.h>
7
8 #include <fcntl.h>
9 #include <sys/types.h>
10 #include <sys/mman.h>
11 #include <sys/ioctl.h>
12 //显示有关帧缓冲设备的信息
13 void show_fix_screeninfo(struct fb_fix_screeninfo *p) //固定属性
14 {
15 printf("=== FIX SCREEN INFO === \n");
16
17 printf("\tid: %s\n", p->id);
18 printf("\tsmem_start: %#x\n", p->smem_start);
19 printf("\tsmem_len: %u bytes\n", p->smem_len);
20
21 printf("\ttype:");
22 switch(p->type)
23 {
24 case FB_TYPE_PACKED_PIXELS:
25 printf("PACKED_PIXELS\n");break;
26 case FB_TYPE_PLANES:
27 printf("PLANES\n");break;
28 case FB_TYPE_INTERLEAVED_PLANES:
29 printf("INTERLEAVED_PLANES\n");break;
30 case FB_TYPE_TEXT:
31 printf("TEXT\n");break;
32 case FB_TYPE_VGA_PLANES:
33 printf("VGA_PLANES\n");break;
34 }
35
36 printf("\tvisual:");
37 switch(p->visual)
38 {
39 case FB_VISUAL_MONO01:
40 printf("MONO01\n");break;
41 case FB_VISUAL_MONO10:
42 printf("MONO10\n");break;
43 case FB_VISUAL_TRUECOLOR:
44 printf("TRUECOLOR\n");break;
45 case FB_VISUAL_PSEUDOCOLOR:
46 printf("PSEUDOCOLOR\n");break;
47 case FB_VISUAL_DIRECTCOLOR:
48 printf("DIRECTCOLOR\n");break;
49 case FB_VISUAL_STATIC_PSEUDOCOLOR:
50 printf("STATIC_PSEUDOCOLOR\n");break;
51 }
52
53 printf("\txpanstep: %u\n", p->xpanstep);
54 printf("\typanstep: %u\n", p->ypanstep);
55 printf("\tywrapstep: %u\n", p->ywrapstep);
56 printf("\tline_len: %u bytes\n", p->line_length);
57
58 printf("\tmmio_start: %#x\n", p->mmio_start);
59 printf("\tmmio_len: %u bytes\n", p->mmio_len);
60
61 printf("\taccel: ");
62 switch(p->accel)
63 {
64 case FB_ACCEL_NONE: printf("none\n"); break;
65 default: printf("unkown\n");
66 }
67
68 printf("\n");
69 }
70 //显示帧缓冲设备的可变属性信息 打印了诸如水平同步长度、垂直同步长度、视频模式、可见屏幕大小、虚拟屏幕大小、每像素位数、激活状态、偏移量以及颜色位字段等属性
71 void show_var_screeninfo(struct fb_var_screeninfo *p) // 可变属性
72 {
73 printf("=== VAR SCREEN INFO === \n");
74
75 printf("\thsync_len: %u\n", p->hsync_len);
76 printf("\tvsync_len: %u\n", p->vsync_len);
77 printf("\tvmode: %u\n", p->vmode);
78
79 printf("\tvisible screen size: %ux%u\n",
80 p->xres, p->yres);
81 printf("\tvirtual screen size: %ux%u\n\n",
82 p->xres_virtual, 83 p->yres_virtual);
84
85 printf("\tbits per pixel: %u\n", p->bits_per_pixel);
86 printf("\tactivate: %u\n\n", p->activate);
87
88 printf("\txoffset: %d\n", p->xoffset);
89 printf("\tyoffset: %d\n", p->yoffset);
90
91 printf("\tcolor bit-fields:\n");
92 printf("\tR: [%u:%u]\n", p->red.offset,
93 p->red.offset+p->red.length-1);
94 printf("\tG: [%u:%u]\n", p->green.offset,
95 p->green.offset+p->green.length-1);
96 printf("\tB: [%u:%u]\n\n", p->blue.offset,
97 p->blue.offset+p->blue.length-1);
98
99 printf("\n");
100 }
101
102 int main(void)
103 {
104 int lcd = open("/dev/fb0", O_RDWR|O_EXCL);//打开帧缓冲设备 /dev/fb0
105 if(lcd == -1)
106 {
107 perror("open()");
108 exit(1);
109 }
110
111 struct fb_fix_screeninfo finfo; // 显卡设备的固定属性结构体
112 struct fb_var_screeninfo vinfo; // 显卡设备的可变属性结构体
113
114 ioctl(lcd, FBIOGET_FSCREENINFO, &finfo); // 获取帧缓冲设备的固定属性
115 ioctl(lcd, FBIOGET_VSCREENINFO, &vinfo); // 获取帧缓冲设备的可变属性
116
117 show_fix_screeninfo(&finfo); // 打印相应的属性
118 show_var_screeninfo(&vinfo); // 打印相应的属性
119
120 // 将显示设备的具体信息保存起来,方便使用
121 unsigned long WIDTH = vinfo.xres;//屏幕的宽度
122 unsigned long HEIGHT = vinfo.yres;//高度
123 unsigned long VWIDTH = vinfo.xres_virtual;//虚拟宽度
124 unsigned long VHEIGHT = vinfo.yres_virtual;//虚拟高度
125 unsigned long BPP = vinfo.bits_per_pixel;//每像素位数
126
127 char *p = mmap(NULL, VWIDTH * VHEIGHT * BPP/8,
128 PROT_READ|PROT_WRITE,
129 MAP_SHARED, lcd, 0); // 申请一块虚拟区映射内存,并将图片数据加载到映射区
130
131 int image = open("images/girl.bin", O_RDWR);//打开一个二进制图像文件
132 int image_size = lseek(image, 0L, SEEK_END);//将文件指针移动到文件末尾,获取文件的大小
133 lseek(image, 0L, SEEK_SET);//将文件指针重新移动到文件的开头
134 read(image, p, image_size); // 获取图片数据并将之刷到映射内存
135
136
137 vinfo.xoffset = 0;
138 vinfo.yoffset = 0;
139 if(ioctl(lcd, FB_ACTIVATE_NOW, &vinfo)) // 偏移量均置位为 0,激活显示设备
140 {
141 perror("ioctl()");
142 }
143 ioctl(lcd, FBIOPAN_DISPLAY, &vinfo); // 配置属性并扫描显示,配置属性并扫描显示,将图片显示在屏幕上
144
145 sleep(1);
146 // Y 轴偏移量调整为 100 像素,并重新配置属性并扫描显示
147 vinfo.xoffset = 0;
148 vinfo.yoffset = 100; // 1 秒钟之后将 Y 轴偏移量调整为 100 像素
149 if(ioctl(lcd, FB_ACTIVATE_NOW, &vinfo))
150 {
151 perror("ioctl()");
152 }
153 show_var_screeninfo(&vinfo);
154 ioctl(lcd, FBIOPAN_DISPLAY, &vinfo); // 重新配置属性并扫描显示
155
156 return 0;
157 }
例子2
展示了 LCD 每隔 1 秒显示一种单色,用来检测 LCD 屏幕有没有坏点:
1 #include <stdio.h>
2 #include <signal.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <Linux/fb.h>
7
8 #include <fcntl.h>
9 #include <sys/types.h>
10 #include <sys/mman.h>
11 #include <sys/ioctl.h>
12
13 enum color{red, green, blue};
14
15 // 根据 fb_var_screeninfo 的色彩构成生成一个颜色像素数据
16 unsigned long *create_pixel(struct fb_var_screeninfo *pinfo,
17 enum color c)
18 {
19 unsigned long *pixel = calloc(1, pinfo->bits_per_pixel/8);//存储像素数据分配了内存
20 unsigned long *mask = calloc(1, pinfo->bits_per_pixel/8);//掩码分配了内存
21 *mask |= 0x1;//将掩码的最低位设置为 1 掩码用于确定要设置的像素位
22
23 int i;
24 switch(c)
25 {
26 case red://确定红色分量在像素数据中的偏移量和长度,并相应地生成掩码
27 for(i=0; i<pinfo->red.length-1; i++)
28 {
29 *mask <<= 1;
30 *mask |= 0x1;
31 }
32 *pixel |= *mask << pinfo->red.offset;
33 break;
34 case green://确定绿色分量在像素数据中的偏移量和长度,并相应地生成掩码
35 for(i=0; i<pinfo->green.length-1; i++)
36 {
37 *mask <<= 1;
38 *mask |= 0x1;
39 }
40 *pixel |= *mask << pinfo->green.offset;
41 break;
42 case blue://确定蓝色分量在像素数据中的偏移量和长度,并相应地生成掩码
43 for(i=0; i<pinfo->blue.length-1; i++)
44 {
45 *mask <<= 1;
46 *mask |= 0x1;
47 }
48 *pixel |= *mask << pinfo->blue.offset;
49 }
50
51 return pixel;
52 }
53
54 int main(void)
55 {
56 int lcd = open("/dev/fb0", O_RDWR);//打开帧缓冲设备 /dev/fb0
57 if(lcd == -1)
58 {
59 perror("open(\"/dev/fb0\")");
60 exit(1);
61 }
62
63 // 获取显示设备相关信息
64 struct fb_fix_screeninfo finfo;//固定信息
65 struct fb_var_screeninfo vinfo;//可变信息
66 ioctl(lcd, FBIOGET_FSCREENINFO, &finfo);
67 ioctl(lcd, FBIOGET_VSCREENINFO, &vinfo);
68
69 // 初始化可见区偏移量
70 vinfo.xoffset = 0;
71 vinfo.yoffset = 0;
72 ioctl(lcd, FBIOPAN_DISPLAY, &vinfo);
73 //计算每个像素所占的位数
74 unsigned long bpp = vinfo.bits_per_pixel;
75
76 // 创建三原色像素点 创建红色、绿色和蓝色的像素点数据
77 unsigned long *pixel[3] = {0};
78 pixel[0] = create_pixel(&vinfo, red);
79 pixel[1] = create_pixel(&vinfo, green);
80 pixel[2] = create_pixel(&vinfo, blue);
81
82 // 申请一块对应 LCD 设备的映射内存
83 char *FB = mmap(NULL, vinfo.xres * vinfo.yres * bpp/8,
84 PROT_READ | PROT_WRITE, MAP_SHARED, 85 lcd, 0);
86 int k;
87 for(k=0; ;k++)
88 {
89 int i;//每个像素并将相应颜色的像素数据复制到映射内存中
90 for(i=0; i<vinfo.xres * vinfo.yres; i++)
91 {
92 memcpy(FB+i*bpp/8, pixel[k%3], bpp/8);
93 }
94 sleep(1); // 每隔一秒刷一次屏
95 }
96
97 return 0;
98 }
例子3
在 LCD 上画图
先获取到一个图片的像素数据,生成一个二进制图片像素数据的*.bin文件,扫描模式选择水平扫描,灰度旋转32位真色彩,一个像素32位数据来表示,宽度高度分别是800和500
将这个bin文件读到内存中,然后将它刷到LCD所对应的FRAME-BUFFER上, 就实现了LCD显示图像了
pic_show.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<stdbool.h>
4 #include<unistd.h>
5 #include<string.h>
6 #include<strings.h>
7 #include<errno.h>
8
9 #include<sys/stat.h>
10#include<sys/types.h>
11#include<sys/mman.h>
12#include<fcntl.h>
13
14#defineSCREEN_SIZE800*480 //群创AT070TN92显示屏像素总数
15#defineWIDTH 800
16#defineHEIGHT480
17
18 voidwrite_lcd(char*p, intpicfd)
19 {
20 memset(p,0,SCREEN_SIZE*4);
21
22 intn,offset=0;
23 while(1)//这个循环是以防万一不能一次将图片全部读出
24 {
25 n=read(picfd,p+offset,SCREEN_SIZE*4);
26 if(n<=0)
27 break;
28 offset+=n;
29 }
30 }
31
32 intmain(void)
33 {
34 int lcd=open("/dev/fb0",O_RDWR);//打开LCD设备节点文件
35 if(lcd==-1)
36 {
37 perror("open()");
38 exit(1);
39 }
40
41 //将一块适当大小的内存映射为LCD设备的frame-buffer
42 char*p=mmap(NULL,SCREEN_SIZE*4,
43 PROT_READ|PROT_WRITE,
44 MAP_SHARED, lcd,0);
45
46 intpicfd=open("girl.bin",O_RDONLY); //打开图片文件
47 if(picfd==-1)
48 {
49 perror("open()");
50 exit(1);
51 }
52 write_lcd(p,picfd);//将文件刷进LCD设备对应的frame-buffer中
53
54 return0;
55 }
jpeg库
如果想直接对jpeg图片进行读取就需要用到这个库,要在代码中加入对jpeg压缩格式的解码库API
步骤
- 下载jpeg库
- 安装jpeg库
- 进入解压后的目录 cd jpeg-9a/
- 配置交叉环境 ./configure --host=arm-none-Linux-gnueabi
- 编译并安装 make && make install
- 将/usr/local/include和/usr/local/lib下关于jpeg的头文件和库拷贝到开发板
- 在程序中加入对jpeg压缩图片的解压代码
- main函数
- 读取图片属性
- 根据其大小分配内存缓冲区jpg_buffer
- 声明解压缩结构体,以及错误管理结构体
- 使用缺省的出错处理来初始化解压缩结构体
- 配置该cinfo,使其从jpg_buffer 中读取jpg_size个字节
- 读取jpeg文件的头,并判断其格式是否合法
- 开始解压
- 根据图片的尺寸大小分配一块相应的内存bmp_buffer
- 用来存放从jpg_buffer解压出来的图像数据
- 循环地将图片的每一行读出并解压到bmp_buffer中
- 解压完了,将jpeg相关的资源释放掉
- 打开屏幕文件
- 获取LCD设备的当前参数
- 根据当前LCD设备参数申请适当大小的FRAMEBUFFR
- 将bmp_buffer中的RGB图像数据,写入FRAMEBUFFER中