天天看點

ILI9341的使用之【九】BG2312字庫1、ASCII碼回顧2、BG2312編碼規則3、點陣字庫結語:

前一篇把ASCII碼在LCD屏上的顯示的方法和驅動進行了詳細的說明。ASCII碼的顯示相對會簡單一些。關鍵需要了解ASCII碼的編碼規則和點陣資料的資料結構。剩下的就是要熟悉顯示裝置的指令。

在顯示裝置上顯示了英文字元後,自然會想到顯示漢字。漢字的顯示基本原理也對點陣的顯示,是以原理和ASCII 碼一樣。但因為漢字比ASCII128個字元多得多,是以漢字的處理上,在編碼方案,檢索,點陣的結構等方面會有很多不同。這篇就着文字顯示的方向展開來分析漢字國标字庫的相關問題。

《ILI9341的使用之【一】TFT-LCD原理(轉載)》

《ILI9341的使用之【二】ILI9341介紹》

《ILI9341的使用之【三】ILI9341系統通信接口模式操作詳解》

《ILI9341的使用之【四】RGB接口操作詳解》

《ILI9341的使用之【五】指令一》

《ILI9341的使用之【六】指令二》

《ILI9341的使用之【七】實體面闆案例-arduino 2.4inch TFT Touch Shield》

《ILI9341的使用之【八】ASCII字元顯示及驅動分析》

《ILI9341的使用之【九】BG2312字庫》

文章目錄

  • 1、ASCII碼回顧
  • 2、BG2312編碼規則
    • 2.1區位碼:
    • 2.2機内碼
    • 2.3區位的分類
    • 2.4編碼表舉例
    • 2.5 正常字元的操作
      • 判斷是否是GB2312
      • 判斷一個字元是ASCII字元還是中文字元
  • 3、點陣字庫
    • 3.1、16點陣字庫
    • 3.2、字模庫的資料檢索
    • 3.3、字模資料獲得
      • 方法一:二進制轉文本後擷取字模
      • 方法二:直接用字模取模軟體
  • 結語:

1、ASCII碼回顧

标準的ACSII碼是用一個位元組中的7個二進制位,最高位0或者作為校驗位,可以表示2^7即0000 0000~0111 1111 (0x00-0x7F)共128個字元。

擴充的ASCII碼,一些歐洲國針對本國的文字,利用位元組中閑置的最高位編入新的符号(0x80-0xff),把8個二進制位全部用來編碼,進而可以表達256個字元。

實際上在漢字顯示系統中,由于為了區分漢字與标準ASCII碼的識别,也是使用了這個最高位二進制位進行編碼。

2、BG2312編碼規則

1980年,中國國家标準總局釋出了《資訊交換用漢字編碼字元集》一套中國的字元編碼标準,也就是GB 2312—1980。為了滿足中國常用的大幾千個漢字的編碼,采用了十六位表示一個字元的編碼方案,也就是說一個中文漢字占兩個位元組。

在國标GB2312-80中規定,所有的國标漢字及符号的邏輯結構為:配置設定有94個區塊,每個區塊裡有94個國标字元。總共可以容納9494=8836個字元。

在這個9494的邏輯陣列中,陣列的每個區塊稱為一個“區”,共有01區到94區;區塊中的每一個位稱為“位”,共有01位到94位。這樣,陣列中的每一個漢字和符号所在的區号和位号的組合就形成它們的“區位碼”。區位碼的前兩位是它的區号,後兩位是它的位号。

2.1區位碼:

舉例來說,“啊”字是GB2312之中的第一個漢字,它的區位碼就是1601(十進制)。這個區位碼是十進制的,在實際計算機處理過程中,需要把這個區位碼表達成位元組編碼(機内碼),通常采用EUC(Extended Unix Code)儲存方法(是一個使用8位編碼來表示字元的方法,把每個區位加上0xA0來表示,以便相容于ASCII)。

2.2機内碼

具體方式是,每個漢字及符号以兩個位元組來表示 。 第一個位元組稱為“高位位元組”,第二個位元組稱為“低位位元組”。 “高位位元組”使用了0xA1-0xF7(把01-87區的區号加上0xA0),“低位位元組”使用了0xA1-0xFE(把01-94加上0xA0)。例如 “啊”字在大多數程式中,會以0xB0A1儲存:

(與區位碼對比:0xB0=0xA0+16,0xA1=0xA0+1)。

這裡注意,位元組0x00-0x7F正好是标準ASCII碼的編碼範圍。是以機内碼的這種編碼規則正好實作了對ASCII的編碼規則的相容。

//讀出漢字的機内碼,并顯示出來

#include <stdio.h>
void main(void){
    unsigned char high8bit,low8bit;
    unsigned char *hz = "啊";

    high8bit = *hz;
    low8bit = *(hz+1);

    printf("h = %3d,l = %3d\n",high8bit,low8bit); /*h = 176,l = 161*/
    }
           

2.3區位的分類

GB2312編碼通過分區,很好地把字元即進行了邏輯分類,又實作了編碼與實體尋址的關聯性。下表是分區的編碼的對應關系。在使用中可以根據這個規則去判斷字元的類别等。

