新四季網

正點原子stm32l4教程(F7水星開發板資料連載第四十六章)

2023-09-10 01:38:22 1

1)實驗平臺:正點原子水星 STM32F4/F7 開發板

2)摘自《STM32F7 開發指南(HAL 庫版)》關注官方微信號公眾號,獲取更多資料:正點原子

3)全套實驗源碼 手冊 視頻下載地址:http://www.openedv.com/thread-13912-1-1.html

第四十六章 漢字顯示實驗

漢字顯示在很多單片機系統都需要用到,少則幾個字,多則整個漢字庫的支持,更有甚者

還要支持多國字庫,那就更麻煩了。本章,我們將向大家介紹,如何用 STM32F767 控制 LCD

顯示漢字。在本章中,我們將使用外部 SPI FLASH 來存儲字庫,並可以通過 SD 卡更新字庫。

STM32F767 讀取存在 SPI FLASH 裡面的字庫,然後將漢字顯示在 LCD 上面。本章分為如下幾

個部分:

46.1 漢字顯示原理簡介

46.2 硬體設計

46.3 軟體設計

46.4 下載驗證

46.1 漢字顯示原理簡介

常用的漢字內碼系統有 GB2312,GB13000,GBK,BIG5(繁體)等幾種,其中 GB2312

支持的漢字僅有幾千個,很多時候不夠用,而 GBK 內碼不僅完全兼容 GB2312,還支持了繁體

字,總漢字數有 2 萬多個,完全能滿足我們一般應用的要求。

本實例我們將製作三個 GBK 字庫,製作好的字庫放在 SD 卡裡面,然後通過 SD 卡,將字

庫文件複製到外部 FLASH 晶片 W25Q256 裡,這樣,W25Q256 就相當於一個漢字字庫晶片了。

漢字在液晶上的顯示原理與前面顯示字符的是一樣的。漢字在液晶上的顯示其實就是一些

點的顯示與不顯示,這就相當於我們的筆一樣,有筆經過的地方就畫出來,沒經過的地方就不

畫,我們以 12*12 的漢字為例,假設其取模方向為從上到下,從左到右的方向取模,且高位在

前,那麼其取模原理如圖 46.1.1 所示:

圖 46.1.1 從上到下,從左到右取模原理

圖中,我們取模的時候,從最左上方的點開始取(從上到下,從左到右),且高位在前(bit7

在表示第一個位),那麼第一個字節就是:0X11(1,表示淺藍色的點,即要畫出來的點,0

則表示不要畫出來),第二個字節是:0X10,第三個字節(到第二列了,每列 2 個字節)是:

0X1E……,依次類推,一個 12*12 的漢字,總共有 12 列,每列 2 個字節,總共需要 24 個字節

來表示。

在顯示的時候,我們只需要讀取這個漢字的點陣數據(12*12 字體,一個漢字的點陣數據

為 24 個字節),然後將這些數據,按取模方式,反向解析出來(坐標要處理好),每個字節,

是 1 的位,就畫出來,不是 1 的位,就忽略,這樣,就可以顯示出這個漢字了。

所以要顯示漢字,我們首先要知道漢字的點陣數據,這些數據可以由專門的軟體來生成。

知道顯示了一個漢字,就可以推及整個漢字庫了。漢字在各種文件裡面的存儲不是以點陣數據

的形式存儲的(否則那佔用的空間就太大了),而是以內碼的形式存儲的,就是

GB2312/GBK/BIG5 等這幾種的一種,每個漢字對應著一個內碼,在知道了內碼之後再去字庫

裡面查找這個漢字的點陣數據,然後在液晶上顯示出來。這個過程我們是看不到,但是計算機

是要去執行的。

單片機要顯示漢字也與此類似:漢字內碼(GBK/GB2312)→查找點陣庫→解析→顯示。

所以只要我們有了整個漢字庫的點陣,就可以把電腦上的文本信息在單片機上顯示出來了。

這裡我們要解決的最大問題就是製作一個與漢字內碼對得上號的漢字點陣庫。而且要方便單片

機的查找。每個 GBK 碼由 2 個字節組成,第一個字節為 0X81~0XFE,第二個字節分為兩部分,

一是 0X40~0X7E,二是 0X80~0XFE。其中與 GB2312 相同的區域,字完全相同。

