以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。
一、步骤总结
步骤1:打开设备文件
步骤2:获取设备信息
步骤3:mmap函数做映射
步骤4:填充framebuffer
二、步骤分析
1、打开设备文件
设备文件为/dev/fb0。
2、获取设备信息
应用层通过使用ioctl函数,配合相关的命令码,来获取想要的设备信息。
比如 ret = ioctl(fd, FBIOGET_FSCREENINFO, &finfo);
比如 ret = ioctl(fd, FBIOPUT_VSCREENINFO, &vinfo);
(1)LCD的硬件信息
相关硬件信息定义在x210_kernel\include\linux\fb.h文件中。
(2)struct fb_fix_screeninfo结构体
该结构体用来表示LCD的那些不可修改的属性,比如物理分辨率等等。
struct fb_fix_screeninfo { char id[16]; /* identification string eg "TT Builtin" */ unsigned long smem_start; /* Start of frame buffer mem */ /* (physical address) */ __u32 smem_len; /* Length of frame buffer mem */ __u32 type; /* see FB_TYPE_* */ __u32 type_aux; /* Interleave for interleaved Planes */ __u32 visual; /* see FB_VISUAL_* */ __u16 xpanstep; /* zero if no hardware panning */ __u16 ypanstep; /* zero if no hardware panning */ __u16 ywrapstep; /* zero if no hardware ywrap */ __u32 line_length; /* length of a line in bytes */ 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 reserved[3]; /* Reserved for future compatibility */ };
在应用层使用ioctl函数来获取LCD设备那些不可修改的属性信息时,所使用的命令码是FBIOGET_FSCREENINFO。因为这些属性信息是不可修改的,所以不可能存在表示修改这些属性信息的命令码FBIOPUT__FSCREENINFO。
(3)struct fb_var_screeninfo 结构体
该结构体用来表示LCD的那些可以修改的属性,比如屏幕的分辨率等等。
struct fb_var_screeninfo { __u32 xres; /* visible resolution */ __u32 yres; __u32 xres_virtual; /* virtual resolution */ __u32 yres_virtual; __u32 xoffset; /* offset from virtual to visible */ __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 */ /* 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 reserved[5]; /* Reserved for future compatibility */ };
在应用层使用ioctl函数来获取LCD设备那些可以修改的属性信息时,所使用的命令码是FBIOGET_VSCREENINFO。因为这些属性信息可以修改,所以存在表示对这些属性信息进行修改的命令码FBIOPUT__VSCREENINFO。
3、mmap函数做映射
在linux系统中,使用“ man 2 mmap ”得知mmap函数的原型与用法。
函数原型
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
参数说明
- 第一个参数表示映射到哪个物理地址,写NULL表示自动分配;
- 第二个参数表示被映射的内存的大小;
- 第三个参数表示权限;
- 第四个参数表示当前的映射结果能否被多个进程共享;
- 第五个参数表示文件节点;
- 第六个参数表示偏移量。
- 返回值表示映射结果的内存的开始地址。
补充说明
需要包含头文件:#include <sys/mman.h>
可以利用该函数把内核中那段作为显示缓冲区的内存,映射到当前进程的用户空间。做完了mmap后,fb在当前进程中就绪,随时可以去读写LCD显示器。
4、填充framebuff的例子
例子1:刷背景
使用四个字节表示一个像素,即(0x)xx xx xx xx,后三者表示RGB,前一表示透明度?
使用draw_back()函数来实现,参数为屏幕的长度、宽度、颜色。
void draw_back(unsigned int width, unsigned int height, unsigned int color) { unsigned int x, y; for (y=0; y<height; y++) { for (x=0; x<width; x++) { *(pfb + y * WIDTH + x) = color; } } }
例子2:写字、画线、图片显示等
三、代码与测试
(1)代码
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/ioctl.h> #include <sys/mman.h> // 宏定义 #define FBDEVICE "/dev/fb0" // 旧开发板 //#define WIDTH 800 //#define HEIGHT 480 // 新开发板 #define WIDTH 1024 #define HEIGHT 600 #define WHITE 0xffffffff // test ok #define BLACK 0x00000000 #define RED 0xffff0000 #define BLUE 0xff0000ff #define GREEN 0xff00ff00 // test ok #define GREENP 0x0000ff00 // 一样,说明前2个ff透明位不起作用 // 函数声明 void draw_back(unsigned int width, unsigned int height, unsigned int color); void draw_line(unsigned int color); // 全局变量 unsigned int *pfb = NULL; int main(void) { int fd = -1, ret = -1; struct fb_fix_screeninfo finfo = {0}; struct fb_var_screeninfo vinfo = {0}; // 第1步:打开设备 fd = open(FBDEVICE, O_RDWR); if (fd < 0) { perror("open"); return -1; } printf("open %s success.\n", FBDEVICE); // 第2步:获取设备的硬件信息 ret = ioctl(fd, FBIOGET_FSCREENINFO, &finfo); if (ret < 0) { perror("ioctl"); return -1; } printf("smem_start = 0x%x, smem_len = %u.\n", \ finfo.smem_start, finfo.smem_len); ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo); if (ret < 0) { perror("ioctl"); return -1; } printf("xres = %u, yres = %u.\n", vinfo.xres, vinfo.yres); printf("xres_virtual = %u, yres_virtual = %u.\n",\ vinfo.xres_virtual, vinfo.yres_virtual); printf("bpp = %u.\n", vinfo.bits_per_pixel); // 修改驱动中屏幕的分辨率 vinfo.xres = 1024; vinfo.yres = 600; vinfo.xres_virtual = 1024; vinfo.yres_virtual = 1200; ret = ioctl(fd, FBIOPUT_VSCREENINFO, &vinfo); if (ret < 0) { perror("ioctl"); return -1; } // 再次读出来检验一下 ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo); if (ret < 0) { perror("ioctl"); return -1; } printf("修改过之后的参数:\n"); printf("xres = %u, yres = %u.\n", vinfo.xres, vinfo.yres); printf("xres_virtual = %u, yres_virtual = %u.\n", \ vinfo.xres_virtual, vinfo.yres_virtual); printf("bpp = %u.\n", vinfo.bits_per_pixel); // 第3步:进行mmap unsigned long len = vinfo.xres_virtual * vinfo.yres_virtual *\ vinfo.bits_per_pixel / 8; printf("len = %ld\n", len); pfb = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (NULL == pfb) { perror("mmap"); return -1; } printf("pfb = %p.\n", pfb); //第4步,填充framebuff draw_back(WIDTH, HEIGHT, WHITE);//背景 draw_line(RED);//线条 close(fd); return 0; } void draw_back(unsigned int width, unsigned int height, unsigned int color) { unsigned int x, y; for (y=0; y<height; y++) { for (x=0; x<width; x++) { *(pfb + y * WIDTH + x) = color; } } } void draw_line(unsigned int color) { unsigned int x, y; for (x=50; x<600; x++) { *(pfb + 200 * WIDTH + x) = color; } }
(2)测试
将上面的代码文件命名为app.c,然后使用命令“ arm-linux-gcc app.c -o app ”进行编译,接着在开发板系统上运行“./app.c”。