I.MX6ULL之LCD显示

I.MX6ULL之LCD显示

代码放在git仓库,有需要的可以自行下载:Gitee

LCD的操作原理:

在Linux系统中通过Framebuffer驱动程序来控制LCD。我们只需要知道怎么获取LCD的参数,并且使用mmap映射到Framebuffer上,然后再Framebuffer中写入数据就可以了。

详细的流程如下图所示:

在这里插入图片描述

对于上图的32bpp,我们要引入RGB的显示格式,其中包括RGB888,RGB565,RGB555。本文中使用的时RGB565。他们的不同点就是在于后面根据FrameBuff取出来的数据要进行不同的处理,比如使用RGB565时,代码的配置如下:

red   = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue  = (color >> 0) & 0xff;
color = (((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3));/* 分别取出R的高5位,G的高6位,B的高6位,组成一个数据 */

在这里插入图片描述

API函数:具体函数使用请参考man手册

1.open函数:

在这里插入图片描述

描述:用于打开一个文件,并且返回文件描述符(系统的一些宏定义,非负整数),如果失败了会返回-1

参数:pathname是要打开或创建的文件的名字。oflag参数可用来说明此函数的多个选择项,用一些宏来传入。

2.ioctl函数:

在这里插入图片描述

描述:针对设备的控制操作,可以传数据给驱动程序,也可以从驱动程序中读出数据。。

参数:fd 参数为某个设备或文件已打开的文件描述符, request 参数指定了将在 fd 上执行的控制操作。具体设备的头文件定义了可传递给 request 参数的常量 。

3.mmap函数:

在这里插入图片描述

描述:在调用进程的虚拟地址空间中创建一个新映射。

参数:addr 参数指定了映射被放置的虚拟地址。length 参数指定了映射的字节数。port参数是一个位掩码,它制定了施加于映射之上的保护信息,其取值为一些宏。flags 参数是一个控制映射操作各个方面的选项的位掩码(私有映射/共享映射)。fd参数是一个标识被映射的文件的文件描述符。offset参数指定了映射在文件中的起点。

4.close函数:

在这里插入图片描述

描述:关闭一个打开的文件描述符,并将其释放回调用进程。

参数:文件描述符

5.munmap函数:

在这里插入图片描述

描述:从调用进程的虚拟地址空间中删除一个映射。

获取LCD的参数

包含在#include<linux/fb.h>

在这里插入图片描述

字符的编码格式

编码与字体:

1.编码:编码是信息从一种形式或格式转换为另一种形式的过程,也称为计算机编程语言的代码简称编码。此处使用的编码即为将一个字符采用什么ascii码来显示。

1️⃣ASCII:是American Standard Code for Information Interchange的缩写,美国信息交换标准代码。

​ 即采用一个数值来表示对应的字符。

2️⃣ANSI:ANSI是一种字符代码,为使计算机支持更多语言,通常使用 0x00~0x7f 范围的1 个字节来表示 1 个英文字符。超出此范围的使用0x80~0xFFFF来编码,即扩展的ASCII编码。

​ 对于ASCII字符仍以一个字节来表示,对于非ASCII字符则使用2字节来表示。其实ANSI并不是某一种特定的字符编码,而是在不同的系统中,ANSI表示不同的编码。

3️⃣UNICODE:统一码,也叫万国码、单一码(Unicode).Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。码位就是可以分配给字符的数字。

ASCII编码中,使用一个字节来表示一个字符,只用到其中的7位,最高位恒为0;

ANSI编码中,对于ASCII字符仍使用一个字节来表示(BIT7是0),对于非ASCII字符一般使用2个字节来表示,非ASCII字符的数值BIT7都是1。

UNICODE编码中,对不同范围的字符使用不同长度的编码。常用UTF-8

2.ASCII字符的显示

​ 要在LCD中显示一个ASCII字符,即英文字母这些字符,首先是要找到字符对应的点阵。在Linux内核源码中有这个文件:lib\fonts\font_8x16.c,里面以数组形式保存各个字符的点阵。

3.中文字符的点阵显示:

手动指定编码格式,以及手动指定可执行程序的编码格式,默认为UTF-8

#编译时的编码格式
-finput-charset=GB2312
-finput-charset=UTF-8
#编译出来的可执行程序的编码格式
-fexec-charset=GB2312
-fexec-charset=UTF-8

4.代码实现:

static int fd_fb;
static struct fb_var_screeninfo var;	/* Current var */
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;
static unsigned int pixel_width;

int fd_hzk16;
struct stat hzk_stat;
unsigned char *hzkmem;





/*
	description:在(x,y)处画颜色可修改的点
	int x:				x坐标
	int y:				y坐标
	unsigned int color:	颜色(采用RGB)
*/
void lcd_put_pixel(int x, int y, unsigned int color)
{
	/* 字号大小 */
	unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
	unsigned short *pen_16;	
	unsigned int *pen_32;	

	unsigned int red, green, blue;

	pen_16 = (unsigned short *)pen_8;
	pen_32 = (unsigned int *)pen_8;


	switch(var.bits_per_pixel)	/* 选择BPP */
	{
		case 8:
		{
			*pen_8 = color;
			break;
		}
		case 16: 
		{
			/* 565格式 */
			red   = (color >> 16) & 0xff;
			green = (color >> 8) & 0xff;
			blue  = (color >> 0) & 0xff;
			color = (((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3));
			*pen_16 = color;
			break;
		}
		case 32:
		{
			*pen_32 = color;
			break;
		}
		default:
		{
			printf("can't surport %dbpp\n", var.bits_per_pixel);
			break;
		}

	}


}

/**********************************************************************
 * 函数名称: lcd_put_ascii
 * 功能描述: 在LCD指定位置上显示一个8*16的字符
 * 输入参数: x坐标,y坐标,ascii码,颜色
 * r	o	y	g	c	b	p
 * 红	橙	黄	绿	青	蓝	紫
 * 输出参数: 无
 * 返 回 值: 无
 ***********************************************************************/ 
void lcd_put_ascii(int x,int y,unsigned char c,unsigned int rgb)
{
	unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];	/* 获取一个字符的首地址 */
	int i,b;			/* i->16行 b->8列 */
	unsigned char byte;	/* 用于中间存储每一行的byte数值 */
	unsigned int color;


	switch(rgb)
	{
		case 'r':color = 0xff0000;break;
		case 'o':color = 0xffa500;break;
		case 'y':color = 0xffff00;break;
		case 'g':color = 0x00ff00;break;
		case 'c':color = 0x00ffff;break;
		case 'b':color = 0x0000ff;break;
		case 'p':color = 0x800080;break;

		default:
			break;
	}

	for(i = 0; i < 16;i++)
	{
		byte = dots[i];		/* 指针数组 */
		for(b = 0;b < 8;b++)
		{
			if(byte & (1<<b))	/* 如果这一位为1:点亮 */
				lcd_put_pixel(x+7-b,y+i, color);		/* 因为X轴是低位在前,高位在后 */
			else
				lcd_put_pixel(x+7-b,y+i, 0);		

		}
	}

}


/**********************************************************************
 * 函数名称: lcd_put_string
 * 功能描述: 在LCD指定位置上显示8*16的字符串
 * 输入参数: x坐标,y坐标,字符串,颜色
 * r	o	y	g	c	b	p
 * 红	橙	黄	绿	青	蓝	紫
 * 输出参数: 无
 * 返 回 值: 无
 ***********************************************************************/ 
void lcd_put_string(int x,int y,unsigned char *c,unsigned int rgb)
{
	while(*c != '\0')
	{
		lcd_put_ascii(x,y,*c++,rgb);
		x += 8;
	}

}

/**********************************************************************
 * 函数名称: lcd_put_chinese
 * 功能描述: 在LCD指定位置上显示一个16*16的汉字
 * 输入参数: x坐标,y坐标,ascii码
 * 输出参数: 无
 * 返 回 值: 无
 ***********************************************************************/
void lcd_put_chinese(int x,int y,unsigned char *str,unsigned int rgb)
{
	unsigned int area = str[0] - 0xa1;	/* 区码 从0xa1开始,避免和ascii码冲突 */
	unsigned int where = str[1] - 0xa1;	/* 位码 */
	unsigned char *dots = hzkmem + (area * 94 + where)*32;	/* 从HZK的映射地址开始,每个区94个汉字+位的地址,一个汉字占32个字节 */
	unsigned char byte;

	unsigned int color;

	int i,j,b;

	switch(rgb)
	{
		case 'r':color = 0xff0000;break;
		case 'o':color = 0xffa500;break;
		case 'y':color = 0xffff00;break;
		case 'g':color = 0x00ff00;break;
		case 'c':color = 0x00ffff;break;
		case 'b':color = 0x0000ff;break;
		case 'p':color = 0x800080;break;

		default:
			break;
	}



	for(i = 0;i < 16;i++)
	{
		/* 判断是哪个字节 */
		for(j = 0;j < 2;j++)
		{
			byte = dots[i*2 + j];
			for(b = 0;b < 7;b++)
			{
				if(byte & (1 << b))
				{
					lcd_put_pixel(x+j*8+7-b, y+i, color); /* 白 */
				}
				else
				{
					lcd_put_pixel(x+j*8+7-b, y+i, 0); /* 黑 */
				}
			}
		}
	}
}







int main(int argc, char **argv)
{
	int i,j;
	unsigned char *str = "中";
	unsigned char *str1 = "国";

	fd_fb = open("/dev/fb0",O_RDWR);		/* 以可读可写的方式打开/dev/fb0 */
	if(fd_fb < 0)
	{
		printf("can't open /dev/fb0\n");	/* 返回值为-1,表示打开文件失败! */
		return -1;
	}
	if(ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))		/* get var screen info:获取屏幕的可变信息 */
	{
		printf("can't get var\n");
		return -1;
	}
	line_width  = var.xres * var.bits_per_pixel / 8;	/* 行宽 = X*BPP/8 */
	pixel_width = var.bits_per_pixel / 8;				/* 列宽 = BPP/8 */
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;	/* FrameBuffer的总大小 */
	fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if(fb_base == (unsigned char *)-1)
	{
		printf("can't mmap\n'");
		return -1;
	}

	fd_hzk16 = open("HZK16", O_RDONLY);		/* 打开字体库 */
	if (fd_hzk16 < 0)
	{
		printf("can't open HZK16\n");
		return -1;
	}
	if(fstat(fd_hzk16,&hzk_stat))
	{
		printf("can't get fstat\n");
		return -1;
	}

	/* mmap的返回结果保存在hzkmem中,作为字库的基地址 */
	hzkmem = (unsigned char *)mmap(NULL,hzk_stat.st_size,PROT_READ, MAP_SHARED,fd_hzk16,0);
	if(hzkmem == (unsigned char *)-1)
	{
		printf("can't mmap for hzk16\n'");
		return -1;
	}




	// /* 清屏: 全部设为白色 */
	// memset(fb_base, 0xff, screen_size);	/* 在fb_base地址上的screen_size大小的区域全部设为1 */
		/* 清屏: 全部设为黑色 */
	memset(fb_base, 0, screen_size);	/* 在fb_base地址上的screen_size大小的区域全部设为1 */

	// /* 随便设置出100个为红色 */
	// for (i = 0; i < 500; i++)
	// {
	// 	for(j=0; j < 100;j++)
	// 	{
	// 		lcd_put_pixel(var.xres/2+i, var.yres/2+j, 0x800080);	/* 在中间X轴画100个点 */
	// 		// lcd_put_pixel(var.xres+i, var.yres+j, 0x800080);	/* 在中间X轴画100个点 */
	// 	}
			
	// }


	// lcd_put_ascii(var.xres/2,var.yres/2,'S','r');		/* 在屏幕中间显示一个8*16的字母S */
	// lcd_put_string(var.xres/2,var.yres/2,"hello world!",'r');
	lcd_put_chinese(var.xres/2 + 8,  var.yres/2, str,'r');
	lcd_put_chinese(var.xres/2 + 32,  var.yres/2, str1,'r');
  
	munmap(fb_base , screen_size);		/* 取消映射 */
	close(fd_fb);						/* 关掉文本 */

	return 0;

}

// }

// }


// lcd_put_ascii(var.xres/2,var.yres/2,'S','r');		/* 在屏幕中间显示一个8*16的字母S */
// lcd_put_string(var.xres/2,var.yres/2,"hello world!",'r');
lcd_put_chinese(var.xres/2 + 8,  var.yres/2, str,'r');
lcd_put_chinese(var.xres/2 + 32,  var.yres/2, str1,'r');

munmap(fb_base , screen_size);		/* 取消映射 */
close(fd_fb);						/* 关掉文本 */

return 0;

}






  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值