我們把第一個字節代表的意義稱為區,那麼 GBK 裡面總共有 126 個區(0XFE-0X81 1),

每個區內有 190 個漢字(0XFE-0X80 0X7E-0X40 2),總共就有 126*190=23940 個漢字。我

們的點陣庫只要按照這個編碼規則從 0X8140 開始,逐一建立,每個區的點陣大小為每個漢字

所用的字節數*190。這樣,我們就可以得到在這個字庫裡面定位漢字的方法:

當 GBKL0X80 時:Hp=((GBKH-0x81)*190 GBKL-0X41)*(size*2);

其中 GBKH、GBKL 分別代表 GBK 的第一個字節和第二個字節(也就是高位和低位),size

代表漢字字體的大小(比如 16 字體,12 字體等),Hp 則為對應漢字點陣數據在字庫裡面的起

始地址(假設是從 0 開始存放)。

這樣我們只要得到了漢字的 GBK 碼,就可以顯示這個漢字了。從而實現漢字在液晶上的

顯示。

上一章,我們提到要用 cc936.c,以支持長文件名,但是 cc936.c 文件裡面的兩個數組太大

了(172KB),直接刷在單片機裡面,太佔用 flash 了,所以我們必須把這兩個數組存放在外部

flash。cc936 裡面包含的兩個數組 oem2uni 和 uni2oem 存放 unicode 和 gbk 的互相轉換對照表,

這兩個數組很大,這裡我們利用 ALIENTEK 提供的一個 C 語言數組轉 BIN(二進位)的軟體:

C2B 轉換助手 V1.1.exe,將這兩個數組轉為 BIN 文件,我們將這兩個數組拷貝出來存放為一個

新的文本文件,假設為 UNIGBK.TXT,然後用 C2B 轉換助手打開這個文本文件,如圖 46.1.2

所示:

圖 46.1.2 C2B 轉換助手

然後點擊轉換,就可以在當前目錄下(文本文件所在目錄下)得到一個 UNIGBK.bin 的文

件。這樣就完成將 C 語言數組轉換為.bin 文件,然後只需要將 UNIGBK.bin 保存到外部 FLASH

就實現了該數組的轉移。

在 cc936.c 裡面,主要是通過 ff_convert 調用這兩個數組,實現 UNICODE 和 GBK 的互轉,

該函數原代碼如下:

WCHAR ff_convert ( /* Converted code, 0 means conversion error */WCHAR src, /* Character code to be converted */UINTdir/* 0: Unicode to OEMCP, 1: OEMCP to Unicode */){const WCHAR *p;WCHAR c;int i, n, li, hi;if (src p[i * 2]) li = i;else hi = i;}c = n ? p[i * 2 1] : 0;}return c;}

此段代碼,通過二分法(16 階)在數組裡面查找 UNICODE(或 GBK)碼對應的 GBK(或

UNICODE)碼。當我們將數組存放在外部 flash 的時候,將該函數修改為:

