2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

上章-学习了数码相框的框架分析(1)

本章主要内容如下:

  • 1)熟悉ASCII/GB2312/Unicode编码
  • 2)写应用程序,使LCD显示汉字和字符

大家都知道,数据传输的是二进制,而字符和汉字却有各种各样的,所以便通过二进制将字符和汉字编成一个字符集(charset).

1.而字符集(charset)又经历3个阶段

ASCII码

最早的计算机采用ASCII码,一个字节便包括了英文数字这些符号

GB2312编码

由于不支持中文,那时候的常用汉字就有6763个,所以中国人发明了GB2312(GB国标),汉字为2个字节,与ascll码兼容,后来又继续扩展汉字,所以又有了GBK编码.

GB2312编码是将字符进行一个分区处理,共有94个区,每个区有94个位,所以区位码范围为0000~9393

汉字分为了一级汉字(常用)和二级汉字(不常用).

其中GB2312分区表如下图所示:

2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

比如“啊”,位于第16区第1位,也就是.

然后分别在区和位上加0xA1,便转换为了GB2312编码(编码从0xA1A1开始是为了兼容英文字符,)

所以“啊”的GB2312编码为: 0xB0A1

15(区)+0xA1=0xB0

00(位)+0xA1=0xA1

2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

这种编码方式仅仅在中国行的通,若去浏览繁体字或日文时,便会出现乱码,因为繁体字使用的是Big5编码,日文则需要安装日本的Shift_JIS 编码才行.

在不同的国家的编码标准都不同,所以在PC里,使用ANSI编码来代表它们,比如中文PC里,ANSI编码代表GBK编码.

Unicode编码(统一世界所有符号)

包括中、日、韩、英文等字符,格式有utf-32、utf-16、utf-8

在PC,Unicode一般代表utf-16,而utf-8是单独列出来的,

utf-32

指每个字符都采用4个字节(32位),缺点在于浪费空间,比如:a=0x0000 0061,啊=0x0000554A.

utf-16(错一个字节,则整个乱码)

每个字符的长度为2字节或4字节,常用的都是2字节(包括汉字等). 比如: a=0x0061,啊=0x554A.

utf-8(容错能力高)

指每个字符的长度为1~4个字节,越常用的字符,字节越短,比如:a=0x61,啊=0xE5958A

可以通过utf-16转换过来,高4位表示有多少个字节,然后剩下的每个字节的高2位都为10(表示只有一个字节),剩下的值加起来就是utf-16编码,如下图所示:

2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

如果是unicode转utf-8,则对应代码为:

int UnicodeToUtf8( unsigned short* src, unsigned short* putf8)
{
int len = ;
while (*src)
{
if (*src < 0x80) //one byte
{
putf8[len++] = *src;
}
else if (*src < 0x800) //two byte
{
putf8[len++] = 0xC0 | (*src >> );
putf8[len++] = 0x80 | ((*src) & 0x3F);
}
else
{
putf8[len++] = 0xE0 | (*src >> );    //获取src高4位
putf8[len++] = 0x80 | ((*src >> ) & 0x3F); //获取src 第6位,长度为3f(6位)
putf8[len++] = 0x80 | (*src & 0x3F);    //获取src低6位
}
src++;
}
putf8[len] = ;
return len;
} int main()
{
unsigned short Unicode[]={0x4e2d};  //中的unicode码 unsigned short utf[]={,,,}; UnicodeToUtf8(Unicode,utf); for(int i=;i<;i++)
printf(" %x ",utf[i]);
return ; }

一般一个文件的开头会有标志,通过十六进制编辑文件,便可以看到

EF BB BF 表示utf-8

FE FF 表示utf-16大端(大开头,比如a=00 61)

FF FE 表示utf-16小端(小开头,比如a=61 00)

没有前缀 表示ANSI格式

2.所以文件格式不同,执行的结果也不同

2.1我们下面代码为例:

#include <stdio.h>
int main(int argc,char **argv)
{
int i=;
unsigned char s[]="abc中"; while(s[i])
{
printf("%02x ",s[i]);
i++;
}
printf("\n");
return ;
}

然后在PC上,另存为ANSI.c和UTF-8.c,编码分别选择ANSI(GBK编码)和UTF-8

