framebuffer
1.framebuffer的理解
概念:FrameBuffer,可以译作"帧缓冲",有时简称为 fbdrv,这是一种独立于硬件的抽象图形设备,是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。面向文件描述符。
1.分辨率:横向的像素点和纵向的像素点构成的,像素点越多图片越高清
2.显存(不允许用户操作):用来存储显示屏刷新出来的数据,数模转换到显示屏(显卡的控制电路完成)
3.用户空间:将显存中一段没有用到的空间经过内存映射到用户的空间,此时在用户空间写入东西就可以映射到显存中(MMU)
4.RGB:颜色值,红绿蓝三原色,可以调节颜色比例获得需要的颜色
RGB888:占4字节 (0x00 FF 00 00 32bit 红色)
RGB565:占2字节
RGB555:不常见
显示屏:像素点先由左至右,再由上至下排列
核心思路:通过在用户空间写入东西(RGB颜色值)映射在显存中,显卡再将显存的内容转换到显示屏上,完成像素点的绘制。
过程:想在在显示屏上的(x,y)点写入红色的颜色值。通过用户空间首地址pmem偏移,偏移的字节数由数据类型来定(pmem+y*800+x)。将红色的RGB值写入偏移完后的位置,通过内存映射到显存中,写入的位置为(y*800+x 先从左到右,再从上到下)
2.代码实现:
打开显示设备(/dev/fb0)-> 获取显示设备相关参数(ioctl),如分辨率,像素深度 -> 建立内存映射mmap,获得用户空间的首地址 -> 直接写入颜色值 -> 清屏操作,解除内存映射 -> 关闭显示设备
需要用到的函数:
1.#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
ioctl(fd, FBIOGET_VSCREENINFO, &vinfo); //获取屏幕信息(可变)
<linux/fb.h>中还提供了专门的结构体类型,用来存放参数,如下就是存放可变参数的结构体类型
struct fb_var_screeninfo {
__u32 xres; /* visible resolution 可视画面的x、y轴分辨率 */
__u32 yres;
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible 可视画面相对于虚拟画面的x、y轴偏移量*/
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what 像素深度 */
__u32 grayscale; /* != 0 Graylevels instead of colors */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
#include <sys/mman.h>
2.void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
功能:内存映射
参数:
addr:欲映射内存的起始地址,NULL表示系统自动选定,映射
成功后返回该地址
length:映射内存大小
prot:映射区域的保护方式(读/写/执行/不能存取)
flags:影响区域映射的各种特性
MAP_SHARED:对映射区域写入的数据会复制回源文件内,
而且允许其他映射该文件的进程共享
MAP_PRIVATE:对映射区域的写入操作会产生一个映射文件的复制,
即对此区域的任何修改都不会写回原来的文件内
fd:要映射的目标文件
offset:文件映射的偏移量(0代表从开头开始)
返回值:
void * :返回映射欲映射内存的首地址
eg:
unsigned int* addr = mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
3.int munmap(void *addr, size_t length);
功能:解除内存映射
参数:
addr:映射内存的地址
length:映射内存大小
代码:
/*初始化显示设备*/
int init_fb(const char *fbname)
{//1.打开显示设备int fd = open(fbname, O_RDWR); if (-1 == fd){perror("fail open fb");return -1;}struct fb_var_screeninfo vinf; //虚拟屏幕的信息//2.获取显示设备(虚拟屏幕)的相关参数:分辨率和像素深度int ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinf); //获取信息if (ret < 0){perror("fail ioctl");return -1;}printf("xres = %d, yres = %d\n", vinf.xres, vinf.yres); printf("xres_virtual = %d, yres_virtual = %d\n", vinf.xres_virtual, vinf.yres_virtual);printf("bits_per_pixel = %d\n", vinf.bits_per_pixel);//将获取到的信息放进定义的结构体变量中fbinf_g.bits = vinf.bits_per_pixel;fbinf_g.x_virtual = vinf.xres_virtual;fbinf_g.y_virtual = vinf.yres_virtual;fbinf_g.fd = fd;//3.建立内存映射关系:将显存空间映射到用户空间size_t size = vinf.xres_virtual*vinf.yres_virtual*vinf.bits_per_pixel/8;fbinf_g.pmem = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);if ((void *)-1 == fbinf_g.pmem){perror("fail mmap");return -1;}return 0;
}/*绘制一个像素点*/
void draw_point(int x, int y, unsigned int col) //传参:坐标x,y和颜色
{if (x >= fbinf_g.x_virtual || y >= fbinf_g.y_virtual){return ; //超过显示屏范围跳出}if (fbinf_g.bits == RGB888_FMT){unsigned int *p = fbinf_g.pmem; //RGB888*(p+y*fbinf_g.x_virtual + x) = col; //将这个点赋值颜色 点为:p+y*宽+x}else if (fbinf_g.bits == RGB565_FMT){unsigned short *p = fbinf_g.pmem; //RGB565*(p+y*fbinf_g.x_virtual + x) = col;}
}/*解除映射*/
void uninit_fb()
{munmap(fbinf_g.pmem , fbinf_g.x_virtual*fbinf_g.y_virtual*fbinf_g.bits/8);// 映射地址 映射内存的大小,显示屏的x*y*深度/8(字节)close(fbinf_g.fd);
}/*画横直线*/
void draw_x_line(int x, int y, int len, unsigned int col)
{for (int i = x; i < x+len; i++){draw_point(i, y, col); //y不变,x递增}
}/*画竖直线*/
void draw_y_line(int x, int y, int len, unsigned int col)
{for (int i = y; i < y+len; i++){draw_point(x, i, col); //x不变,y递增}
}/*画矩形*/
void draw_rectangle(int x, int y, int w, int h, unsigned int col)
{draw_x_line(x, y, w, col);draw_y_line(x, y, h, col);draw_x_line(x, y+h, w, col);draw_y_line(x+w, y, h, col);
}/*画圆*/
void draw_circle(int x, int y, int r, unsigned int col)
{int x0, y0;for (float si = 0; si <= 360; si += 0.1){x0 = r * cos(2 * 3.1415/360 * si) + x;y0 = r * sin(2 * 3.1415/360 * si) + y;draw_point(x0, y0, col);draw_point(x0+1, y0, col);draw_point(x0, y0+1, col);draw_point(x0-1, y0, col);draw_point(x0, y0-1, col);}
}