WCHAR ff_convert ( /* Converted code, 0 means conversion error */WCHAR src,/* Character code to be converted */UINTdir/* 0: Unicode to OEMCP, 1: OEMCP to Unicode */){WCHAR t[2];WCHAR c;u32 i, li, hi;u16 n;u32 gbk2uni_offset=0;if (src < 0x80)c = src;//ASCII,直接不用轉換.else{if(dir) gbk2uni_offset=ftinfo.ugbksize/2;//GBK 2 UNICODEelse gbk2uni_offset=0;//UNICODE 2 GBK/* Unicode to OEMCP */hi=ftinfo.ugbksize/2;//對半開.hi =hi / 4 - 1;li = 0;for (n = 16; n; n--){i = li (hi - li) / 2;W25QXX_Read((u8*)&t,ftinfo.ugbkaddr i*4 gbk2uni_offset,4);//讀出 4 個字節if (src == t[0]) break;if (src > t[0])li = i;else hi = i;}c = n ? t[1] : 0;}return c;}

代碼中的 ftinfo.ugbksize 為我們剛剛生成的 UNIGBK.bin 的大小,而 ftinfo.ugbkaddr 是我們

存放 UNIGBK.bin 文件的首地址。這裡同樣採用的是二分法查找,關於 cc936.c 的修改,我們就

介紹到這。

字庫的生成,我們要用到一款軟體,由易木雨軟體工作室設計的點陣字庫生成器 V3.8。該

軟體可以在 WINDOWS 系統下生成任意點陣大小的 ASCII,GB2312(簡體中文)、GBK(簡體中

文)、BIG5(繁體中文)、HANGUL(韓文)、SJIS(日文)、Unicode 以及泰文,越南文、俄文、烏克

蘭文,拉丁文,8859 系列等共二十幾種編碼的字庫,不但支持生成二進位文件格式的文件,也

可以生成 BDF 文件,還支持生成圖片功能,並支持橫向,縱向等多種掃描方式,且掃描方式

可以根據用戶的需求進行增加。該軟體的界面如圖 46.1.3 所示:

圖 46.1.3 點陣字庫生成器默認界面

要生成 16*16 的 GBK 字庫,則選擇:936 中文 PRC GBK,字寬和高均選擇 16,字體大小

選擇 12,然後模式選擇縱向取模方式二(從上到下,從左到右,且字節高位在前,低位在後),

最後點擊創建,就可以開始生成我們需要的字庫了(.DZK 文件,在生成完以後,我們手動修改

後綴為.fon)。具體設置如圖 46.1.4 所示:

圖 46.1.4 生成 GBK16*16 字庫的設置方法

注意:電腦端的字體大小與我們生成點陣大小的關係為:

fsize=dsize*6/8

其中,fsize 是電腦端字體大小,dsize 是點陣大小(12、16、24、32 等)。所以 16*16 點陣

大小對應的是 12 字體。

生成完以後,我們把文件名和後綴改成:GBK16.FON(這裡是手動修改後綴!!)。同樣的

方法,生成 12*12 的點陣庫(GBK12.FON)、24*24 的點陣庫(GBK24.FON)和 32*32 的點陣

庫(GBK32.FON),總共製作 4 個字庫。

另外,該軟體還可以生成其他很多字庫,字體也可選,大家可以根據自己的需要按照上面

的方法生成即可。該軟體的詳細介紹請看軟體自帶的《點陣字庫生成器說明書》,關於漢字顯示

原理,我們就介紹到這。

46.2 硬體設計

本章實驗功能簡介:開機的時候先檢測 W25Q256 中是否已經存在字庫,如果存在,則按

次序顯示漢字(四種字體都顯示)。如果沒有,則檢測 SD 卡和文件系統,並查找 SYSTEM 文件

夾下的 FONT 文件夾,在該文件夾內查找 UNIGBK.BIN、GBK12.FON、GBK16.FON、

GBK24.FON 和 GBK32.FON(這幾個文件的由來,我們前面已經介紹了)。在檢測到這些文

件之後,就開始更新字庫,更新完畢才開始顯示漢字。通過按按鍵 KEY0,可以強制更新字庫。

同樣我們也是用 DS0 來指示程序正在運行。

所要用到的硬體資源如下:

1) 指示燈 DS0

2) KEY0 按鍵

3) 串口

4) LCD 模塊

5) SD 卡

6) SPI FLASH

這幾部分分,在之前的實例中都介紹過了,我們在此就不介紹了。

46.3 軟體設計

打開本章實驗目錄可以看到,首先在工程根目錄文件夾下面新建了一個 TEXT 的文件夾。

在 TEXT 文件夾下新建 fontupd.c、fontupd.h、text.c、text.h 這 4 個文件。同時,我們在實驗工

程中新建了 TEXT 分組,將新建的源文件加入到了分組之下,並將頭文件包含路徑加入到了工

程的 PATH 中。

打開 fontupd.c,代碼如下:

