武汉轻工大学
数学与计算机学院
移动设备课程设计
设计题目:ARM开发板上通过转换函数实现汉字显示
专 业 计算计科学与技术
班 级 1802
学 号 1801070108
姓 名 毛佳文
指导教师 易逵
2021 年 7 月5 日
开发板:iTOP-4412
操作系统:Windows 10
虚拟机:VMware Workstation Pro 15 Ubuntu12.04.2_V2.0
数据传输工具:Xshell 7
驱动软件:驱动精灵
ARM开发板上实现汉字显示
当前流行的字符编码格式有:US-ASCII、ISO-8859-1、UTF-8、UTF-16BE、UTF-16LE、UTF-16、GBK、GB2312等,其中GBK、GB2312是专门处理中文编码的。而libiconv是一个常用的编码转换库,支持常用的多种编码之间的转换。在Linux下,工具链gcc有专门的libiconv库,所有不用移植,但在arm-linux下就没那么幸运了,需要我们额外移植libiconv库,不过,过程还是比较简单的。
字符编码转换libiconv库介绍
Libiconv是一个常用的编码转换库,支持常用的多种编码之间的转换。
iconv_open是打开一个编码流,类似于打开一个编码管道(通道),出错则返回-1;
iconv用于具体输入的转换,如果出错,则返回-1,否则返回 0;
iconv_close是关闭该管道(通道)。
如果对于大量需要转换的编码,上述函数covert不适合该方式,一是内存的限制不能一次调用,二是若分多次调用会频繁打开一个编码管道(通道),导致资源浪费,最好的办法还是拆开该函数根据情况使用。
存在的问题
开发环境为ubuntu11.10,开发板为ARM开发板,交叉编译器版本为arm-linux-4.4.3.相同的C源程序,在ubuntu11.10上能够正常执行,而在ARM开发板则不能正常执行,调用iconv_open(“utf-8”, “gb2312”)返回失败,错误信息为“Invalid argument”.经过查询资料得知iconv相关函数为libc中的函数,初步分析得出结论为有可能是libc版本中
iconv相关函数的版本不同造成的,因此要更新iconv相关函数。
更新iconv相关函数有两种方法:
第一,更新libc库;
第二,更新libiconv库。
第一种方法更新libc库比较麻烦,因为我们用的是编译好的交叉编译器,这种方法需要重新编译生成交叉编译器,并且也需要使用新编译生成的交叉编译工具重新编译应用程序,因此本方法代价太大,因此我们采用第二种方法。
GBK对汉字的编码
GBK的中文编码是双字节来表示的,英文编码是用ASCII码表示的,既用单字节表示。但GBK编码表中也有英文字符的双字节表示形式,所以英文字母可以有2种GBK表示方式。为区分中文,将其最高位都定成1。英文单字节最高位都为0。当用GBK解码时,若高字节最高位为0,则用ASCII码表解码;若高字节最高位为1,则用GBK编码表解码。
字符有一字节和双字节编码,00–7F范围内是第一个字节,和ASCII保持一致,此范围内严格上说有96个文字和32个控制符号。之后的双字节中,前一字节是双字节的第一位。总体上说第一字节的范围是81–FE(也就是不含80和FF),第二字节的一部分领域在40–7E,其他领域在80–FE。
1.搭建实验环境
确保iTop-4412已经搭载了最小Linux子系统。
2.更新libiconv动态链接库
下载最新库文件并且编译安装: libiconv-1.14.tar.gz
tar-vxf libiconv-1.14.tar.gz //解压缩libiconv-1.14.tar.gz
cd jpeg-9d
./configure--host=arm-none-linux-gnueabi //配置交叉编译器
make //编译
make install //安装
将preloadable_libiconv.so 这个动态库安装在开发板的根目录下的 /lib文件夹下:
图1 libiconv编译后的lib库文件
图2 开发板下的 lib库文件
从虚拟机中将文件preloadable_libiconv.so cp iTopeet-4412/lib下
1,编写实验代码
文件结构如下:
图3 代码结构图
其中ascii.h、HZK16均为资源文件,而mode_print_CH_String.c才是最关键的文件(实现了本实验的全部功能:输入一串中文字符就可以在开发板屏幕上显示)
具体代码如下:
(1)ascii.h
图4 ascii.h源码部分截图
其中ascii_bitmaps[]的每16个数据元就存放一个ASCII字符的编码信息(前面有大量无法显示的字符,故为0x00)
(2)mode_print_CH_String.c
#include #include #include #include #include #include #include #include ”ascii.h“ #include #include #include int *fbmem;//数据帧缓存 unsigned char *hzkmem;汉字字符串缓存 int w, h; unsigned char *string = ”张多鱼国家排核废水 #define OUTLEN 128 void fb_point(int x, int y, int color)//画点函数 { fbmem[y * w + x] = color; } void fb_hline(int x1, int x2, int y, int color)//画线函数 { int i; for(i=x1;i fb_point(i, y, color); } void show_ascii(int x, int y, unsigned char c, int color) { /* 得到字符点阵在数组中的起始位置 */ // unsigned char *dots =(unsigned char *)&ascii_bitmaps[c*16]; unsigned char *dots = &ascii_bitmaps[c*16]; int i,j; unsigned char byte; for(i = 0;i < 16;i ++){ /* 取出一个字节 */ byte = dots[i]; for(j = 7;j >= 0;j--) { if(byte &(1 << j)){ /* 传入参数的值是lcd要显示的起点坐标 这里每个点的坐标每描一个点要移动一次 */ fb_point(x + 7-j, y + i, color); } } } } void show_chinese(int x, int y, unsigned char *str,int d,int w,int color) //d w 分别是放大倍数和点阵密度倍数,两者需保持一致 可放大字体倍数 { unsigned int area = str[0]-0xa1; unsigned int where = str[1]-0xa1; unsigned char *dots = hzkmem +(area * 94 + where)* 32; unsigned char byte; // int i,j,k; int i, j, b,m,n; 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< /* show 加粗显示*/ for(m=0;m< d;m++){ for(n=0;n fb_point((x+(7-b+j*8)*w)+m,(y+i*w)+n, color); }//for n }//for m } //if } } }/ x+=16*w+20;//按照行来显示,中间空20个pixs y+=0; } int main() { int i, j, k; int fd, fd_hzk; struct fb_var_screeninfo fb_var; struct stat hzk_stat; iconv_t cd; int inbuf_len = strlen(string); char outbuf[OUTLEN]; char *pin = string; char *pout = &outbuf[0];//用“pout=&outbuf” 会引发SIGSERV信号,导致段错误 int outbuf_len = OUTLEN; fd = open(“/dev/fb0”, O_RDWR);//打开缓冲设备 if(fd < 0){ printf(“open errorn”); return-1; } fd_hzk = open(“HZK16”, O_RDONLY);//打开字库文件 if(fd_hzk < 0) { printf(“cant open HZK16n”); return-1; } /* 得到这个件的统计信息当然也包含了大小 */ if(fstat(fd_hzk, &hzk_stat)) { printf(“cant get hzk_stat!n ”); return-1; } if(ioctl(fd, FBIOGET_VSCREENINFO, &fb_var))/* 获得可变信息 */ { /* 正常获得信息的话 ioctl 会返回0 如果返回值不为0时表示出错 */ printf(“cant get var!n”); return-1; } w = fb_var.xres; h = fb_var.yres; k = fb_var.bits_per_pixel; printf(“framebuffer: %d * %d * %dn”, w, h, k); fbmem = mmap(0, w*h*4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); hzkmem =(unsigned char *)mmap(NULL, hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk, 0); memset(outbuf, 0, OUTLEN);//清空输出缓存,否则会有意外结果的 printf(“Originial Data:n”);//打印转换前的一些参数和信息,以进行比较 printf(“tpin str: %s, outbuf str:%sn”, pin, outbuf); printf(“tinbuf_len=%d, outbuf_len=%dn”, inbuf_len, outbuf_len); printf(“tstrlen(outbuf)= %dn”, strlen(outbuf)); cd = iconv_open(“GB2312”, “UTF-8”); if(cd == 0) return EXIT_FAILURE; int count = iconv(cd, &pin, &inbuf_len, &pout, &outbuf_len); printf(“iconv count : %dn”, count);//观察iconv返回值,理解不可逆转换含义 iconv_close(cd); printf(“After Converted Data:n”);//注意发生变化的变量 printf(“tpin str: %s, gb2312 str:%sn”, pin, outbuf); printf(“tinbuf_len=%d, outbuf_len=%dn”, inbuf_len, outbuf_len); printf(“tstrlen(outbuf)= %dn”, strlen(outbuf)); for(i = 0;i < strlen(outbuf);i += 2) { show_chinese(100+32*i, 300, outbuf+i,2,2, 0xffff00); //使用HZK16字库显示GB2312编码的中文点阵 } return 0; } |
对该文件进行交叉编译:
arm-none-linux-gnueabi-gcc mode_print_CH_String.c
2.配置preloadable_libiconv.so的环境变量
在超级终端在输入:
export LD_PRELOAD=/lib/preloadable_libiconv.so
五、实验运行
挂载
图5 挂载(将带有运行程序的TF卡挂载到/mnt/disk)
运行程序:
图6 运行程序:./mode_print_CH_String
运行结果截图
经过本学期的移动设备开发学习,我了解到嵌入式Linux开发的基本流程,对xshell软件等的使用有了了解,发现自己知识水平有待提高,在今后的学习里,我会更加地努力,提升自我的知识素养。