2.2然后拖到linux里编译运行:

gcc -o ANSI ANSI.c
gcc -o UTF- UTF-.c

2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

3.如何解决文件格式不同,编码也不同的问题?

我们可以指定字符集(charset), 强制使它以什么编码格式解析

man gcc                //查看gcc使用手册
/charset //搜索charset相关字

找到:

-finput-charset=charset  //表示源文件的编码方式, 默认以UTF-8来解析
-fexec-charset=charset //表示可执行程序里的字时候以什么编码方式来表示,默认是UTF-8

3.1指定字符集(charset)

gcc -finput-charset=GBK  -fexec-charset=UTF-    -o  utf-8_2   ANSI.c

2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

如上图所示,通过参数,告诉gcc该文件是GBK编码,需要转换为UTF-8编码后,再编译,便解决了文件格式问题.

4.LCD显示文字

4.1首先LCD设备fb0的file_operations是fb_fops(位于fbmem.c)

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

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

而ioctl成员是do_fb_ioctl()函数,我们需要通过ioctl()获取LCD驱动的数据:

FBIOGET_VSCREENINFO:获取fb_info-> var成员(可变信息:xy分辨率,像素位数等)

FBIOGET_FSCREENINFO:获取fb_info-> fix成员(固定信息:缓存地址,每行字节数,)

4.2 mmap

mamp()函数:申请一段用户空间的内存区域,并映射到内核空间某个内存区域.

接下来,我们便申请一块内存映射到fb0文件,然后应用程序直接向内存写数据,即可直接写入fb0文件(显存地址)

man 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即可.

int munmap(void* start,size_t length);

返回值:成功返回0,失败返回-1,并设置errno值

start:要取消映射的内存起始地址

length: 映射地址的大小

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

4.3 ASCII码字库文件使用

在si里搜索font,找到内核有个font_8x16.c文件(位于drivers/video/console)

如下图所示,找到8*16的点阵存在fontdata_8x16[]数组里:

2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

我们以0x41(A)为例,找到该点阵信息为:

2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

可以看到一个ASCII代表了16字节.所以0x41(A)位于0x41*16~0x41*16+15

后面我们直接将fontdata_8x16[]数组拷贝到应用程序里,用来显示ASCII

4.3 HZK16汉字库文件使用

1)HZK16描述

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

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

2)然后还要将编码转为点阵码,我们以中为例:

的GBK编码为D6 D0

转为分区表(每字节减去A1): 35 2F

所以中的点阵位于:

(35*94+2F)*32~(35*94+2F)*32+31     //94: 每个区占据94位   32:每个汉字点阵为32字节

注意: 2440的LCD是RGB565的.所以点阵每一位,又是一个16位的数据地址

5.接下来开始写应用程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>