//字庫區域佔用的總扇區數大小(4 個字庫 unigbk 表 字庫信息=6302984 字節,約佔 1539 個W25QXX 扇區,一個扇區 4K 字節)#define FONTSECSIZE1539//字庫存放起始地址#define FONTINFOADDR 1024*1024*25//開發板是從 25M 地址以後開始存放字庫//前面 25M 被 fatfs 佔用了,25M 以後緊跟 4 個字庫 UNIGBK.BIN,總大//小 6.01M,被字庫佔用了,不能動! 31.01M 以後,用戶可以自由使用.//用來保存字庫基本信息,地址,大小等_font_info ftinfo;//字庫存放在磁碟中的路徑u8*const GBK_PATH[5]={"/SYSTEM/FONT/UNIGBK.BIN",//UNIGBK.BIN 的存放位置"/SYSTEM/FONT/GBK12.FON", //GBK12 的存放位置"/SYSTEM/FONT/GBK16.FON", //GBK16 的存放位置"/SYSTEM/FONT/GBK24.FON", //GBK24 的存放位置"/SYSTEM/FONT/GBK32.FON", //GBK32 的存放位置};//更新時的提示信息u8*const UPDATE_REMIND_TBL[5]={"Updating UNIGBK.BIN", //提示正在更新 UNIGBK.bin"Updating GBK12.FON",//提示正在更新 GBK12"Updating GBK16.FON",//提示正在更新 GBK16"Updating GBK24.FON",//提示正在更新 GBK24"Updating GBK32.FON",//提示正在更新 GBK32};//顯示當前字體更新進度//x,y:坐標//size:字體大小//fsize:整個文件大小//pos:當前文件指針位置u32 fupd_prog(u16 x,u16 y,u8 size,u32 fsize,u32 pos){…//此處省略部分代碼}//更新某一個//x,y:坐標//size:字體大小//fxpath:路徑//fx:更新的內容 0,ungbk;1,gbk12;2,gbk16;3,gbk24;4,gbk32;//返回值: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)); //分配內存if(fftemp==NULL)rval=1;tempbuf=mymalloc(SRAMIN,4096);//分配 4096 個字節空間if(tempbuf==NULL)rval=1;res=f_open(fftemp,(const TCHAR*)fxpath,FA_READ);if(res)rval=2;//打開文件失敗if(rval==0){switch(fx){case 0://更新 UNIGBK.BINftinfo.ugbkaddr=FONTINFOADDR sizeof(ftinfo);//信息頭之後,緊跟 UNIGBK 轉換碼錶ftinfo.ugbksize=fftemp->fsize;//UNIGBK 大小flashaddr=ftinfo.ugbkaddr;break;case 1:ftinfo.f12addr=ftinfo.ugbkaddr ftinfo.ugbksize;//UNIGBK 之後,緊跟 GBK12 字庫ftinfo.gbk12size=fftemp->fsize; //GBK12 字庫大小flashaddr=ftinfo.f12addr;//GBK12 的起始地址break;case 2:ftinfo.f16addr=ftinfo.f12addr ftinfo.gbk12size;//GBK12 之後,緊跟 GBK16 字庫ftinfo.gbk16size=fftemp->fsize; //GBK16 字庫大小flashaddr=ftinfo.f16addr;//GBK16 的起始地址break;case 3:ftinfo.f24addr=ftinfo.f16addr ftinfo.gbk16size;//GBK16 之後,緊跟 GBK24 字庫ftinfo.gbk24size=fftemp->fsize;//GBK24 字庫大小flashaddr=ftinfo.f24addr;//GBK24 的起始地址break;case 4:ftinfo.f32addr=ftinfo.f24addr ftinfo.gbk24size;//GBK24 之後,緊跟 GBK32 字庫ftinfo.gbk32size=fftemp->fsize;//GBK32 字庫大小flashaddr=ftinfo.f32addr;//GBK32 的起始地址break;}while(res==FR_OK)//死循環執行{res=f_read(fftemp,tempbuf,4096,(UINT *)&bread);//讀取數據if(res!=FR_OK)break;//執行錯誤W25QXX_Write(tempbuf,offx flashaddr,4096); //從 0 開始寫入 4096 個數據offx =bread;fupd_prog(x,y,size,fftemp->fsize,offx);//進度顯示if(bread!=4096)break;//讀完了.}f_close(fftemp);}myfree(SRAMIN,fftemp);//釋放內存myfree(SRAMIN,tempbuf); //釋放內存return res;}//更新字體文件,UNIGBK,GBK12,GBK16,GBK24,GBK32 一起更新//x,y:提示信息的顯示地址//size:字體大小//src:字庫來源磁碟."0:",SD 卡;"1:",FLASH 盤,"2:",U 盤.//提示信息字體大小//返回值:0,更新成功;//其他,錯誤代碼.u8 update_font(u16 x,u16 y,u8 size,u8* src){u8 *pname;u32 *buf;u8 res=0;u16 i,j;FIL *fftemp;u8 rval=0;res=0XFF;ftinfo.fontok=0XFF;pname=mymalloc(SRAMIN,100); //申請 100 字節內存buf=mymalloc(SRAMIN,4096); //申請 4K 字節內存fftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL)); //分配內存if(buf==NULL||pname==NULL||fftemp==NULL){myfree(SRAMIN,fftemp);myfree(SRAMIN,pname);myfree(SRAMIN,buf);return 5;//內存申請失敗}for(i=0;i<5;i )//先查找文件 UNIGBK,GBK12,GBK16,GBK24,GBK32 是否正常{strcpy((char*)pname,(char*)src);//copy src 內容到 pnamestrcat((char*)pname,(char*)GBK_PATH[i]);//追加具體文件路徑res=f_open(fftemp,(const TCHAR*)pname,FA_READ);//嘗試打開if(res){rval|=1<<7; //標記打開文件失敗break;//出錯了,直接退出}}myfree(SRAMIN,fftemp);//釋放內存if(rval==0)//字庫文件都存在.{LCD_ShowString(x,y,240,320,size,"Erasing sectors... ");//提示正在擦除扇區for(i=0;i<FONTSECSIZE;i )//先擦除字庫區域,提高寫入速度{fupd_prog(x 20*size/2,y,size,FONTSECSIZE,i);//進度顯示W25QXX_Read((u8*)buf,((FONTINFOADDR/4096) i)*4096,4096);//讀出整個扇區的內容for(j=0;j<1024;j )//校驗數據{if(buf[j]!=0XFFFFFFFF)break;//需要擦除}if(j!=1024)W25QXX_Erase_Sector((FONTINFOADDR/4096) i);//需要擦除的扇區}for(i=0;i<5;i )//依次更新 UNIGBK,GBK12,GBK16,GBK24,GBK32{LCD_ShowString(x,y,240,320,size,UPDATE_REMIND_TBL[i]);strcpy((char*)pname,(char*)src);//copy src 內容到 pnamestrcat((char*)pname,(char*)GBK_PATH[i]);//追加具體文件路徑res=updata_fontx(x 20*size/2,y,size,pname,i); //更新字庫if(res){myfree(SRAMIN,buf);myfree(SRAMIN,pname);return 1 i;}}//全部更新好了ftinfo.fontok=0XAA;W25QXX_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));//保存字庫信息}myfree(SRAMIN,pname);//釋放內存myfree(SRAMIN,buf);return rval;//無錯誤.}//初始化字體//返回值:0,字庫完好.//其他,字庫丟失u8 font_init(void){u8 t=0;W25QXX_Init;while(t<10)//連續讀取 10 次,都是錯誤,說明確實是有問題,得更新字庫了{t ;W25QXX_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));//讀出 ftinfo 結構體數據if(ftinfo.fontok==0XAA)break;delay_ms(20);}if(ftinfo.fontok!=0XAA)return 1;return 0;}

