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分區表如下圖所示:

 

比如“啊”,位於第16區第1位,也就是1500.

然后分別在區和位上加0xA1,便轉換為了GB2312編碼(編碼從0xA1A1開始是為了兼容英文字符,)

所以“啊”的GB2312編碼為: 0xB0A1

15(區)+0xA1=0xB0

00(位)+0xA1=0xA1

 

這種編碼方式僅僅在中國行的通,若去瀏覽繁體字或日文時,便會出現亂碼,因為繁體字使用的是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編碼,如下圖所示:

 

 

 

 如果是unicode轉utf-8,則對應代碼為:

int UnicodeToUtf8( unsigned short* src, unsigned short* putf8)
{
    int len = 0;
    while (*src)
    {
        if (*src < 0x80) //one byte
        {
            putf8[len++] = *src;
        }
        else if (*src < 0x800) //two byte
        {
            putf8[len++] = 0xC0 | (*src >> 6);  
            putf8[len++] = 0x80 | ((*src) & 0x3F);
        }
        else
        {
            putf8[len++] = 0xE0 | (*src >> 12);    //獲取src高4位
            putf8[len++] = 0x80 | ((*src >> 6) & 0x3F);  //獲取src 第6位,長度為3f(6位)
            putf8[len++] = 0x80 | (*src & 0x3F);    //獲取src低6位
        }
        src++;
    }
    putf8[len] = 0;
    return len;
}
 
int main()
{
unsigned short Unicode[2]={0x4e2d};  //中的unicode碼

unsigned short utf[4]={0,0,0,0};
    
UnicodeToUtf8(Unicode,utf); 

for(int i=0;i<4;i++)
 printf(" %x ",utf[i]);
return 0;
    
}

 

一般一個文件的開頭會有標志,通過十六進制編輯文件,便可以看到

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=0;
     unsigned char s[]="abc中";

     while(s[i])
    {
       printf("%02x ",s[i]);
       i++;
    }
     printf("\n");
     return 0;
}

然后在PC上,另存為ANSI.c和UTF-8.c,編碼分別選擇ANSI(GBK編碼)UTF-8

2.2然后拖到linux里編譯運行:

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

 

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-8    -o  utf-8_2   ANSI.c

 

如上圖所示,通過參數,告訴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[]數組里:

 

我們以0x41(A)為例,找到該點陣信息為:

 

可以看到一個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 32: { unsigned int *addr=(unsigned int *)fbmem+(fb_var.xres*y+x); *addr=color; break; } case 24: { unsigned int *addr=(unsigned int *)fbmem+(fb_var.xres*y+x); *addr=color; break; } case 16: //將RGB888 轉為RGB565 { unsigned short *addr=(unsigned short *)fbmem+(fb_var.xres*y+x); red = (color >> 16) & 0xff; green = (color >> 8) & 0xff; blue = (color >> 0) & 0xff; color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); *addr = color; break; } case 8: { 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*16]; unsigned char i,j; for(i=0;i<16;i++) //8*16 for(j=0;j<8;j++) { //從高位到低 if(index[i]&(1<<(7-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[0]-0xA1)*94+(s[1]-0xA1); //轉為點陣碼(每個漢字32字節) unsigned char *dots=hzkmem+index*32; for(i=0;i<16;i++) //16*16 for(k=0;k<2;k++) for(j=0;j<8;j++) { if((dots[i*2+k]>>(7-j))&0X01) // pixel_show(x+8*k+j,y+i, 0xffffff); //白色 else // pixel_show(x+8*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+=1; x+=8; } else //GB2313 16*16 { printf("GBK %x %x\r\n",*s, *(s+1)); lcd_put_chinese(x,y,s); s+=2; x+=16; } } } 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<0) { printf("can't open HZK16 \n"); return 0; } if(fstat(fd_hzk,&hzk_start)<0) //獲取HZK16文件信息 { printf("can't get fstart \n"); return 0; } hzkmem =(unsigned char *)mmap(NULL,hzk_start.st_size, PROT_READ,MAP_SHARED,fd_hzk, 0);
//映射HZK16文件 if(!hzkmem) { printf("can't map HZK16 \n"); return 0; } fd_fb=open("/dev/fb0", O_RDWR); if(fd_fb<0) { printf("can't open /dev/fb0 \n"); return 0; } if(ioctl(fd_fb,FBIOGET_VSCREENINFO,&fb_var)<0) { printf("can't get var \n"); return 0; } if(ioctl(fd_fb,FBIOGET_FSCREENINFO,&fb_fix)<0) { printf("can't get fix \n"); return 0; } screensize=fb_var.xres*fb_var.yres*(fb_var.bits_per_pixel/8); //顯存大小 fbmem =(unsigned char *)mmap(NULL,screensize, PROT_READ|PROT_WRITE,MAP_SHARED,fd_fb, 0);
//映射fb0 if(!fbmem) { printf("can't map /dev/fb0 \n"); return 0; } memset(fbmem, 0, screensize); //清屏黑色 /*顯示數據*/ lcd_put(0,fb_var.yres/2,s); munmap(hzkmem,hzk_start.st_size); munmap(fbmem,screensize); return 0; }

 

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編譯並啟動內核后,運行程序:

 

 

下章學習:  3.數碼相框-通過freetype庫實現矢量顯示

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM