framebuffer驱动详解1——应用层编程实践

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

一、步骤总结

步骤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:写字、画线、图片显示等

见博客:s5pv210——LCD的原理和实战_天糊土的博客-CSDN博客

三、代码与测试

(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”。

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...