unsigned char *fbmem;
unsigned char *hzkmem; struct fb_var_screeninfo fb_var;
struct fb_fix_screeninfo fb_fix; unsigned int screensize; #define FONTDATAMAX 4096
static const unsigned char fontdata_8x16[FONTDATAMAX] = {
//ASCII码点阵太长,省略...
}; /*rgb565*/
void pixel_show(int x,int y, unsigned int color)
{
unsigned int red,green,blue;
switch(fb_var.bits_per_pixel) //rgb 像素
{
case :
{
unsigned int *addr=(unsigned int *)fbmem+(fb_var.xres*y+x);
*addr=color;
break;
}
case :
{
unsigned int *addr=(unsigned int *)fbmem+(fb_var.xres*y+x);
*addr=color;
break;
}
case : //将RGB888 转为RGB565
{
unsigned short *addr=(unsigned short *)fbmem+(fb_var.xres*y+x);
red = (color >> ) & 0xff;
green = (color >> ) & 0xff;
blue = (color >> ) & 0xff;
color = ((red >> ) << ) | ((green >> ) << ) | (blue >> );
*addr = color;
break;
}
case :
{
unsigned char *addr=(unsigned char *)fbmem+(fb_var.xres*y+x);
*addr = (unsigned char)color;
break;
}
default:
{
printf("can't surport %dbpp \n",fb_var.bits_per_pixel);
break;
}
}
} /*显示ascii码*/
void lcd_put_char(int x,int y, unsigned char s)
{
unsigned char *index=(unsigned char *)&fontdata_8x16[s*];
unsigned char i,j;
for(i=;i<;i++) //8*16
for(j=;j<;j++)
{
//从高位到低
if(index[i]&(<<(-j))) //亮
pixel_show(x+j,y+i, 0xffffff); //白色 else //灭
pixel_show(x+j,y+i, 0x0); //黑色
}
} /*显示GBK码*/
void lcd_put_chinese(int x,int y, unsigned char *s)
{
unsigned char i,j,k; //将编码转为区码
unsigned int index=(s[]-0xA1)*+(s[]-0xA1); //转为点阵码(每个汉字32字节)
unsigned char *dots=hzkmem+index*; for(i=;i<;i++) //16*16
for(k=;k<;k++)
for(j=;j<;j++)
{
if((dots[i*+k]>>(-j))&0X01) //亮
pixel_show(x+*k+j,y+i, 0xffffff); //白色 else //灭
pixel_show(x+*k+j,y+i, 0x0); //黑色
}
} void lcd_put(int x,int y, unsigned char *s)
{
while(*s)
{
if(*s<0xA1) //ASCII码8*16
{
printf("ASCII %x \r\n",*s );
lcd_put_char(x,y,*s);
s+=;
x+=;
}
else //GB2313 16*16
{
printf("GBK %x %x\r\n",*s, *(s+));
lcd_put_chinese(x,y,s);
s+=;
x+=;
}
}
} int main(int argc,char **argv)
{
int fd_fb,fd_hzk;
struct stat hzk_start; //HZK16文件信息 unsigned char s[]="abc 中国chinese";

fd_hzk=open("HZK16",O_RDONLY);
if(fd_hzk<)
{
printf("can't open HZK16 \n");
return ;
} if(fstat(fd_hzk,&hzk_start)<) //获取HZK16文件信息
{
printf("can't get fstart \n");
return ;
} hzkmem =(unsigned char *)mmap(NULL,hzk_start.st_size, PROT_READ,MAP_SHARED,fd_hzk, );
//映射HZK16文件
if(!hzkmem)
{
printf("can't map HZK16 \n");
return ;
} fd_fb=open("/dev/fb0", O_RDWR);
if(fd_fb<)
{
printf("can't open /dev/fb0 \n");
return ;
}
if(ioctl(fd_fb,FBIOGET_VSCREENINFO,&fb_var)<)
{
printf("can't get var \n");
return ;
}
if(ioctl(fd_fb,FBIOGET_FSCREENINFO,&fb_fix)<)
{
printf("can't get fix \n");
return ;
}
screensize=fb_var.xres*fb_var.yres*(fb_var.bits_per_pixel/); //显存大小
fbmem =(unsigned char *)mmap(NULL,screensize, PROT_READ|PROT_WRITE,MAP_SHARED,fd_fb, );
//映射fb0
if(!fbmem)
{
printf("can't map /dev/fb0 \n");
return ;
} memset(fbmem, , screensize); //清屏黑色 /*显示数据*/
lcd_put(,fb_var.yres/,s); munmap(hzkmem,hzk_start.st_size);
munmap(fbmem,screensize);
return ;
}

6.编译运行

6.1编译代码后,然后使内核支持LCD启动

make menuconfig

进入Device Drivers -> Graphics support -> Support for frame buffer devices

<*>   S3C2410 LCD framebuffer support         //编译进内核
<*> Silicon Motion SM501 framebuffer support //编译进内核

并修改linux-3.4.2/drivers/video/Makefile

#obj-$(CONFIG_FB_S3C2410)         += s3c2410fb.o
obj-$(CONFIG_FB_S3C2410) += 9th_lcd.o //添加以前写的lcd驱动

6.2编译并启动内核后,运行程序:

2.数码相框-编码(ASCII/GB2312/Unicode)介绍,并使LCD显示汉字字符(2)

下章学习:  3.数码相框-通过freetype库实现矢量显示

上一篇:day01-day04总结- Python 数据类型及其用法


下一篇:Python 批量翻译 使用有道api;