FrameBuffer应用编程
步骤:
- 首先打开/dev/fbX 设备文件。
- 使用 ioctl()函数获取到当前显示设备的参数信息,譬如屏幕的分辨率大小、像素格式,根据屏幕参
数计算显示缓冲区的大小。- 通过存储映射 I/O 方式将屏幕的显示缓冲区映射到用户空间(mmap)。
- 映射成功后就可以直接读写屏幕的显示缓冲区,进行绘图或图片显示等操作了。
- 完成显示后, 调用 munmap()取消映射、并调用 close()关闭设备文件。
一、使用 ioctl()获取屏幕参数信息
#include <linux/fb.h>
#define FBIOGET_VSCREENINFO 0x4600
#define FBIOPUT_VSCREENINFO 0x4601
#define FBIOGET_FSCREENINFO 0x4602
FBIOGET_VSCREENINFO
:表示获取 FrameBuffer 设备的可变参数信息,可变参数信息使用 struct fb_var_screeninfo
结 构 体 来 描 述 , 所 以 此 时 ioctl()
需 要 有 第 三 个 参 数 , 它 是 一 个 struct fb_var_screeninfo *
指针,指向 struct fb_var_screeninfo
类型对象
struct fb_var_screeninfo fb_var;
ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
FBIOPUT_VSCREENINFO
:表示设置 FrameBuffer 设备的可变参数信息,既然是可变参数,那说明应用层可对其 进行修改、重新配置。尽量别去动它
FBIOGET_FSCREENINFO
:表示获取 FrameBuffer
设备的固定参数信息,既然是固定参数,那就
意味着应用程序不可修改。固定参数信息使用struct fb_fix_screeninfo
结构体来描述,所以此时ioctl()
需要有第三个参数,它是一个 struct fb_fix_screeninfo *
指针
struct fb_fix_screeninfo fb_fix;
ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
struct fb_var_screeninfo {
__u32 xres; /* 可视区域,一行有多少个像素点, X 分辨率 */
__u32 yres; /* 可视区域,一列有多少个像素点, Y 分辨率 */
__u32 xres_virtual; /* 虚拟区域,一行有多少个像素点 */
__u32 yres_virtual; /* 虚拟区域,一列有多少个像素点 */
__u32 xoffset; /* 虚拟到可见屏幕之间的行偏移 */
__u32 yoffset; /* 虚拟到可见屏幕之间的列偏移 */
__u32 bits_per_pixel; /* 每个像素点使用多少个 bit 来描述,也就是像素深度 bpp */
__u32 grayscale; /* =0 表示彩色, =1 表示灰度, >1 表示 FOURCC 颜色 */
/* 用于描述 R、 G、 B 三种颜色分量分别用多少位来表示以及它们各自的偏移量 */
struct fb_bitfield red; /* Red 颜色分量色域偏移 */
struct fb_bitfield green; /* Green 颜色分量色域偏移 */
struct fb_bitfield blue; /* Blue 颜色分量色域偏移 */
struct fb_bitfield transp; /* 透明度分量色域偏移 */
__u32 nonstd; /* nonstd 等于 0,表示标准像素格式;不等于 0 则表示非标准像素格式 */
__u32 activate;
__u32 height; /* 用来描述 LCD 屏显示图像的高度(以毫米为单位) */
__u32 width; /* 用来描述 LCD 屏显示图像的宽度(以毫米为单位) */
__u32 accel_flags;
/* 以下这些变量表示时序参数 */
__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 */
};
struct fb_bitfield {
__u32 offset; /* 偏移量 */
__u32 length; /* 长度 */
__u32 msb_right; /* != 0 : Most significant bit is right */
};
struct fb_fix_screeninfo {
char id[16]; /* 字符串形式的标识符 */
unsigned long smem_start; /* 显存的起始地址(物理地址) */
__u32 smem_len; /* 显存的长度 */
__u32 type;
__u32 type_aux;
__u32 visual;
__u16 xpanstep;
__u16 ypanstep;
__u16 ywrapstep;
__u32 line_length; /* 一行的字节数 */
unsigned long mmio_start; /* Start of Memory Mapped I/O(physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which specific chip/card we have */
__u16 capabilities;
__u16 reserved[2];
};
获取屏幕分辨率、像素深度、RGB格式
#include <linux/fb.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
int main(int argc,char * argv[])
{
int fd;
struct fb_var_screeninfo fb_var;
struct fb_fix_screeninfo fb_fix;
if(2!=argc){
fprintf(stderr,"argc err\n");
exit(1);
}
fd=open(argv[1],O_WRONLY);
if(-1==fd){
perror("open err\n");
exit(1);
}
ioctl(fd,FBIOGET_VSCREENINFO,&fb_var);
ioctl(fd,FBIOGET_FSCREENINFO,&fb_fix);
printf("分辨率:%d*%d\n",fb_var.xres,fb_var.yres);
printf("像素深度bpp:%d\n",fb_var.bits_per_pixel);
printf("一行的字节数:%d\n",fb_fix.line_length);
printf("像素格式:Red<偏移量:%d,长度:%d>,Green<偏移量:%d,长度:%d>,Blue<偏移量:%d,长度:%d>\n",fb_var.red.offset,fb_var.red.length,
fb_var.green.offset,fb_var.green.length,fb_var.blue.offset,fb_var.blue.length);
exit(0);
}
二、存储映射I/O
[存储映射I/O]((23条消息) 嵌入式Linux中高级I/O的常用知识点_sharp_OvO的博客-CSDN博客)
三、LCD中显示BMP图片
1.BMP图片组成
典型的 BMP 图像文件由四部分组成:
-
BMP 文件头(14Byte),它包含 BMP 文件的格式、大小、 位图数据的偏移量等信息;
-
位图信息头(40或56Byte) ,它包含位图信息头大小、 图像的尺寸、 图像大小、 位平面数、
压缩方式以及颜色索引等信息; -
调色板,这部分是可选的,如果使用索引来表示图像, 调色板就是索引与其对应颜色的映射表;(真彩色图像不需要调色板)
-
位图数据,也就是图像数据。
//bmp 文件头定义了如下结构体: typedef struct tagBITMAPFILEHEADER { UINT16 bfType; //说明 bmp 文件的类型,可取值为:BM – Windows DWORD bfSize; //4个字节,说明该文件的大小,以字节为单位。 UINT16 bfReserved1; //保留字段,必须设置为 0。 UINT16 bfReserved2; //保留字段,必须设置为 0。 DWORD bfOffBits; //说明从文件起始位置到图像数据之间的字节偏移量。 } BITMAPFILEHEADER;
bfType
:说明 bmp 文件的类型(实验中用到的是BM);bfSize
:说明该文件的大小;bfOffBits
:说明从文件起始位置到图像数据之间的字节偏移量。(重要)//位图信息头定义了如下结构体: typedef struct tagBITMAPINFOHEADER { DWORD biSize; //位图信息头大小 LONG biWidth; //图像的宽度,以像素为单位(4byte) LONG biHeight; //图像的高度,以像素为单位(4byte) WORD biPlanes; WORD biBitCount; //像素深度,指明一个像素点需要多少个 bit 数据来描述(2byte) DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; //水平分辨率,用像素/米来表示,有符号整数。 LONG biYPelsPerMeter; //垂直分辨率,用像素/米来表示,有符号整数 DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER;
biHeight
:这个值除了用于描述图像的高度之外, 它还有另外一个用途,用于指明该图像是倒向 的位图、还是正向的位图。 如果该值是一个正数,说明是倒向的位图;如 果该值是一个负数,则说明是正向的位图。 一般情况下, BMP 图像都是倒向的位图,也就 是该值是一个正数。#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <string.h> #include <linux/fb.h> #include <sys/mman.h> /**** BMP文件头数据结构 ****/ typedef struct { unsigned char type[2]; //文件类型 unsigned int size; //文件大小 unsigned short reserved1; //保留字段1 unsigned short reserved2; //保留字段2 unsigned int offset; //到位图数据的偏移量 } __attribute__ ((packed)) bmp_file_header; /**** 位图信息头数据结构 ****/ typedef struct { unsigned int size; //位图信息头大小 int width; //图像宽度 int height; //图像高度 unsigned short planes; //位面数 unsigned short bpp; //像素深度 unsigned int compression; //压缩方式 unsigned int image_size; //图像大小 int x_pels_per_meter; //像素/米 int y_pels_per_meter; //像素/米 unsigned int clr_used; unsigned int clr_omportant; } __attribute__ ((packed)) bmp_info_header; /**** 静态全局变量 ****/ static int width; //LCD X分辨率 static int height; //LCD Y分辨率 static unsigned short *screen_base = NULL; //映射后的显存基地址 static unsigned long line_length; //LCD一行的长度(字节为单位) /******************************************************************** * 函数名称: show_bmp_image * 功能描述: 在LCD上显示指定的BMP图片 * 输入参数: 文件路径 * 返 回 值: 成功返回0, 失败返回-1 ********************************************************************/ static int show_bmp_image(const char *path) { bmp_file_header file_h; bmp_info_header info_h; unsigned short *line_buf = NULL; //行缓冲区 unsigned long line_bytes; //BMP图像一行的字节的大小 unsigned int min_h, min_bytes; int fd = -1; int j; /* 打开文件 */ if (0 > (fd = open(path, O_RDONLY))) { perror("open error"); return -1; } /* 读取BMP文件头 */ if (sizeof(bmp_file_header) != read(fd, &file_h, sizeof(bmp_file_header))) { perror("read error"); close(fd); return -1; } if (0 != memcmp(file_h.type, "BM", 2)) { fprintf(stderr, "it's not a BMP file\n"); close(fd); return -1; } /* 读取位图信息头 */ if (sizeof(bmp_info_header) != read(fd, &info_h, sizeof(bmp_info_header))) { perror("read error"); close(fd); return -1; } /* 打印信息 */ printf("文件大小: %d\n" "位图数据的偏移量: %d\n" "位图信息头大小: %d\n" "图像分辨率: %d*%d\n" "像素深度: %d\n", file_h.size, file_h.offset, info_h.size, info_h.width, info_h.height, info_h.bpp); /* 将文件读写位置移动到图像数据开始处 */ if (-1 == lseek(fd, file_h.offset, SEEK_SET)) { perror("lseek error"); close(fd); return -1; } /* 申请一个buf、暂存bmp图像的一行数据 */ line_bytes = info_h.width * info_h.bpp / 8; line_buf = malloc(line_bytes); if (NULL == line_buf) { fprintf(stderr, "malloc error\n"); close(fd); return -1; } if (line_length > line_bytes) min_bytes = line_bytes; else min_bytes = line_length; /**** 读取图像数据显示到LCD ****/ /******************************************* * 为了软件处理上方便,这个示例代码便不去做兼容性设计了 * 如果你想做兼容, 可能需要判断传入的BMP图像是565还是888 * 如何判断呢?文档里边说的很清楚了 * 我们默认传入的bmp图像是RGB565格式 *******************************************/ if (0 < info_h.height) {//倒向位图 if (info_h.height > height) { min_h = height; lseek(fd, (info_h.height - height) * line_bytes, SEEK_CUR); screen_base += width * (height - 1); //定位到屏幕左下角位置 } else { min_h = info_h.height; screen_base += width * (info_h.height - 1); //定位到....不知怎么描述 懂的人自然懂! } for (j = min_h; j > 0; screen_base -= width, j--) { read(fd, line_buf, line_bytes); //读取出图像数据 memcpy(screen_base, line_buf, min_bytes);//刷入LCD显存 } } else { //正向位图 int temp = 0 - info_h.height; //负数转成正数 if (temp > height) min_h = height; else min_h = temp; for (j = 0; j < min_h; j++, screen_base += width) { read(fd, line_buf, line_bytes); memcpy(screen_base, line_buf, min_bytes); } } /* 关闭文件、函数返回 */ close(fd); free(line_buf); return 0; } int main(int argc, char *argv[]) { struct fb_fix_screeninfo fb_fix; struct fb_var_screeninfo fb_var; unsigned int screen_size; int fd; /* 传参校验 */ if (2 != argc) { fprintf(stderr, "usage: %s <bmp_file>\n", argv[0]); exit(-1); } /* 打开framebuffer设备 */ if (0 > (fd = open("/dev/fb0", O_RDWR))) { perror("open error"); exit(EXIT_FAILURE); } /* 获取参数信息 */ ioctl(fd, FBIOGET_VSCREENINFO, &fb_var); ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix); screen_size = fb_fix.line_length * fb_var.yres; line_length = fb_fix.line_length; width = fb_var.xres; height = fb_var.yres; /* 将显示缓冲区映射到进程地址空间 */ screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0); if (MAP_FAILED == (void *)screen_base) { perror("mmap error"); close(fd); exit(EXIT_FAILURE); } /* 显示BMP图片 */ memset(screen_base, 0xFF, screen_size); show_bmp_image(argv[1]); /* 退出 */ munmap(screen_base, screen_size); //取消映射 close(fd); //关闭文件 exit(EXIT_SUCCESS); //退出进程 }