武漢輕工大學(xué)
數(shù)學(xué)與計(jì)算機(jī)學(xué)院
移動(dòng)設(shè)備課程設(shè)計(jì)
設(shè)計(jì)題目:ARM開發(fā)板上通過轉(zhuǎn)換函數(shù)實(shí)現(xiàn)漢字顯示
專 業(yè) 計(jì)算計(jì)科學(xué)與技術(shù)
班 級(jí) 1802
學(xué) 號(hào) 1801070108
姓 名 毛佳文
指導(dǎo)教師 易逵
2021 年 7 月5 日
開發(fā)板:iTOP-4412
操作系統(tǒng):Windows 10
虛擬機(jī):VMware Workstation Pro 15 Ubuntu12.04.2_V2.0
數(shù)據(jù)傳輸工具:Xshell 7
驅(qū)動(dòng)軟件:驅(qū)動(dòng)精靈
ARM開發(fā)板上實(shí)現(xiàn)漢字顯示
當(dāng)前流行的字符編碼格式有:US-ASCII、ISO-8859-1、UTF-8、UTF-16BE、UTF-16LE、UTF-16、GBK、GB2312等,其中GBK、GB2312是專門處理中文編碼的。而libiconv是一個(gè)常用的編碼轉(zhuǎn)換庫,支持常用的多種編碼之間的轉(zhuǎn)換。在Linux下,工具鏈gcc有專門的libiconv庫,所有不用移植,但在arm-linux下就沒那么幸運(yùn)了,需要我們額外移植libiconv庫,不過,過程還是比較簡(jiǎn)單的。
字符編碼轉(zhuǎn)換libiconv庫介紹
Libiconv是一個(gè)常用的編碼轉(zhuǎn)換庫,支持常用的多種編碼之間的轉(zhuǎn)換。
iconv_open是打開一個(gè)編碼流,類似于打開一個(gè)編碼管道(通道),出錯(cuò)則返回-1;
iconv用于具體輸入的轉(zhuǎn)換,如果出錯(cuò),則返回-1,否則返回 0;
iconv_close是關(guān)閉該管道(通道)。
如果對(duì)于大量需要轉(zhuǎn)換的編碼,上述函數(shù)covert不適合該方式,一是內(nèi)存的限制不能一次調(diào)用,二是若分多次調(diào)用會(huì)頻繁打開一個(gè)編碼管道(通道),導(dǎo)致資源浪費(fèi),最好的辦法還是拆開該函數(shù)根據(jù)情況使用。
存在的問題
開發(fā)環(huán)境為ubuntu11.10,開發(fā)板為ARM開發(fā)板,交叉編譯器版本為arm-linux-4.4.3.相同的C源程序,在ubuntu11.10上能夠正常執(zhí)行,而在ARM開發(fā)板則不能正常執(zhí)行,調(diào)用iconv_open(“utf-8”, “gb2312”)返回失敗,錯(cuò)誤信息為“Invalid argument”.經(jīng)過查詢資料得知iconv相關(guān)函數(shù)為libc中的函數(shù),初步分析得出結(jié)論為有可能是libc版本中
iconv相關(guān)函數(shù)的版本不同造成的,因此要更新iconv相關(guān)函數(shù)。
更新iconv相關(guān)函數(shù)有兩種方法:
第一,更新libc庫;
第二,更新libiconv庫。
第一種方法更新libc庫比較麻煩,因?yàn)槲覀冇玫氖蔷幾g好的交叉編譯器,這種方法需要重新編譯生成交叉編譯器,并且也需要使用新編譯生成的交叉編譯工具重新編譯應(yīng)用程序,因此本方法代價(jià)太大,因此我們采用第二種方法。
GBK對(duì)漢字的編碼
GBK的中文編碼是雙字節(jié)來表示的,英文編碼是用ASCII碼表示的,既用單字節(jié)表示。但GBK編碼表中也有英文字符的雙字節(jié)表示形式,所以英文字母可以有2種GBK表示方式。為區(qū)分中文,將其最高位都定成1。英文單字節(jié)最高位都為0。當(dāng)用GBK解碼時(shí),若高字節(jié)最高位為0,則用ASCII碼表解碼;若高字節(jié)最高位為1,則用GBK編碼表解碼。
字符有一字節(jié)和雙字節(jié)編碼,00–7F范圍內(nèi)是第一個(gè)字節(jié),和ASCII保持一致,此范圍內(nèi)嚴(yán)格上說有96個(gè)文字和32個(gè)控制符號(hào)。之后的雙字節(jié)中,前一字節(jié)是雙字節(jié)的第一位??傮w上說第一字節(jié)的范圍是81–FE(也就是不含80和FF),第二字節(jié)的一部分領(lǐng)域在40–7E,其他領(lǐng)域在80–FE。
1.搭建實(shí)驗(yàn)環(huán)境
確保iTop-4412已經(jīng)搭載了最小Linux子系統(tǒng)。
2.更新libiconv動(dòng)態(tài)鏈接庫
下載最新庫文件并且編譯安裝: 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 這個(gè)動(dòng)態(tài)庫安裝在開發(fā)板的根目錄下的 /lib文件夾下:
圖1 libiconv編譯后的lib庫文件
圖2 開發(fā)板下的 lib庫文件
從虛擬機(jī)中將文件preloadable_libiconv.so cp iTopeet-4412/lib下
1,編寫實(shí)驗(yàn)代碼
文件結(jié)構(gòu)如下:
圖3 代碼結(jié)構(gòu)圖
其中ascii.h、HZK16均為資源文件,而mode_print_CH_String.c才是最關(guān)鍵的文件(實(shí)現(xiàn)了本實(shí)驗(yàn)的全部功能:輸入一串中文字符就可以在開發(fā)板屏幕上顯示)
具體代碼如下:
(1)ascii.h
圖4 ascii.h源碼部分截圖
其中ascii_bitmaps[]的每16個(gè)數(shù)據(jù)元就存放一個(gè)ASCII字符的編碼信息(前面有大量無法顯示的字符,故為0x00)
(2)mode_print_CH_String.c
#include #include #include #include #include #include #include #include ”ascii.h“ #include #include #include int *fbmem;//數(shù)據(jù)幀緩存 unsigned char *hzkmem;漢字字符串緩存 int w, h; unsigned char *string = ”張多魚國(guó)家排核廢水 #define OUTLEN 128 void fb_point(int x, int y, int color)//畫點(diǎn)函數(shù) { fbmem[y * w + x] = color; } void fb_hline(int x1, int x2, int y, int color)//畫線函數(shù) { int i; for(i=x1;i fb_point(i, y, color); } void show_ascii(int x, int y, unsigned char c, int color) { /* 得到字符點(diǎn)陣在數(shù)組中的起始位置 */ // 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 ++){ /* 取出一個(gè)字節(jié) */ byte = dots[i]; for(j = 7;j >= 0;j--) { if(byte &(1 << j)){ /* 傳入?yún)?shù)的值是lcd要顯示的起點(diǎn)坐標(biāo) 這里每個(gè)點(diǎn)的坐標(biāo)每描一個(gè)點(diǎn)要移動(dòng)一次 */ 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 分別是放大倍數(shù)和點(diǎn)陣密度倍數(shù),兩者需保持一致 可放大字體倍數(shù) { 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個(gè)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” 會(huì)引發(fā)SIGSERV信號(hào),導(dǎo)致段錯(cuò)誤 int outbuf_len = OUTLEN; fd = open(“/dev/fb0”, O_RDWR);//打開緩沖設(shè)備 if(fd < 0){ printf(“open errorn”); return-1; } fd_hzk = open(“HZK16”, O_RDONLY);//打開字庫文件 if(fd_hzk < 0) { printf(“cant open HZK16n”); return-1; } /* 得到這個(gè)件的統(tǒng)計(jì)信息當(dāng)然也包含了大小 */ if(fstat(fd_hzk, &hzk_stat)) { printf(“cant get hzk_stat!n ”); return-1; } if(ioctl(fd, FBIOGET_VSCREENINFO, &fb_var))/* 獲得可變信息 */ { /* 正常獲得信息的話 ioctl 會(huì)返回0 如果返回值不為0時(shí)表示出錯(cuò) */ 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);//清空輸出緩存,否則會(huì)有意外結(jié)果的 printf(“Originial Data:n”);//打印轉(zhuǎn)換前的一些參數(shù)和信息,以進(jì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返回值,理解不可逆轉(zhuǎn)換含義 iconv_close(cd); printf(“After Converted Data:n”);//注意發(fā)生變化的變量 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編碼的中文點(diǎn)陣 } return 0; } |
對(duì)該文件進(jìn)行交叉編譯:
arm-none-linux-gnueabi-gcc mode_print_CH_String.c
2.配置preloadable_libiconv.so的環(huán)境變量
在超級(jí)終端在輸入:
export LD_PRELOAD=/lib/preloadable_libiconv.so
五、實(shí)驗(yàn)運(yùn)行
?掛載
圖5 掛載(將帶有運(yùn)行程序的TF卡掛載到/mnt/disk)
?運(yùn)行程序:
圖6 運(yùn)行程序:./mode_print_CH_String
?運(yùn)行結(jié)果截圖
經(jīng)過本學(xué)期的移動(dòng)設(shè)備開發(fā)學(xué)習(xí),我了解到嵌入式Linux開發(fā)的基本流程,對(duì)xshell軟件等的使用有了了解,發(fā)現(xiàn)自己知識(shí)水平有待提高,在今后的學(xué)習(xí)里,我會(huì)更加地努力,提升自我的知識(shí)素養(yǎng)。