此部分代碼主要用於字庫的更新操作(包含 UNIGBK 的轉換碼錶更新),其中 ftinfo 是我

們在 fontupd.h 裡面定義的一個結構體,用於記錄字庫首地址及字庫大小等信息。因為我們將

W25Q256 的前 25M 字節給 FATFS 管理(用做本地磁碟),隨後,緊跟字庫結構體、UNIGBK.bin、

和三個字庫,這部分內容首地址是:(1024*12)*1024,大小約 6.01M,最後 W25Q256 還剩下約

0.99M 給用戶自己用。

接下來我們打開 fontupd.h 文件代碼如下:

extern u32 FONTINFOADDR; //字體信息保存地址,佔 41 個字節,第 1 個字節用於標記字庫

//是否存在.後續每 8 個字節一組,分別保存起始地址和文件大小//字庫信息結構體定義//用來保存字庫基本信息,地址,大小等__packed typedef struct{u8 fontok;//字庫存在標誌,0XAA,字庫正常;其他,字庫不存在u32 ugbkaddr;//unigbk 的地址u32 ugbksize;//unigbk 的大小u32 f12addr;//gbk12 地址u32 gbk12size;//gbk12 的大小u32 f16addr;//gbk16 地址u32 gbk16size;//gbk16 的大小u32 f24addr;//gbk24 地址u32 gbk24size;//gbk24 的大小u32 f32addr;//gbk32 地址u32 gbk32size;//gbk32 的大小}_font_info;extern _font_info ftinfo; //字庫信息結構體u32 fupd_prog(u16 x,u16 y,u8 size,u32 fsize,u32 pos);//顯示更新進度u8 updata_fontx(u16 x,u16 y,u8 size,u8 *fxpath,u8 fx);//更新指定字庫u8 update_font(u16 x,u16 y,u8 size,u8* src);//更新全部字庫u8 font_init(void);//初始化字庫#endif這裡,我們可以看到 ftinfo 的結構體定義,總共佔用 41 個字節,第一個字節用來標識字庫是否 OK,其他的用來記錄地址和文件大小。接下來打開 text.c 文件,代碼如下://code 字符指針開始//從字庫中查找出字模//code 字符串的開始地址,GBK 碼//mat 數據存放地址 (size/8 ((size%8)?1:0))*(size) bytes 大小//size:字體大小void Get_HzMat(unsigned char *code,unsigned char *mat,u8 size){unsigned char qh,ql;unsigned char i;unsigned long foffset;u8 csize=(size/8 ((size%8)?1:0))*(size);//得到字體一個字符對應點陣集所佔的字節數qh=*code;ql=*( code);if(qh<0x81||ql<0x40||ql==0xff||qh==0xff)//非 常用漢字{ for(i=0;i<csize;i )*mat =0x00;//填充滿格 return; //結束訪問}if(ql<0x7f)ql-=0x40;//注意!else ql-=0x41;qh-=0x81;foffset=((unsigned long)190*qh ql)*csize; //得到字庫中的字節偏移量switch(size){case 12:W25QXX_Read(mat,foffset ftinfo.f12addr,csize);break;case 16:W25QXX_Read(mat,foffset ftinfo.f16addr,csize);break;case 24:W25QXX_Read(mat,foffset ftinfo.f24addr,csize);break;case 32:W25QXX_Read(mat,foffset ftinfo.f32addr,csize);break;}}//顯示一個指定大小的漢字//x,y :漢字的坐標//font:漢字 GBK 碼//size:字體大小//mode:0,正常顯示,1,疊加顯示void Show_Font(u16 x,u16 y,u8 *font,u8 size,u8 mode){u8 temp,t,t1;u16 y0=y;u8 dzk[128];u8 csize=(size/8 ((size%8)?1:0))*(size);//得到字體一個字符對應點陣集所佔的字節數if(size!=12&&size!=16&&size!=24&&size!=32)return;//不支持的 sizeGet_HzMat(font,dzk,size); //得到相應大小的點陣數據for(t=0;t<csize;t ){temp=dzk[t];//得到點陣數據for(t1=0;t1<8;t1 ){if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);else if(mode==0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);temp<<=1;y ;if((y-y0)==size){y=y0;x ;break;}}}}//在指定位置開始顯示一個字符串//支持自動換行//(x,y):起始坐標//width,height:區域//str :字符串//size :字體大小//mode:0,非疊加方式;1,疊加方式void Show_Str(u16 x,u16 y,u16 width,u16 height,u8*str,u8 size,u8 mode){…//此處省略部分代碼}//在指定寬度的中間顯示字符串//如果字符長度超過了 len,則用 Show_Str 顯示//len:指定要顯示的寬度void Show_Str_Mid(u16 x,u16 y,u8*str,u8 size,u8 len){…//此處省略部分代碼}

