02.点阵显示中文汉字

1.环境

  1. 官方给的内核文件(linux3.4)和根文件系统、linux2.6和配套根文件系统
  2. 不同的内核文件编译时用的交叉编译器不同,linux3.4用4.x的编译器,linux2.6用3.x的编译器,否则不能运行
  3. !!注意:在写代码时要用GB2312编码写,否则程序中的"中"是UTF-8,显示出来就是"涓"字

2.架构思路

  • 打开屏所对应的文件,调用fctl函数使可以用地址来操作文件
  • 获得屏的一些信息(如:屏幕一个像素点占用的位数,屏的分辨率等等)
  • 打开汉字库文件,调用fctl函数使可以用地址来操作文件
  • 获得汉字库文件的一些信息(如大小)
  • 清屏
  • 写ascii字符和汉字

3.知识点

  • LCD设备fb0的file_operations是fb_fops(位于fbmem.c)

    fb_fops的write成员是fb_write()函数.

    发现write()函数直接是对显存地址写数据, 所以使用echo “hello”> /dev/fb0时会直接出现乱码(没有点阵信息)

  • ioctl函数

octl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的参数个数如下:int ioctl(int fd, int cmd, …);其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。

ioctl获取屏幕的信息:ioctl(fd_fb , FBIOGET_FSCREENINFO, &fix)
FBIOGET_VSCREENINFO:获取fb_info-> var成员(可变信息:xy分辨率,像素位数等)
FBIOGET_FSCREENINFO:获取fb_info-> fix成员(固定信息:缓存地址,每行字节数,)
  • mmap函数
    mmap()函数:申请一段用户空间的内存区域,并映射到内核空间某个内存区域.
    申请一块内存映射到某一个文件,然后应用程序直接向内存写数据,即可直接写入这个文件
    即:通过mmap函数可以实现用指针对文件操作
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);

返回值:失败返回-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即可.

  • ASCII码字库文件使用

在linux工程文件中搜索font,内核有很多font文件,我们使用font_8*16.c,这里面就存着字符对应的点阵信息。
在这里插入图片描述
数组的下标就是字符对应的ascii码

  • HZK16汉字库文件使用
    (1)HZK16是按分区表排列的点阵文件,由于每个汉字是2字节,每个字节的点阵是8x16,所以HZK16里的每个汉字点阵大。小:2816=32字节.
    (2)然后还要将编码转为点阵码,我们以"中"为例:
    “中” 的GBK编码为D6 D0.
    转为分区表(每字节减去A1): 35 2F
    所以中的点阵位于:(35*94+2F)32~(3594+2F)*32+31

注意!!

注意编写程序的软件的编码方式是什么,HZK16使用的是GBK的编码,所以如果编辑器的编码方式不是ANSI的,那么就需要在编译的时候加上

 gcc  -finput-charset=UTF-8  -fexec-charset=GBK  -o  ansi ansi.c 

附录:源码

#include <sys/mman.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#define FONTDATAMAX 4096
static const unsigned char fontdata_8x16[FONTDATAMAX] = {
... /*asic编码*/int fd_fb;     //屏的文件描述符
int fd_hzk16;  //汉字库文件的描述符
struct fb_var_screeninfo var;   //用来存屏的可变信息的,对应驱动 
struct fb_fix_screeninfo fix;   //用来存屏不可变信息的
int screen_size;  //屏幕大小
unsigned char *fbmem;  //屏的framebuffer

struct stat hzk_stat;  //汉字文件的属性
unsigned char *hzkmem; //汉字文件的指针,可以直接用这个指针去操作那个文件

unsigned int line_width; //一行有多少个字节
unsigned int pixel_width; //一行有多少个字节

//color : 0x00RRGGBB 
//板子驱动上是每个像素16位
void lcd_put_pixel(int x,int y, unsigned int color)
{
	unsigned char *pen_8 = fbmem + y*line_width + x*pixel_width; 
	unsigned short *pen_16;
	unsigned int *pen_32;

	unsigned int red,green,blue; //这里用int不用char,是因为在给color赋值时需要左移11位,如果是char可能会出错


	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*/
			red   = (color >> 16) & 0xff;
			green = (color >> 8) & 0xff;
			blue  = color &0xff;

			color = ((red >> 3)<<11) | ((green >> 2)<<5) | (blue >> 3);
			*pen_16 = color;
			break;
		}
		
		case 32: *pen_32 = color;break;

		default:printf("can't support %d bpp\n",var.bits_per_pixel);break; //不能支持这种像素
	}
}

