一。漢字顯示原理
1. 常用漢字編碼
這里我們采用GBK編碼
2. 漢字顯示原理
假如顯示一個16*8的漢字,高16,寬8,則高需要2個字節(jié),顯示這個漢字需要2*8=16個字節(jié),這16個字節(jié)的數(shù)據(jù)就是漢字的點陣數(shù)據(jù)。
3. 字符/漢字顯示方法
利用軟件取模
取模方向設(shè)置:
掃描方向:縱向為先顯示第一個字節(jié),然后顯示第二個字節(jié),從低位到高位,然后換到第二列。
數(shù)據(jù)前綴改為“0x”,數(shù)據(jù)后綴改為“,”,行后綴為“;”。
對于漢字顯示采用16*16。
例如:開,為16*16,得到下面的字模數(shù)據(jù)。
得到字模數(shù)據(jù)后,程序中就可以從中取得相應(yīng)的字節(jié)進(jìn)行顯示。
4. 漢字顯示的過程
如何顯示字庫中所有的漢字?利用專用的軟件實現(xiàn)。
根據(jù)內(nèi)碼,查找相應(yīng)的字庫點陣,然后解析,顯示。
對于漢字的內(nèi)碼常用GBK碼。每一個漢字都對應(yīng)一個GBK的編碼。
認(rèn)識GBK碼:
GBK碼共有126個區(qū),每個區(qū)里有190個漢字。
如何根據(jù)GBK編碼找到漢字:
1.首先找到GBK編碼的高位,得到這個漢字在哪個區(qū)。(高位-0x81)確定在哪個區(qū)。
(GBKH-0x81)*190表示這個漢字所在區(qū)的前面幾個區(qū)已經(jīng)有這么多個漢字。
當(dāng)GBKL<0x7F時:漢字所在區(qū)的偏移為GBKL-0x40。
當(dāng)GBKL>0x7F時:漢字所在區(qū)的偏移為GBKL-0x41,因為前面空掉一個0x7F。
因為每個漢字占用的字節(jié)數(shù)為size*2,比如16*16的漢字,那么每個漢字占用的空間是16*2=32個字節(jié)。
當(dāng) GBKL<0X7F 時:Hp=((GBKH-0x81)*190+GBKL-0X40)*(size*2);
當(dāng) GBKL>0X80 時:Hp=((GBKH-0x81)*190+GBKL-0X41)*(size*2);
根據(jù)GBK編碼得到這個漢字所在漢字庫的起始地址。
從以上的方法找到了漢字所在的位置,下面開始制作點陣字庫。
采用字庫制作軟件制作漢字庫。
制作點陣字庫
生成字庫軟件設(shè)置:
注意字體大小選12,這個是電腦端字體的大小。
注意電腦端的字體大小與生成點陣的字體大小有區(qū)別
模式選擇縱向取模方式二。
生成點陣字庫后,要用文件系統(tǒng)讀取字庫,然后存到SPI-Flash中。
在SPI FLASH中的某個地方寫個標(biāo)志0xAA,表示已經(jīng)更新過字庫。
Show_Str函數(shù)既能顯示字符,也能顯示漢字,如果是顯示漢字就調(diào)用Show_Font函數(shù)。
Get_HzMat函數(shù)根據(jù)編碼取得字庫中漢字的字模。然后根據(jù)字模數(shù)據(jù)解析,顯示。
二。實驗講解
生成的字庫文件名為:16.DZK,字庫大小為766,080字節(jié),然后把名字改成:GBK16.FON。
然后把這個文件存進(jìn)SD卡,程序會從SD卡中把這個文件寫入SPI FLASH。
fontupd.c 更新字庫
text.c 顯示漢字
1. 程序主函數(shù)中開始的時候會檢查字庫,調(diào)用fontinit()函數(shù)。
//初始化字體
//返回值:0,字庫完好.
//其他,字庫丟失
u8 font_init(void)
{
SPI_Flash_Init(); //初始化25Q**系列。
FONTINFOADDR=(1024*6+500)*1024; //W25Q64,6M以后
ftinfo.ugbkaddr=FONTINFOADDR+25; //UNICODEGBK 表存放首地址固定地址
SPI_Flash_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));//讀出ftinfo結(jié)構(gòu)體數(shù)據(jù)
if(ftinfo.fontok!=0XAA)return 1; //字庫錯誤.
return 0;
}
字體信息保存地址,占25個字節(jié),第1個字節(jié)用于標(biāo)記字庫是否存在.后續(xù)每8個字節(jié)一組,分別保存起始地址和文件大小。
//字庫信息結(jié)構(gòu)體定義
//用來保存字庫基本信息,地址,大小等
__packed typedef struct
{
u8 fontok; //字庫存在標(biāo)志,0XAA,字庫正常;其他,字庫不存在
u32 ugbkaddr; //unigbk的地址
u32 ugbksize; //unigbk的大小
u32 f12addr; //gbk12地址
u32 gbk12size; //gbk12的大小
u32 f16addr; //gbk16地址
u32 gkb16size; //gbk16的大小
}_font_info;
__packed是字節(jié)對齊的意思。
比如說int float double char它的總大小是4 + 4 + 8 + 1 = 17
但如果不用__packed的話,系統(tǒng)將以默認(rèn)的方式對齊(假設(shè)是4字節(jié)),那么它占4 + 4 + 8 + 4 = 20;(不足4字節(jié)以4字節(jié)補齊)。
ftinfo 是我們在 fontupd.h 里面定義的一個結(jié)構(gòu)體,用于記錄字庫首地址及字庫大小等信息。
因為我們將W25Q64 的前 6M 字節(jié)給 FATFS 管理(用做本地磁盤),然后又預(yù)留了 500K 字節(jié)給用戶自己使用,最后的 1.5M 字節(jié)(W25Q64 總共 8M 字節(jié)),才是 UNIGBK 碼表和字庫的存儲空間。
finfo結(jié)構(gòu)體的存放地址為:
FONTINFOADDR=(1024*6+500)*1024;
GBK字庫存放的首地址為:
ftinfo.ugbkaddr=FONTINFOADDR+25;
2. 主函數(shù)如果沒有檢測到字庫
a. 初始化SD卡
while(SD_Initialize()) //檢測SD卡
沒檢測到SD卡報錯
LCD_ShowString(60,70,200,16,16,"SD Card Failed!");
b. 調(diào)用 update_font(20,110,16,0); 從SD卡更新字庫
自己做的字庫要放在SD卡SYSTEM/FONT目錄下
文件名為GBK16.FON或GBK24.FON
//更新字體文件,UNIGBK,GBK12,GBK16一起更新
//x,y:提示信息的顯示地址
//size:字體大小
//提示信息字體大小
//src: 0, 從SD卡更新.
//src:1, 從25QXX更新
//返回值:0,更新成功;
//其他,錯誤代碼.
u8 update_font(u16 x,u16 y,u8 size,u8 src)
{
u8 *gbk16_path;
u8 *gbk12_path;
u8 *unigbk_path;
u8 res;
if(src)//從25qxx更新
{
unigbk_path=(u8*)UNIGBK_25QPATH;
gbk12_path=(u8*)GBK12_25QPATH;
gbk16_path=(u8*)GBK16_25QPATH;
}else//從sd卡更新
{
unigbk_path=(u8*)UNIGBK_SDPATH;
gbk12_path=(u8*)GBK12_SDPATH;
gbk16_path=(u8*)GBK16_SDPATH;
}
res=0XFF;
ftinfo.fontok=0XFF;
SPI_Flash_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //清除之前字庫成功的標(biāo)志.防止更新到一半重啟,導(dǎo)致的字庫部分?jǐn)?shù)據(jù)丟失.
SPI_Flash_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //重新讀出ftinfo結(jié)構(gòu)體數(shù)據(jù)
LCD_ShowString(x,y,240,320,size,"Updating UNIGBK.BIN");
res=updata_fontx(x+20*size/2,y,size,unigbk_path,0); //更新UNIGBK.BIN
if(res)return 1;
LCD_ShowString(x,y,240,320,size,"Updating GBK12.BIN ");
res=updata_fontx(x+20*size/2,y,size,gbk12_path,1); //更新GBK12.FON
if(res)return 2;
LCD_ShowString(x,y,240,320,size,"Updating GBK16.BIN ");
res=updata_fontx(x+20*size/2,y,size,gbk16_path,2); //更新GBK16.FON
if(res)return 3;
//全部更新好了,寫入標(biāo)志位0xAA。
ftinfo.fontok=0XAA;
SPI_Flash_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //保存字庫信息
return 0;//無錯誤.
}
調(diào)用updata_fontx(u16 x,u16 y,u8 size,u8 *fxpath,u8 fx);
//更新某一個
//x,y:坐標(biāo)
//size:字體大小
//fxpath:路徑
//fx:更新的內(nèi)容 0,ungbk;1,gbk12;2,gbk16;
//返回值:0,成功;其他,失敗.
u8 updata_fontx(u16 x,u16 y,u8 size,u8 *fxpath,u8 fx)
{
u32 flashaddr=0;
FIL * fftemp;
u8 *tempbuf;
u8 res;
u16 bread;
u32 offx=0;
u8 rval=0;
fftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL)); //為文件系統(tǒng)分配內(nèi)存
if (fftemp==NULL) rval=1; //分配失敗,rval = 1;
tempbuf=mymalloc(SRAMIN,4096); //為數(shù)據(jù)緩存區(qū)分配4096個字節(jié)空間
if (tempbuf==NULL) rval=1; //分配失敗,rval = 1;
res=f_open(fftemp,(const TCHAR*)fxpath,FA_READ); //根據(jù)文件路徑打開文件
if (res) rval=2; //打開文件失敗
if (rval==0)
{
if(fx==0) //更新UNIGBK.BIN
{
ftinfo.ugbkaddr = FONTINFOADDR+sizeof(ftinfo);//信息頭之后,緊跟UNIGBK轉(zhuǎn)換碼表
ftinfo.ugbksize = fftemp->fsize; //UNIGBK大小
flashaddr = ftinfo.ugbkaddr; //UNIGBK字庫的地址
}else if (fx==1) //更新GBK12
{
ftinfo.f12addr=ftinfo.ugbkaddr+ftinfo.ugbksize; //UNIGBK之后,緊跟GBK12字庫
ftinfo.gbk12size=fftemp->fsize; //GBK12字庫大小
flashaddr=ftinfo.f12addr; //GBK12的起始地址
}else //更新GBK16
{
ftinfo.f16addr=ftinfo.f12addr+ftinfo.gbk12size; //GBK12之后,緊跟GBK16字庫
ftinfo.gkb16size=fftemp->fsize; //GBK16字庫大小
flashaddr=ftinfo.f16addr; //GBK16的起始地址
}
while (res==FR_OK) //死循環(huán)執(zhí)行
{
res=f_read(fftemp,tempbuf,4096,(UINT *)&bread); //讀取數(shù)據(jù)
if(res!=FR_OK)break; //執(zhí)行錯誤
SPI_Flash_Write(tempbuf,offx+flashaddr,4096);//從0開始寫入4096個數(shù)據(jù)
offx+=bread;
fupd_prog(x,y,size,fftemp->fsize,offx); //進(jìn)度顯示
if (bread!=4096) break; //,如果讀出來的數(shù)據(jù)不是4092個,說明數(shù)據(jù)讀完了,跳出死循環(huán).
}
f_close(fftemp); //關(guān)閉文件
}
myfree(SRAMIN,fftemp); //釋放內(nèi)存
myfree(SRAMIN,tempbuf); //釋放內(nèi)存
return res;
}
f_open函數(shù)用法:
The f_open function creates afile objectto be used to access the file.
FRESULTf_open(
FIL*fp,
constTCHAR*path,
BYTEmode
);
f_read函數(shù)的用法:
Thef_readfunctionreadsdatafromafile.
FRESULTf_read(
FIL*fp,
void*buff,
UINTbtr,
UINT*br
);
text.c中的函數(shù):
1.根據(jù)GBK碼找到字模函數(shù)Get_HzMat(unsignedchar*code,unsignedchar*mat,u8size)
//code字符指針開始
//從字庫中查找出字模
//code字符串的開始地址,GBK碼
//mat字模數(shù)據(jù)存放地址size*2bytes大小
voidGet_HzMat(unsignedchar*code,unsignedchar*mat,u8size)//漢字取模
{
unsignedcharqh,ql;
unsignedchari;
unsignedlongf