此部分代碼總共有 4 個函數,我們省略了兩個函數(Show_Str_Mid 和 Show_Str)的代碼,

另外兩個函數,Get_HzMat 函數用於獲取 GBK 碼對應的漢字字庫,通過我們 47.1 節介紹的辦

法,在外部 flash 查找字庫,然後返回對應的字庫點陣。Show_Font 函數用於在指定地址顯示一

個指定大小的漢字,採用的方法和 LCD_ShowChar 所採用的方法一樣,都是畫點顯示,這裡就

不細說了。

text.h 頭文件是一些函數申明,我們這裡不細說了。

前面提到我們對 cc936.c 文件做了修改,我們將其命名為 mycc936.c,並保存在 exfuns 文件

夾下,將工程 FATFS 組下的 cc936.c 刪除,然後重新添加 mycc936.c 到 FATFS 組下,mycc936.c

的源碼就不貼出來了,其實就是在 cc936.c 的基礎上去掉了兩個大數組,然後對 ff_convert 進行

了修改,詳見本例程源碼。

最後,我們看看 main 函數如下:

int main(void){u32 fontcnt;u8 i,j;u8 fontx[2]; //gbk 碼u8 key,t; Cache_Enable; //打開 L1-Cache MPU_Memory_Protection; //保護相關存儲區域 HAL_Init; //初始化 HAL 庫 Stm32_Clock_Init(432,25,2,9); //設置時鐘,216Mhz delay_init(216); //延時初始化uart_init(115200); //串口初始化 LED_Init;//初始化 LED KEY_Init; //初始化按鍵 SDRAM_Init; //初始化 SDRAM LCD_Init;//初始化 LCDW25QXX_Init;//初始化 W25Q256 my_mem_init(SRAMIN);//初始化內部內存池 my_mem_init(SRAMEX); //初始化外部 SDRAM 內存池 my_mem_init(SRAMDTCM); //初始化內部 DTCM 內存池 exfuns_init; //為 fatfs 相關變量申請內存 f_mount(fs[0],"0:",1); //掛載 SD 卡 f_mount(fs[1],"1:",1); //掛載 SPI FLASH. f_mount(fs[2],"2:",1); //掛在 NAND FLASHwhile(font_init)//檢查字庫{ UPD:LCD_Clear(WHITE);//清屏POINT_COLOR=RED;//設置字體為紅色LCD_ShowString(30,50,200,16,16,"Apollo STM32F4/F7");while(SD_Init)//檢測 SD 卡{ LCD_ShowString(30,70,200,16,16,"SD Card Failed!"); delay_ms(200); LCD_Fill(30,70,200 30,70 16,WHITE); delay_ms(200);}LCD_ShowString(30,70,200,16,16,"SD Card OK");LCD_ShowString(30,90,200,16,16,"Font Updating...");key=update_font(20,110,16,"0:");//更新字庫while(key)//更新失敗{LCD_ShowString(30,110,200,16,16,"Font Update Failed!");delay_ms(200);LCD_Fill(20,110,200 20,110 16,WHITE);delay_ms(200);}LCD_ShowString(30,110,200,16,16,"Font Update Success! ");delay_ms(1500);LCD_Clear(WHITE);//清屏}POINT_COLOR=RED;Show_Str(30,30,200,16,"水星 STM32F4/F7 開發板",16,0);Show_Str(30,50,200,16,"GBK 字庫測試程序",16,0);Show_Str(30,70,200,16,"正點原子@ALIENTEK",16,0);Show_Str(30,90,200,16,"2016 年 7 月 15 日",16,0);Show_Str(30,110,200,16,"按 KEY0,更新字庫",16,0);POINT_COLOR=BLUE;Show_Str(30,130,200,16,"內碼高字節:",16,0);Show_Str(30,150,200,16,"內碼低字節:",16,0);Show_Str(30,170,200,16,"漢字計數器:",16,0);Show_Str(30,200,200,32,"對應漢字為:",32,0);Show_Str(30,232,200,24,"對應漢字為:",24,0);Show_Str(30,256,200,16,"對應漢字(16*16)為:",16,0);Show_Str(30,272,200,12,"對應漢字(12*12)為:",12,0);while(1){fontcnt=0;for(i=0x81;i<0xff;i ){fontx[0]=i;LCD_ShowNum(118,150,i,3,16);//顯示內碼高字節for(j=0x40;j<0xfe;j ){if(j==0x7f)continue;fontcnt ;LCD_ShowNum(118,150,j,3,16); //顯示內碼低字節LCD_ShowNum(118,170,fontcnt,5,16);//漢字計數顯示fontx[1]=j;Show_Font(30 176,200,fontx,32,0);Show_Font(30 132,232,fontx,24,0);Show_Font(30 144,256,fontx,16,0);Show_Font(30 108,272,fontx,12,0);t=200;while(t--)//延時,同時掃描按鍵{delay_ms(1);key=KEY_Scan(0);if(key==KEY0_PRES)goto UPD;}LED0_Toggle;}}}}

此部分代碼就實現了我們在硬體描述部分所描述的功能,至此整個軟體設計就完成了。這

節有太多的代碼,而且工程也增加了不少,我們來看看工程的截圖吧,整個工程截圖如圖 46.3.1

所示:

圖 46.3.1 工程建成截圖

46.4 下載驗證

在代碼編譯成功之後,我們通過下載代碼到 ALIENTEK 水星 STM32 開發板上,可以看到

LCD 開始顯示漢字及漢字內碼,如圖 46.4.1 所示:

圖 46.4.1 漢字顯示實驗顯示效果

一開始就顯示漢字,是因為 ALIENTEK 水星 STM32F767 開發板在出廠的時候都是測試過

的,裡面刷了綜合測試程序,已經把字庫寫入到了 W25Q256 裡面,所以並不會提示更新字庫。

如果你想要更新字庫,那麼則必須先找一張 SD 卡,把:光碟\5,SD 卡根目錄文件 文件夾下

面的 SYSTEM 文件夾拷貝到 SD 卡根目錄下,插入開發板,並按復位,之後,在顯示漢字的時

候,按下 KEY0,就可以開始更新字庫了。

字庫更新界面如圖 46.4.2 所示:

圖 46.4.2 漢字字庫更新界面

我們還可以通過 USMART 來測試該實驗,將 Show_Str 函數加入 USMART 控制(方法前

面已經講了很多次了),就可以通過串口調用該函數,在屏幕上顯示任何你想要顯示的漢字了,

有興趣的朋友可以測試一下。

,
同类文章
葬禮的夢想

葬禮的夢想

夢見葬禮,我得到了這個夢想,五個要素的五個要素,水火只好,主要名字在外面,職業生涯良好,一切都應該對待他人治療誠意,由於小,吉利的冬天夢想,秋天的夢是不吉利的
找到手機是什麼意思?

找到手機是什麼意思?

找到手機是什麼意思?五次選舉的五個要素是兩名士兵的跡象。與他溝通很好。這是非常財富,它擅長運作,職業是仙人的標誌。單身男人有這個夢想,主要生活可以有人幫忙
我不怎麼想?

我不怎麼想?

我做了什麼意味著看到米飯烹飪?我得到了這個夢想,五線的主要土壤,但是Tu Ke水是錢的跡象,職業生涯更加真誠。他真誠地誠實。這是豐富的,這是夏瑞的巨星
夢想你的意思是什麼?

夢想你的意思是什麼?

你是什​​麼意思夢想的夢想?夢想,主要木材的五個要素,水的跡象,主營業務,主營業務,案子應該抓住魅力,不能疏忽,春天夢想的吉利夢想夏天的夢想不幸。詢問學者夢想
拯救夢想

拯救夢想

拯救夢想什麼意思?你夢想著拯救人嗎?拯救人們的夢想有一個現實,也有夢想的主觀想像力,請參閱週宮官方網站拯救人民夢想的詳細解釋。夢想著敵人被拯救出來
2022愛方向和生日是在[質量個性]中

2022愛方向和生日是在[質量個性]中

[救生員]有人說,在出生88天之前,胎兒已經知道哪天的出生,如何有優質的個性,將走在什麼樣的愛情之旅,將與生活生活有什么生活。今天
夢想切割剪裁

夢想切割剪裁

夢想切割剪裁什麼意思?你夢想切你的手是好的嗎?夢想切割手工切割手有一個真正的影響和反應,也有夢想的主觀想像力。請參閱官方網站夢想的細節,以削減手
夢想著親人死了

夢想著親人死了

夢想著親人死了什麼意思?你夢想夢想你的親人死嗎?夢想有一個現實的影響和反應,還有夢想的主觀想像力,請參閱夢想世界夢想死亡的親屬的詳細解釋
夢想搶劫

夢想搶劫

夢想搶劫什麼意思?你夢想搶劫嗎?夢想著搶劫有一個現實的影響和反應,也有夢想的主觀想像力,請參閱週恭吉夢官方網站的詳細解釋。夢想搶劫
夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂什麼意思?你夢想缺乏異常藥物嗎?夢想缺乏現實世界的影響和現實,還有夢想的主觀想像,請看官方網站的夢想組織缺乏異常藥物。我覺得有些東西缺失了