void lcd_put_ascii(int x, int y, unsigned char c)
{
	//存放字符c的点阵的首地址
	//每个字符需要16个字节存放,所以第c个字符就是c*16
	unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
	int i,b;
	unsigned char byte;

	for(i = 0; i < 16; i++)
	{
		byte = *(dots+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); //黑色
			}
		}

	}
		
}

void lcd_put_chinese(int x,int y, unsigned char *str)
{
	unsigned int area   = str[0] - 0xA1; //这个汉字所在的区,汉字的第一个字符-0xa1
	unsigned int where  = str[1] - 0xA1; //这个汉字在这个区的什么地方,汉字的第二个字符-0xa1
	unsigned char *dots =(unsigned char *)(hzkmem + (area*94 + where)*32) ; //一个区有94个字符,一个字符占用32字节(16*16/8) 
	unsigned char byte;
	
	int i,j,b;
	for(i = 0; i < 16; i++)
	{
		for(j = 0; j < 2; j++)
		{
			byte = dots[i*2 + j];                //一个汉字占用两个8*16的地方,地址是横着算的,不是先竖着弄完一个8*16,然后再接着另外一个8*16
			for(b =7; b >=0; b--)
			{
				if(byte & (1<<b))
				{
					/*show*/
					lcd_put_pixel(x + j*8 + 7-b , y + i , 0xffffff);
				}
				else
				{
					lcd_put_pixel(x + j*8 + 7-b , y + i , 0);
				}
			}
		}
	}
}


int main(int argc,char **argv)
{
	
	unsigned char str[]="中";
	
	
	//屏的初始化
	fd_fb = open("/dev/fb0",O_RDWR);
	if(fd_fb <0)            //没打开
	{
		printf("can't open /dev/fb0\n");
	}

	//首先获得LCD的一些参数,如每个点用多少位来显示,XY的分辨率是多少
	//这些信息在写LCD驱动的时候,都放在了一个fb_info的结构体中
	//可以返回去驱看当时的驱动,在lcd_init函数里
	if(ioctl(fd_fb , FBIOGET_VSCREENINFO, &var))  //可变信息:分辨率、RBG谁在高位、每一个点需要几位数据
	{
		printf("can't get var\n");
		return -1;
	}
	
  	if(ioctl(fd_fb , FBIOGET_FSCREENINFO, &fix))  //不可变数据:显存长度、设备名、一行的长度等
  	{
		printf("can't get fix\n");
		return -1;
	}

	screen_size = var.xres * var.yres *var.bits_per_pixel /8;  //算出一屏有多大,x*y*每个像素的位数/8 (Bytes)

	
	pixel_width = var.bits_per_pixel / 8;   //每个像素占多少字节:每个像素占多少位/8
	line_width = var.xres * pixel_width;    //一行多少个像素乘以每个像素多少字节
	
	//这个是把文件当作指针来操作,就不用对文件描述符来操作了
	//对那个文件描述符操作,就是对framebuffer操作
	//void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
	//			 指定地址,不指定系统分配                 属性
	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");
	}


	//汉字库初始化
	fd_hzk16 = open("HZK16",O_RDONLY);
	if(fd_hzk16 < 0)
	{
		printf("can't open HZK16\n");
		return -1;
	}

	//int fstat(int fd, struct stat *buf); 获得这个文件的一些信息
	if(fstat(fd_hzk16 , &hzk_stat))
	{
		printf("can't get HZK16\n");
		return -1;
	}

	//同理字库文件
	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");
	}

	/*清屏:全部设为黑色*/
	memset(fbmem, 0 ,screen_size);
	
	lcd_put_ascii(var.xres/2 , var.yres/2 ,'A');
	printf("chines code: %02x %02x \n",str[0] , str[1]);
	lcd_put_chinese(var.xres/2+8,var.yres/2,str);
	
}
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值