数码相框——2、2 字符的点阵显示

from:https://blog.csdn.net/xiaodingqq/article/details/84893316

1、原理

在这里插入图片描述
在SDRAM内存里划出一块空间为FrameBuffer显存,LCD控制器会从FrameBuffer里取出若干节的数据(像素),发给LCD。

2、ASCII码字库文件使用

echo hello > /dev/tty1 就可以在LED上显示hello字样,说明内核是有英文字母的点阵。

在内核中搜索font,找到内核有一个font_8x16.c文件。
如下图所示,找到8*16的点阵存在fontdata_8x16[]数组里:
在这里插入图片描述
我们以0x41(A)为例子,找到该点阵信息为:
在这里插入图片描述

可以看到一个ASCII代表了16字节,后面我们直接将fontdata_8x16[]数组拷贝到应用程序里,用来显示ASCII

3、HZK16汉字库文件使用

https://blog.twofei.com/embedded/hzk.html
(1)HZK16描述
在这里插入图片描述

HZK16是按分区表排列的点阵文件,由于每个汉字是2个字节,每个字节的点阵是8*16

所以HZK16里的每个汉字点阵大小:2 * 8 * 16=32字节

(2)然后还要将编码转为点阵码。

4、编写代码

(1)首先要获得LCD的参数

1)打开/dev/fb0

int fd_fb;
fd_fb = open("/dev/fb0", O_RDWR);
if(fd_fb < 0)
{
	printf("can't open /dev/fb0\n");
	return -1;
}

2)通过ioctl()获得可变信息,固定信息

ioctl()是设备驱动程序中设备控制接口函数
#include <sys/ioctl.h>
详解:https://blog.csdn.net/qq_19923217/article/details/82698787
FBIOGET_VSCREENINFO:获取可变信息:xy分辨率,像素位数等)
FBIOGET_FSCREENINFO:获取固定信息:缓存地址,每行字节数)
如何所示:
在fbmem.c
在这里插入图片描述

将可变信息和固定信息存放在 var和fix里

struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;

通过ioctl()函数获得可变信息,固定信息

if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
	printf("can't get var\n");
	return -1;
}

if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
{
	printf("can't get fix\n");
	return -1;
}

(2)通过mmap映射FramBuffer

mmamp()函数: 申请一段用户空间的内存区域,并映射到内核空间某个内存区域。
头文件: #include<sys/mman.h>
返回值: 失败返回-1,并设置errno值.成功,返回映射的地址指针.若指定start则返回0

start: 需要映射的内存起始地址,通常填NULL,表示让系统自动映射,映射成功后返回该地址.
length: 映射地址的大小,填LCD显存字节数即可,因为2440一个地址存8位.

prot: 对映射地址的保护(protect)方式,常用组合如下:

PROT_EXEC 映射区域可被执行
PROT_READ 映射区域可被读取
PROT_WRITE 映射区域可被写入
PROT_NONE映射区域不可访问

flag: 填MAP_SHARED即可,表示共享此映射,对其它进程可见.

fd: 需要将内存映射到哪个文件描述符(以后便可以直接通过内存来直接操作该文件)

offset: 映射偏移值,填0即可.

mmap的参数详情使用请参考:http://blog.csdn.net/dlutbrucezhang/article/details/9080173

int screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
//x分辨率 * y分辨率 * 每个像素占多少位/8bit 
//(每个像素占多少位/8bit = 每个像素占据多少字节)
unsinged char* fbmem = (unsigned char*)mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if(fbmem == (unsigned char*)-1)
{
	printf("can't mmap\n");
	return -1;
}

(3)显示字母、汉字

unsigned char str[] = '中'; //将汉字存在数组里
lcd_put_ascii( var.xres/2, var.yres/2, 'A'); //在LCD中间显示字母
printf("chinese code: %02x %02x\n", str[0], str[1]);
lcd_put_chinese(var.xres/2 + 8,  var.yres/2, str);//在字母后面显示汉字

1)lcd_put_ascii

void lcd_put_ascii(int x, int y, unsigned char c)
{
	unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
	int i, b;
	unsigned char byte;
	
	//将点阵一行一行描出来
	for (i = 0; i < 16; i++)
	{
		byte = dots[i];//取出第一行
		for (b = 7; b >= 0; b--)
		{
			if (byte & (1<<b))
			{
				/* show */
				lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */
			}
			else
			{
				/* hide */
				lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
			}
		}
	}
}

2)打开汉字库

int fd_hzk16 = open("HZK16", O_RDONLY);
if(fd_hzk16 < 0)
{
	printf("can't open HZK16\n");
	return -1;
}

或者用mmap()映射成内存
但是我们需要知道HZK16该文件大小
因此使用int fstat(int fd, struct stat *statbuf);函数

struct stat hzk_stat;
if( fstat(fd_hzk16, &hzk_stat)
{
	printf("can't get fstat\n");
	return -1;
}

在这里插入图片描述

unsinged char* 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\n");
	return -1;
} //这样就可以访问数组一样访问汉字库

3)lcd_put_chinese
在这里插入图片描述

 void lcd_put_chinese(int x, int y, unsigned char* str)
 {
 	unsigned int area = str[0] - 0xA1;  //区码,减去0xA1的原因是汉字库的行和列是从0xA1开始的
 	unsigned int where = str[1] - 0xA1;//位码
 	unsigned char* dots = hzkmem + (area * 94 + where) * 32;
 	unsigned char byte;
	
	int i, j, b;
	for(i=0; i<16; i++)
	{
		for(j=0; j<2; j++)
		{
			byte = dots[i * 2 + j];
			for(b=7; b>=0; b--)
			{
			if(byte & (1 << b))
			{
				/* show */
				lcd_put_pixel(x+7-b+j*8, y+i, 0xffffff); /* 白 */
			}
			else
			{
				/* hide */
				lcd_put_pixel(x+7-b+j*8, y+i, 0); /* 黑 */
			}
		}
	}
}

4)lcd_put_pixel

unsigned int line_width;//LCD每行有多少字节
unsigned int pixel_width;//每个像素占多少字节
line_width = var.xres * var.bit_per_pixel / 8;
pixel_width = var.bit_per_pixel / 8;

/*color : 0x00RRGGBB*/	
void lcd_put_pixel(int x, int y, unsigned int color)
{
	unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width; //char 1个字节
	unsigned short *pen_16;	//short 2个字节
	unsigned int *pen_32;	//4个字节

	unsigned int red, green, blue;	

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

	//开始描点
	switch (var.bits_per_pixel)
	{
		case 8:
		{
			*pen_8 = color;
			break;
		}
		case 16:
		{
			/* 565 */
			//因为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;
		}
	}
}

5)清屏: 全部设为黑色

	memset(fbmem, 0, screen_size);
发布了56 篇原创文章 · 获赞 3 · 访问量 2358

猜你喜欢

转载自blog.csdn.net/qq_40674996/article/details/98956340