ILI9341的使用之【九】BG2312字庫1、ASCII碼回顧2、BG2312編碼規則3、點陣字庫結語:

2.4編碼表舉例

通過編碼表的局部樣例。能過這圖展現出來的編碼與漢字或符号的對應關系,可以更好更直覺的了解區位碼與機内碼的對應關系。如需完整檢視編碼表,可以到這個連結:http://witmax.cn/gb2312.html中去檢視。

ILI9341的使用之【九】BG2312字庫1、ASCII碼回顧2、BG2312編碼規則3、點陣字庫結語:
ILI9341的使用之【九】BG2312字庫1、ASCII碼回顧2、BG2312編碼規則3、點陣字庫結語:

2.5 正常字元的操作

判斷是否是GB2312

bool isGBCode(const string& strIn)
{
    unsigned char ch1;
    unsigned char ch2;
   
    if (strIn.size() >= 2)
    {
        ch1 = (unsigned char)strIn.at(0);
        ch2 = (unsigned char)strIn.at(1);
        if (ch1>=176 && ch1<=247 && ch2>=160 && ch2<=254)
            return true;
        else return false;
    }
           

判斷一個字元是ASCII字元還是中文字元

判斷一個字元是哪類字元,根據字元的編碼規則的差別,隻要判斷編碼首位是“0”還是“1”就可以準确區分。漢字編碼的兩個位元組的最高有效位都 是1,ASCII編碼位元組最高有效位是0。知道了這個差別,在程式中去判斷字元的分類還是很容易的。

3、點陣字庫

漢字的機内碼确定了一個漢字的檢索規則,而真正要顯示一個漢字,則需要漢字的相應字模資料。使用時常漢字的點陣資料放在一起形成點陣字庫。是以不同的字型,不同的字号都需要有不同的點陣字庫。根據點陣的行列的點數,我們常用行點數列點數來辨別一個字庫的清晰程式。下面拿1616點陣的宋體字庫來說明字庫資料的存儲結構與使用方法。

3.1、16點陣字庫

本文用到的字庫,可以到這裡下載下傳。

下載下傳的1616點陣,采用逐行的方式采樣掃描。每個點對應位元組裡的一個位的話,那每行16個點由2個位元組存儲。從上到下一個字共16行點陣。是以每個字模的點陣資料就有216=32個位元組。這32個位元組按以下順序存儲:

1、第一行左8位點陣形成一個位元組

2、第一行的右側8位點陣形成一個位元組

3、第二行左8位點陣形成一個位元組

4、…

5、第十六行右側8位點陣形成一個位元組

如:國标碼【B0A1】 “啊” 字點陣資料32個位元組的位元組資料如下:

【0x00,0x04,0x2F,0x7E,0xF9,0x04,0xA9,0x04,0xAA,0x14,0xAA,0x7C,0xAC,0x54,0xAA,0x54,0xAA,0x54,0xA9,0x54,0xE9,0x74,0xAD,0x54,0x0A,0x04,0x08,0x04,0x08,0x14,0x08,0x0C】

這32個位元組從0開始計算,第0,2,4,,30的偶數位元組為字模點陣的左側半行的點陣資料。第1,3,,31奇數位元組為字模點陣的右側半行的點陣資料。具體如下圖:

ILI9341的使用之【九】BG2312字庫1、ASCII碼回顧2、BG2312編碼規則3、點陣字庫結語:

以上逐行方式隻是一種模組化順序。當然也可以是逐列式,行列式,列行式等。是以在具體使用字庫時,要注意區分。

3.2、字模庫的資料檢索

一般情況下,每個字模資料都是按GB2312的區位碼順序存儲在字庫檔案裡的,一個位元組緊接着一個位元組。對16*16點陣字庫而言,每個字的字模資料32個位元組。是以從機内碼變換成區位碼後需要再乘以32才能算出字庫裡某一個字元的離檔案頭0x00位址位的偏移量:

字庫内某字元點陣位元組首位偏移量 =

(((機位碼高位位元組- 0xA1)*94字元)+(機位碼低位位元組- 0xA1))*32位元組

如:國标碼【B0A1】 “啊” 字模32個位元組按區位碼檢索在字庫裡的位址偏移量為:( (0xB0-0xA1) * 94 + (0xA1-0xA1) )*32。

注:這裡之是以是減去0xA1,原因是偏移量是從0開始算的。第一個位元組的偏移量是0。

以下這個例程,包含了機内碼與區位碼轉換,字庫檢索與點陣資料讀取

#include <stdio.h>
void printhz(int pos);
main(void){
	
    int p;	//字庫内的偏移量 
    unsigned char hbyte,lbyte;
    const char *hz= "心";


    hbyte = *hz-0xA1;
    lbyte = *(hz+1)-0xA1;

    p = (hbyte*94 + lbyte)*2*16;  //16*16點陣,每個字有2*16個位元組點陣資料

    printhz(p);

    }
    
    
void printhz(int pos){

    FILE *fk;
    
    unsigned char arr[32] = {0};
    unsigned char kk;

    if((fk=fopen("HZK16","r"))==NULL)
    {
        printf("file cannot be opened\n");
        return ;
    }

    fseek(fk,pos,0);
    fread(arr,sizeof(char),32,fk);

    for(int i=0;i<16;i++)//行循環 
    {
        for(int j=0;j<8;j++) //每行的第一個位元組處理 
        {
            kk = arr[2*i] << j;//處理偶數位的位元組,顯示文字的左側 
            if(kk & 0x80) //移位後,确定最高有效位是1還是0 
            {
                printf("*");
            }
            else
            {
                printf(" ");
            }
        }
        
        for(int j=0;j<8;j++) //每行的第二個位元組處理 
        {
            kk = arr[2*i+1] << j; //處理奇數位的位元組,顯示文字的右側 
            if(kk & 0x80)  //移位後,确定最高有效位是1還是0 
            {
                printf("*");
            }
            else
            {
                printf(" ");
            }
        }

        printf("\n");
        
    }
    fclose(fk); }
           

3.3、字模資料獲得

配合本文,我在資源裡提供了字庫檔案HZK16,該檔案是一個16*16點陣宋體字庫,是一個無格式的二進制檔案,點陣資料讀寫方式為如上所述的“逐行式”。

在一些單片機的運作環境中比如arduino UNO,由于RAM很小才幾K,在應用時字模數不能以檔案形式存在,隻能以程式裡的資料結構的形式讀入FLASH中。這樣就需要把HZK16這個二進制檔案轉換格式。變成文本格式的,arduino IDE能識别的字元串數組形式。

方法一:二進制轉文本後擷取字模

/*copyHZK.cpp*/
#include <stdio.h>

#define HZKSIZE  282752  //buff=94*94*32  =282752

const char *hzkHead[9] = {
	"#ifdef __AVR__\n"
	"  #include <avr/io.h>\n"
	"  #include <avr/pgmspace.h>\n"
	"#elif defined(ESP8266)\n"
	"  #include <pgmspace.h>\n"
	"#else\n"
	"  #define PROGMEM\n"
	"endif\n"
	"static const unsigned char HZK[] PROGMEM = {\n" 
};

unsigned char hzk[HZKSIZE];   //buff=94*94*32  =282752
char hexNumber[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char z[4]={'0','x','0','0'};
FILE *fs,*fd;

//把字庫byte變成16進制顯示的字元串  00001111->"0x0F"
void byteTohex(char *tohex, unsigned char hzk){
	*(tohex+2)=hexNumber[hzk>>4];
	*(tohex+3)=hexNumber[hzk & 0x0F];
}

int main(void){

//從原字庫裡讀出放入hzk數組裡 
    if ((fs=fopen("HZK16","r"))==NULL)
    {
        printf("file cannot be opened\n");
        return 0;
    };

	fseek(fs,0,0);
	fread(hzk,sizeof(char),HZKSIZE,fs);
	fclose(fs); 
 
	
//把數組寫入檔案裡hxk16.c 
    if ((fd=fopen("hzk16.c","w"))==NULL)  //open or create file
    {
        printf("file cannot be found and not be create\n");
        return 1;
    }
    
    for (int i=0;i<9;i++){  //write data structure head to hzk16.c
    	fputs(hzkHead[i],fd);     	
	}
     
    //write data to hzk16.c
    for (int k=0;k<HZKSIZE;k++){
    	 byteTohex(z,hzk[k]);
    	fwrite(z,sizeof(char),4,fd);
    	if (k<HZKSIZE-1){	
    	    if ((k+1)%32!=0){
    	    	fwrite(",",sizeof(char),1,fd);
			}else{
				fwrite(",\n",sizeof(char),2,fd);
			};
        };

	};
	
	fwrite("};",sizeof(char),2,fd);
	fclose(fd);
}
           

經過以上程式處理後的HZK16.c有1.3M,無法直接在arduino UNO上使用,對于文字較少的或隻顯示固定一些字元的應用,可以通過把上面這個字庫内的相應字模資料單獨檢索出後放入數組變量後進行顯示。這個可以根據自己的應用需求靈活掌握。而ESP8266子產品上有8M左右的flash。可以直接用上這個字庫。

方法二:直接用字模取模軟體

對于文字較少的或隻顯示固定一些字元的應用,也可以通過取模軟體快速獲得字模資料。具體取模軟體及使用,網上有大量的教程和介紹,這裡不再贅述。

結語:

以上内容就是漢字國際字庫的規則和使用方法。

如果要在本系列之案列硬體環境中使用,在顯示處理方面,隻需要修改write()函數和drawChar()函數的顯示坐标設定方法和字庫的檢索方法就可以了,因為也不難,這裡要以詳細參考上一篇文章的相應說明進行程式設計。

由于字庫一般都比較大。在LCD等這類單片機小系統上使用時,由于flash空間都較小等,實際在使用字庫時有很多限制。是以有種處理方法。也根據裝置的不同而不同。這裡在後續有機會,會陸續進行分析。