framebuffer
在Linux系统中,Framebuffer通常是指Framebuffer设备,它是一种特殊的字符设备,在Linux系统中,Framebuffer设备使得程序员可以通过其设定的函数接口直接访问硬件,而不需要通过CPU。
framebuffer的一般操作流程为:
1.打开 (open)
2.获取frame相关信息 (ioctl)
3.映射显存地址 (mmap)
4.向显存写内容(写到映射的用户空间)
5.解除映射 (munmap)
6.关闭 close(fd);
ioctl()
int ioctl(int fd, unsigned long request, ...);
参数:
fd:操作的文件描述符
request:表示与驱动程序交互的命令,
用不同的命令控制驱动程序输出我们需要的数据;
一般使用到的参数:
FBIOGET_FSCREENINFO:返回与Framebuffer有关的固定的信息,
比如图形硬件上实际的帧缓存空间的大小、
能否硬件加速等信息。
FBIOGET_VSCREENINFO:返回的是与Framebuffer有关的可变信息。
...:表示可变参数 arg,根据 request 命令,
设备驱动程序返回输出的数据。
一般使用struct fb_var_screeninfo 来接收参数,对framebuffer进行基础操作用到
上面几个标注的参数即可
返回值:
成功:返回文件描述符
失败:-1
struct fb_var_screeninfo {
__u32 xres; /* visible resolution */这两个字段表示可见分辨率的宽度和高度,
__u32 yres; //单位是像素。
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual; //这两个字段表示虚拟分辨率的宽度和高度,单位是像素。
//虚拟分辨率是指Framebuffer可以支持的最大分辨率。
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */ //这两个字段表示从虚拟分辨率到
//可见分辨率之间的偏移量。
__u32 bits_per_pixel; /* guess what */ //这个字段表示每个像素的比特数,
//用于确定每个像素的颜色位数。
__u32 grayscale; /* 0 = color, 1 = grayscale, */
/* >1 = FOURCC */
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;__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 */
/* Timing: All values in pixclocks, except pixclock (of course) */
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
};
mmap()
void *mmap(void *addr, size_t length, int prot,
int flags,int fd, off_t offset);
功能:
创建内存映射
参数:
addr:表示指定映射的內存起始地址,通常设为 NULL,在内存中的映射区中(不是堆)
表示让系统自动选定地址,并在成功映射后返回该地址;
length:表示将文件中多大的内容映射到内存中;
一般通过虚拟分辨率(支持最大分辨率)算出:
unsigned long size = info.xres_virtual * info.yres_virtual * info.bits_per_pixel / 8;
//前面三个变量相乘得到虚拟分辨率所占的比特数(最大分辨率),除8得到字节数
prot:表示映射区域的保护方式,可以为以下 4 种方式的组合
a. PROT_EXEC 映射区域可被执行
b. PROT_READ 映射区域可被读出
c. PROT_WRITE 映射区域可被写入
d. PROT_NONE 映射区域不能存取
a. MAP_SHARED 用于创建一个私有的映射,写操作不会立即生效,
当映射的文件被关闭时,所有未写入的更改会被写入到文件中。
这种行为类似于文件的临时副本,可以被修改,
但不会立即影响文件的内容。
可以安全地修改而不立即影响文件。
b. MAP_PRIVATE 用于创建一个共享的映射,写操作会立即生效,
会同步到磁盘,并可以被其他进程看到,
类似于直接在文件上进行修改
munmap()
int munmap(void *addr, size_t length)
功能:与mmap函数配套,解除内存映射
参数:
addr: 映射区域的起始地址,即mmap()函数的返回值。
length: 映射区域的大小,以字节为单位。应与mmap()函数中的length参数相同。
返回值:
成功:返回0
失败:返回-1
显示器与显存的关系:
注意:从映射内存中走完显示器整行内容后,下一个元素并不是第二行的第一个元素,而是走完整行显存后,下一个元素才是第二行的第一个元素
unsigned int * p = (unsigned int *)(p_fd + (y0 * info.xres_virtual + x0) * 4);
示例代码:
1.为什么要使用结构体__color与联合体col?
便于修改与传参,要对这个颜色(r,g,b)中修改时,明显通过结构体对每一位修改更加直观,更加方便,而联合体的另一个int型变量与结构体(4个char型变量)所占用空间一样,公用一个空间,这时,传入int型l就相当于传入了这个颜色所有的位变量
2.mmap映射出的地址p_fd在函数中使用,是使用全局变量好还是使用传参好?
全局变量好,因为如果在实际使用中封装函数不希望别的使用者来对这个变量进行修改时(修改后无法找到该映射空间首地址),使用全局变量后,别的使用者无法调用。
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <math.h>
#define pi 3.141592657
struct fb_var_screeninfo info;
unsigned char * p_fd = NULL;
typedef struct __color
{
unsigned char b;
unsigned char g;
unsigned char r;
unsigned char null; //暂时不需要透明度
}color;
typedef union
{
color rgb;
unsigned int l;
}col;
int draw_point(int x0, int y0, col c)
{
if((x0 < 0) || (x0 > info.xres_virtual))
return -1;
if((y0 < 0) || (y0 > info.yres_virtual))
return -1;
unsigned int * p = (unsigned int *)(p_fd + (y0 * info.xres_virtual + x0) * 4);
*p = c.l;
return 0;
}
int draw_h_line(int x0, int y0, int len, col c)
{
int i = 0;
for(i = 0; i < len; i++)
{
draw_point(x0 + i, y0, c);
}
return 0;
}
int draw_v_line(int x0, int y0, int len, col c)
{
int i = 0;
for(i = 0; i < len; i++)
{
draw_point(x0, y0 + i, c);
}
return 0;
}
int draw_cicle(int x0, int y0, int r, col c)
{
float i=0;
for(i=0;i<360;i+=0.1)
{
int x = r * cos(2 * pi * i/360) + x0;
int y = r * sin(2 * pi * i/360) + y0;
draw_point(x,y,c);
}
}
int main(int argc, const char *argv[])
{
//open
int fd = open("/dev/fb0", O_RDWR);
if(fd < 0)
{
perror("open fd failed:");
return fd;
}
//get_screen_info
int ret = ioctl(fd, FBIOGET_VSCREENINFO, &info);
if(ret < 0)
{
perror("ioctl failed:");
return ret;
}
printf("xres = %d yres = %d\n",
info.xres, info.yres);
printf("xres_virtual = %d yres_virtual = %d\n",
info.xres_virtual, info.yres_virtual);
printf("bits_per_pixel = %d\n", info.bits_per_pixel);
//mmap
unsigned long size = info.xres_virtual * info.yres_virtual * info.bits_per_pixel / 8;
p_fd = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
if(NULL == p_fd)
{
perror("mmap failed:");
return -3;
}
col c;
c.rgb.r = 0xff;
c.rgb.g = 0xff;
c.rgb.b = 0x0;
//draw_h_line(100, 100, 200, c);
draw_cicle(100,200,120,c);
//munmap
munmap(p_fd, size);
//close
close(fd);
return 0;
}