1. 程式人生 > 其它 >【第3版emWin教程】第31章 emWin6.x的全字型檔的實現(GB2312編碼,SPI Flash方案)

【第3版emWin教程】第31章 emWin6.x的全字型檔的實現(GB2312編碼,SPI Flash方案)

教程不斷更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429

第31章 emWin6.x的全字型檔的實現(GB2312編碼,SPI Flash方案)

本章節為大家講解GB2312編碼全字型檔的實現,對於習慣了GB2312編碼的使用者來說,使用本章節的方法非常合適。emWin本身是不支援GB2312編碼字元顯示的,本章節是新建立一種字型型別來實現GB2312編碼字元的顯示,所採用的方式是早期UCGUI3.98時期遺留下來,但對那種方法進行了修改,以適合高版本emWin6.xx的使用。

31.1初學者重要提示

31.2 下載演算法存放位置(操作前必看)

31.3 GB2312編碼全字型檔說明

31.4 GB2312全字型檔的移植方法

31.5 移植檔案簡易說明

31.6 GB2312字型檔使用方法

31.7 內部Flash和SPI Flash程式除錯下載配置(重要必看)

31.8 實驗例程說明(RTOS)

31.9 實驗例程說明(裸機)

31.10 總結

31.1 初學者重要提示

  1. 對於不習慣前面章節講解的XBF格式和SIF格式的Unicode編碼全字型檔的使用者來說,使用GB2312編碼是很好的選擇,很適合初學者,漢字操作方式與大家使用裸機程式碼(沒有使用GUI)時是一樣的。
  2. 下載本章節相關例子前,務必先新增好SPI Flash的下載演算法。本章使用的方法支援內部Flash和外部SPI Flash可以同時下載。如此以來,大家可以方便的將字型檔,相簿和主題存到外部SPI Flash,簡單易用,大大方便大家專案實戰。
  3. GB2312編碼的全字型檔檔案可以存到任何外部儲存介質中。本章節配套例子是將其儲存到SPI Flash裡面了。
  4. 使用GB2312編碼也是有缺點的,相比前面章節使用FontCvt生成的XBF格式和SIF格式全字型檔,GB2312編碼全字型檔不支援抗鋸齒效果,且僅支援等寬字型(僅支援等寬是因為當前新字型的建立方法不支援非等寬字型)。

31.2 下載演算法存放位置(操作前必看)

(注:例子下載地址 http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

編譯例子:V7-065_SPI Flash的MDK下載算法制作,生成的演算法檔案位於此路徑下:

生成演算法檔案後,需要大家將其存到到MDK安裝目錄,有兩個位置可以存放,任選其一,推薦第2種:

  • 第1種:存放到MDK的STM32H7軟包安裝目錄裡面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(軟包版本不同,數值2.6.0不同)。
  • 第2種:MDK的安裝目錄 \ARM\Flash裡面。

31.3 GB2312編碼全字型檔說明

本章節配套例子使用的字型檔是從字型檔晶片提取出來的,下面是點陣字型檔相關資訊,僅列出了要用到的點陣字元:

瞭解了點陣字型的相關資訊後,剩下就是定址演算法了。

漢字點陣在漢字型檔中的地址計算:

漢字型檔種類繁多,但都是按照區位的順序排列的。前一個位元組為該漢字的區號,後一個位元組為該字的位號,位號是該字在該區中的位置。GB2312編碼區範圍是A1A1到F7FE,一共F7-A1+1 = 87個區,每區有FE-A1+1=94個字元,因此GB2312可以表示87*94=8178個字元。

計算公式為:(94 * (區號 - 1) + 位號 - 1) * 一個漢字字模佔用位元組數

我們在計算機中常用的漢字編碼為漢字內碼,不是區位碼,需要進行轉換。因此最終的計算公式為:

ADDRESS = [(內碼1 - 0xa1) * 94 + (內碼2 - 0xa1)] *一個漢字字模佔用位元組數(內碼1對應區號,內碼2對應位號)

ASCII區,GB2312編碼的全形字元區和漢字區的所有字元可以看下這個帖子:

http://bbs.armfly.com/read.php?tid=201 ,初學者務必看下,非常有必要。

下面對這8種點陣依次做下說明:

  • 漢字點陣GB2312編碼字型檔地址計算

GBCode表示漢字內碼。

MSB 表示漢字內碼GBCode的高8bits,

LSB 表示漢字內碼GBCode的低8bits。

Address 表示漢字或ASCII字元點陣在晶片中的位元組地址。

BaseAdd:說明點陣資料在字型檔中的起始地址。

11*12點陣計算方法:

BaseAdd=0x0;

if(MSB >=0xA1 && MSB <= 0Xa9 && LSB >=0xA1)

Address =( (MSB – 0xA1) * 94 + (LSB – 0xA1))*24+ BaseAdd;

else if(MSB >=0xB0 && MSB <= 0xF7 && LSB >=0xA1)

Address = ((MSB – 0xB0) * 94 + (LSB – 0xA1)+ 846)*24+ BaseAdd;

15*16點陣計算方法:

BaseAdd=0x2C9D0;

if(MSB >=0xA1 && MSB <= 0Xa9 && LSB >=0xA1)

Address =( (MSB - 0xA1) * 94 + (LSB - 0xA1))*32+ BaseAdd;

else if(MSB >=0xB0 && MSB <= 0xF7 && LSB >=0xA1)

Address = ((MSB - 0xB0) * 94 + (LSB - 0xA1)+ 846)*32+ BaseAdd;

24*24點陣計算方法:

BaseAdd=0x68190;

if(MSB >=0xA1 && MSB <= 0Xa9 && LSB >=0xA1)

Address =( (MSB - 0xA1) * 94 + (LSB - 0xA1))*72+ BaseAdd;

else if(MSB >=0xB0 && MSB <= 0xF7 && LSB >=0xA1)

Address = ((MSB - 0xB0) * 94 + (LSB - 0xA1)+ 846)*72+ BaseAdd;

32*32點陣計算方法:

BaseAdd=0XEDF00;

if(MSB >=0xA1 && MSB <= 0Xa9 && LSB >=0xA1)

Address =( (MSB - 0xA1) * 94 + (LSB - 0xA1))*128+ BaseAdd;

else if(MSB >=0xB0 && MSB <= 0xF7 && LSB >=0xA1)

Address = ((MSB - 0xB0) * 94 + (LSB - 0xA1)+ 846)*128+ BaseAdd;

這四種點陣字型除了每個點陣字元的位元組數和起始地址不一樣,其餘都是一樣的。其中第一個if條件語句是判斷區號在0xA1到0xA8裡面的全形字元區,共846個字元。第二個else if條件語句是判斷區號在0xB0到0xF7裡面的漢字區,共6763個漢字。

  • ASCII字元地址計算

ASCIICode:表示 ASCII 碼( 8bits)

BaseAdd:說明該套字型檔在晶片中的起始地址。

Address: ASCII 字元點陣在晶片中的位元組地址。

6x12點陣ASCII計算方法:

BaseAdd=0x1DBE00

if (ASCIICode >= 0x20) and (ASCIICode <= 0x7E)

Address = (ASCIICode –0x20 ) * 12+BaseAdd

8x16點陣ASCII計算方法:

BaseAdd=0x1DD780

if (ASCIICode >= 0x20) and (ASCIICode <= 0x7E)

Address = (ASCIICode –0x20 ) * 16+BaseAdd

12x24點陣ASCII計算方法:

BaseAdd=0x1DFF00

if (ASCIICode >= 0x20) and (ASCIICode <= 0x7E)

Address = (ASCIICode –0x20 ) * 48+BaseAdd

16x32點陣ASCII計算方法:

BaseAdd=0x1E5A50

if (ASCIICode >= 0x20) and (ASCIICode <= 0x7E)

Address = (ASCIICode –0x20 ) * 64+BaseAdd

這四種點陣字型除了每個點陣字元的位元組數和起始地址不一樣,其餘都是一樣的。每個點陣都是隻用到了0x20到0x7E,共96個字元。

---------------------------------------------------------------

講解完編碼地址的計算後,再說一個比較重要的知識點,初學者容易在這個問題上面犯迷糊。漢字內碼在檔案裡面儲存的時候就是按照高低位元組依次儲存的,比如漢字“我”的GB2312編碼是0xced2,漢字“們”的編碼是0xc3c7,現在我們在電腦端新建一個記事本檔案,然後將“我們”這兩個字寫到電腦端的記事本里面,然後儲存,文字編碼型別選擇ANSI即可。

此時將這個文字檔案用winhex開啟,可以看到,編碼數值如下:

跟漢字的編碼值對比後發現漢字的編碼值儲存就是高低位元組依次儲存的。那麼問題來了,一般情況下,使用MCU微控制器的時候都是用的小端模式,即低地址儲存低位資料,高地址儲存高位資料。現在我們採用如下的程式做簡單的測試:

char *ptr = {"我們"};
U16 c1, c2;
    
c1 = *(U16 *)ptr;
c2 = *(U16 *)(ptr + 2);
printf("c1 = %x, c2 = %x\r\n", c1, c2);

串列埠列印的輸出結果就是 c1 = 0xd2ce , c2 = 0xc7c3。正好與漢字內碼的高低位元組反過來了,初學者在學習的時候務必要注意這個問題。

31.4 GB2312全字型檔的移植方法

這裡用到的幾個移植檔案是早期UCGUI3.XX版本時代遺留下來的,但都進行了修改,更加適合emWin6.xx版本使用(由於早期UCGUI3.XX版本是有原始碼的,這種方式是在原始碼的基礎上新建立的一種字型方式,適合國內用的GB編碼)。

GUI_Font12.c,GUI_Font16.c,GUI_Font24.c和GUI_Font32.c都是相同的程式碼結構,僅僅是定義的點陣大小不同,使用者要實現其它點陣大小的字型,只需根據這幾個檔案照葫蘆畫瓢即可。剩下的兩個檔案GUI_UC_EncondeNone.c和GUICharPEx.c是必須要包含的,使用者需要根據字型檔的起始位置做修改(如何修改,看本章節的31.2小節,本章節配套的例子不用改,因為已經根據31.2小節的起始地址設定好了)。

接下來要做的移植工作比較簡單,僅需兩步即可完成:

1、第1步:將圖31-1中的6個檔案全部新增到工程中,IAR和MDK是一樣的,下面以MDK為例進行說明。

上面的截圖就是新增到MDK工程的效果。如果使用者要移植這幾個檔案到自己的工程專案,直接從本章節配套例子的emWin資料夾下開啟檔案HanZi就看到這幾個檔案了,所以使用者只需複製貼上HanZi到自己的工程專案即可。

2、第2步:在GUI_Type.h檔案新增一種新的字型型別定義

新新增的字型型別如下:

DECLARE_FONT(USER);

#define GUI_FONTTYPE_USER \

GUIUSER_DispChar, \

GUIUSER_GetCharDistX, \

GUIMONO_GetFontInfo, \

GUIMONO_IsInFont, \

(GUI_GETCHARINFO *)0, \

(tGUI_ENC_APIList*)0

此定義放在等寬字型定義的前面即可:

通過這兩步就完成移植了,剩下就是如何使用了。不過第2步裡面新新增的函式有必要簡單的解釋一下。

  • 首先是函式DECLARE_FONT(USER),這個函式的原始定義如下:
#define DECLARE_FONT(Type)                                     \
void GUI##Type##_DispChar    (U16P c);                         \
int  GUI##Type##_GetCharDistX(U16P c, int * pSizeX);                       \
void GUI##Type##_GetFontInfo (const GUI_FONT * pFont, GUI_FONTINFO * pfi); \
char GUI##Type##_IsInFont    (const GUI_FONT * pFont, U16 c); \
int  GUI##Type##_GetCharInfo (U16P c, GUI_CHARINFO_EXT * pInfo)

##是預處理運算子,這種方式有效的對巨集定義進行了擴充套件,DECLARE_FONT裡面的引數是可以任意定義的,只要滿足函式命名即可,比如使用者呼叫了DECLARE_FONT(USER),就相當於一次聲明瞭5個函式:

void GUIUSER_DispChar    (U16P c);                         \
int  GUIUSER _GetCharDistX(U16P c, int * pSizeX);                       \
void GUIUSER _GetFontInfo (const GUI_FONT * pFont, GUI_FONTINFO * pfi); \
char GUIUSER _IsInFont    (const GUI_FONT * pFont, U16 c); \
int  GUIUSER _GetCharInfo (U16P c, GUI_CHARINFO_EXT * pInfo)

也就是將Type前後的預處理運算子##去掉,並將Type用USER代替。在GUI_Type.h檔案裡面還有其它幾種字型型別的定義,都是這個意思。

DECLARE_FONT(MONO);
DECLARE_FONT(PROP);
DECLARE_FONT(PROP_EXT);
DECLARE_FONT(PROP_FRM);
DECLARE_FONT(PROPAA);
DECLARE_FONT(PROP_AA2);
DECLARE_FONT(PROP_AA2_EXT);
DECLARE_FONT(PROP_AA4);
DECLARE_FONT(PROP_AA4_EXT);
DECLARE_FONT(USER);
  • 再說下巨集定義#define GUI_FONTTYPE_USER

這種形式的巨集定義也很少見,巨集定義後面跟了好幾個子函式。其實就是一種簡單的替換,在程式程式碼遇到GUI_FONTTYPE_USER的地方,就用下面的代替

GUIUSER_DispChar, \

GUIUSER_GetCharDistX, \

GUIMONO_GetFontInfo, \

GUIMONO_IsInFont, \

(GUI_GETCHARINFO *)0, \

(tGUI_ENC_APIList*)0

此巨集定義是在檔案GUI_Font12.c,GUI_Font16.c,GUI_Font24.c和GUI_Font32.c裡面被呼叫。關於替代的這幾個函式有必要再進一步的闡釋下。

函式GUIUSER_DispChar和GUIUSER_GetCharDistX是要使用者自己實現的,已經在GUICharPEx.c裡面實現了。函式GUIUSER_GetFontInfo和GUIUSER_IsInFont沒有實現,而是用emWin等寬字型的GUIMONO_GetFontInfo和GUIMONO_IsInFont實現的。剩下的兩個函式(GUI_GETCHARINFO *)0和(tGUI_ENC_APIList*)0未做實現,使用emWin自帶的方式來實現。講解到這裡,初學者會有疑問,為什麼字型型別要定義這幾種函式?其實這個是由emWin的字型型別變數決定的,定義如下:

struct GUI_FONT {
  GUI_DISPCHAR     * pfDispChar; 
  GUI_GETCHARDISTX * pfGetCharDistX; 
  GUI_GETFONTINFO  * pfGetFontInfo; 
  GUI_ISINFONT     * pfIsInFont;
  GUI_GETCHARINFO  * pfGetCharInfo;
  const tGUI_ENC_APIList* pafEncode;
  U8 YSize;
  U8 YDist;
  U8 XMag;
  U8 YMag;
  union {
    const void              * pFontData;
    const GUI_FONT_MONO     * pMono;
    const GUI_FONT_PROP     * pProp;
    const GUI_FONT_PROP_EXT * pPropExt;
  } p;
  U8 Baseline;
  U8 LHeight;     /* Height of a small lower case character (a,x) */
  U8 CHeight;     /* Height of a small upper case character (A,X) */
};

結構體GUI_FONT前六個成員就是我們這裡實現的,其餘的幾種字型型別全是如此:

DECLARE_FONT(MONO);
DECLARE_FONT(PROP);
DECLARE_FONT(PROP_EXT);
DECLARE_FONT(PROP_FRM);
DECLARE_FONT(PROPAA);
DECLARE_FONT(PROP_AA2);
DECLARE_FONT(PROP_AA2_EXT);
DECLARE_FONT(PROP_AA4);
DECLARE_FONT(PROP_AA4_EXT);

/* MONO: Monospaced fonts */
#define GUI_FONTTYPE_MONO       \
  GUIMONO_DispChar,             \
  GUIMONO_GetCharDistX,         \
  GUIMONO_GetFontInfo,          \
  GUIMONO_IsInFont,             \
  (GUI_GETCHARINFO *)0,         \
  (tGUI_ENC_APIList*)0

/* PROP: Proportional fonts */
#define GUI_FONTTYPE_PROP       \
  GUIPROP_DispChar,             \
  GUIPROP_GetCharDistX,         \
  GUIPROP_GetFontInfo,          \
  GUIPROP_IsInFont,             \
  (GUI_GETCHARINFO *)0,         \
  (tGUI_ENC_APIList*)0

/* PROP_EXT: Extended proportional fonts */
#define GUI_FONTTYPE_PROP_EXT       \
  GUIPROP_EXT_DispChar,             \
  GUIPROP_EXT_GetCharDistX,         \
  GUIPROP_EXT_GetFontInfo,          \
  GUIPROP_EXT_IsInFont,             \
  GUIPROP_EXT_GetCharInfo,          \
  &GUI_ENC_APIList_EXT

/* PROP_FRM: Extended proportional fonts, framed */
#define GUI_FONTTYPE_PROP_FRM       \
  GUIPROP_FRM_DispChar,             \
  GUIPROP_FRM_GetCharDistX,         \
  GUIPROP_FRM_GetFontInfo,          \
  GUIPROP_FRM_IsInFont,             \
  (GUI_GETCHARINFO *)0,             \
  (tGUI_ENC_APIList*)0

/* PROP: Proportional fonts SJIS */
#define GUI_FONTTYPE_PROP_SJIS  \
  GUIPROP_DispChar,             \
  GUIPROP_GetCharDistX,         \
  GUIPROP_GetFontInfo,          \
  GUIPROP_IsInFont,             \
  (GUI_GETCHARINFO *)0,         \
  &GUI_ENC_APIList_SJIS

/* PROPAA: Proportional, antialiased fonts */
#define GUI_FONTTYPE_PROPAA       \
  GUIPROPAA_DispChar,             \
  GUIPROPAA_GetCharDistX,         \
  GUIPROPAA_GetFontInfo,          \
  GUIPROPAA_IsInFont,             \
  (GUI_GETCHARINFO *)0,           \
  (tGUI_ENC_APIList*)0

/* PROP_AA2: Proportional, antialiased fonts, 2bpp */
#define GUI_FONTTYPE_PROP_AA2       \
  GUIPROP_AA2_DispChar,             \
  GUIPROP_AA2_GetCharDistX,         \
  GUIPROP_AA2_GetFontInfo,          \
  GUIPROP_AA2_IsInFont,             \
  (GUI_GETCHARINFO *)0,             \
  (tGUI_ENC_APIList*)0

/* PROP_AA2_EXT: Proportional, antialiased fonts, 2bpp, extended font information */
#define GUI_FONTTYPE_PROP_AA2_EXT   \
  GUIPROP_AA2_EXT_DispChar,         \
  GUIPROP_EXT_GetCharDistX,         \
  GUIPROP_EXT_GetFontInfo,          \
  GUIPROP_EXT_IsInFont,             \
  GUIPROP_EXT_GetCharInfo,          \
  &GUI_ENC_APIList_EXT

/* PROP_AA2: Proportional, antialiased fonts, 2bpp, SJIS encoding */
#define GUI_FONTTYPE_PROP_AA2_SJIS  \
  GUIPROP_AA2_DispChar,             \
  GUIPROP_AA2_GetCharDistX,         \
  GUIPROP_AA2_GetFontInfo,          \
  GUIPROP_AA2_IsInFont,             \
  (GUI_GETCHARINFO *)0,             \
  &GUI_ENC_APIList_SJIS

/* PROP_AA4: Proportional, antialiased fonts, 4bpp */
#define GUI_FONTTYPE_PROP_AA4       \
  GUIPROP_AA4_DispChar,             \
  GUIPROP_AA4_GetCharDistX,         \
  GUIPROP_AA4_GetFontInfo,          \
  GUIPROP_AA4_IsInFont,             \
  (GUI_GETCHARINFO *)0,             \
  (tGUI_ENC_APIList*)0

/* PROP_AA4_EXT: Proportional, antialiased fonts, 4bpp, extended font information */
#define GUI_FONTTYPE_PROP_AA4_EXT   \
  GUIPROP_AA4_EXT_DispChar,         \
  GUIPROP_EXT_GetCharDistX,         \
  GUIPROP_EXT_GetFontInfo,          \
  GUIPROP_EXT_IsInFont,             \
  GUIPROP_EXT_GetCharInfo,          \
  &GUI_ENC_APIList_EXT

/* PROP_AA4: Proportional, antialiased fonts, 4bpp, SJIS encoding */
#define GUI_FONTTYPE_PROP_AA4_SJIS  \
  GUIPROP_AA4_DispChar,             \
  GUIPROP_AA4_GetCharDistX,         \
  GUIPROP_AA4_GetFontInfo,          \
  GUIPROP_AA4_IsInFont,             \
  (GUI_GETCHARINFO *)0,             \
  &GUI_ENC_APIList_SJIS

關於移植部分就給大家講解這麼多,下面對這幾個移植的檔案簡單的說說。

31.5 移植檔案簡易說明

31.5.1 檔案GUICharPEx.c

此檔案主要是實現了新定義字型裡面的API函式GUIUSER_DispChar和GUIUSER_GetCharDistX,其實這兩個函式是由UCGUI3.98原始碼裡面的檔案GUICharP.c修改而來。關於這兩個函式暫不做講解,使用者使用的時候也不需要對這兩個函式做修改,接下來重點看函式:

static void GUI_GetDataFromMemory(const GUI_FONT_PROP GUI_UNI_PTR *pProp, U16P c)

的實現,這個函式是需要使用者去實現的,根據使用的外部儲存器和漢字型檔地址實現點陣資料的讀取。下面是根據31.2小節的說明,實現從SPI Flash裡面讀取點陣資料:

/* 點陣資料快取, 必須大於等於單個字模需要的儲存空間 */ 
#define BYTES_PER_FONT      512 
static U8 GUI_FontDataBuf[BYTES_PER_FONT];

/*
*********************************************************************************************************
*    函 數 名: GUI_GetDataFromMemory
*    功能說明: 讀取點陣資料
*    形    參: pProp  GUI_FONT_PROP型別結構
*             c      字元
*    返 回 值: 無
*********************************************************************************************************
*/
static void GUI_GetDataFromMemory(const GUI_FONT_PROP GUI_UNI_PTR *pProp, U16P c) 
{ 
    U16 BytesPerFont; 
    U32 oft = 0, BaseAdd; 
U8 code1,code2;
    
    char *font = (char *)pProp->paCharInfo->pData; 

    /* 每個字模的資料位元組數 */
    BytesPerFont = GUI_pContext->pAFont->YSize * pProp->paCharInfo->BytesPerLine; 
    if (BytesPerFont > BYTES_PER_FONT)
    {
        BytesPerFont = BYTES_PER_FONT;
    }
    
    /* 英文字元地址偏移計算 */ 
    if (c < 0x80)                                                                
    { 
        if(strncmp("A12", font, 3) == 0)     /* 6*12 ASCII字元 */
        {
            BaseAdd = 0x1DBE00;
        }
        else if(strncmp("A16", font, 3) == 0) /* 8*16 ASCII字元 */
        {
            BaseAdd = 0x1DD780;
        }
        else if(strncmp("A24", font, 3) == 0) /* 12*24 ASCII字元 */
        {
            BaseAdd = 0x1DFF00;
        }
        else if(strncmp("A32", font, 3) == 0) /* 24*48 ASCII字元 */
        {
            BaseAdd = 0x1E5A50;
        }
        
        oft = (c-0x20) * BytesPerFont + BaseAdd; 
    } 
    /* 漢字和全形字元的偏移地址計算 */
    else                                                                           
    { 
        if(strncmp("H12", font, 3) == 0)      /* 12*12 字元 */
        {
            BaseAdd = 0x0;
        }
        else if(strncmp("H16", font, 3) == 0)  /* 16*16 字元 */
        {
            BaseAdd = 0x2C9D0;
        }
        else if(strncmp("H24", font, 3) == 0)  /* 24*24 字元 */
        {
            BaseAdd = 0x68190;
        }
        else if(strncmp("H32", font, 3) == 0)  /* 32*32 字元 */
        {
            BaseAdd = 0XEDF00;
        }
        
        /* 根據漢字內碼的計算公式鎖定起始地址 */
        code2 = c >> 8;
        code1 = c & 0xFF;
        if (code1 >=0xA1 && code1 <= 0xA9 && code2 >=0xA1)
        {
            oft = ((code1 - 0xA1) * 94 + (code2 - 0xA1)) * BytesPerFont + BaseAdd;
        }
        else if (code1 >=0xB0 && code1 <= 0xF7 && code2 >=0xA1)
        {
            oft = ((code1 - 0xB0) * 94 + (code2 - 0xA1) + 846) * BytesPerFont + BaseAdd;
        }
    }
    
    /* 讀取點陣資料 */
    sf_ReadBuffer(GUI_FontDataBuf, oft, BytesPerFont);
}

漢字和ASCII點陣資料的起始地址計算公式就是前面31.2小節講解的內容,只是都合併成一個公式來實現了。

31.5.2 檔案GUI_UC_EncodeNone.c

這個檔案就是來自UCGUI3.98原始碼,並對其進行了簡單修改,以適合我們的GB2312編碼,特別注意一點,在emWin的原始碼裡面也是有一個此檔案的,添加了這個檔案,會把原有的檔案覆蓋掉。

#include "GUI_Private.h"

/*********************************************************************
*
*       Static code
*
**********************************************************************
*/
/*********************************************************************
*
*       _GetCharCode
*
* Purpose:
*   Return the UNICODE character code of the current character.
*/
static U16 _GetCharCode(const char GUI_UNI_PTR * s) {
  if((*s) > 0xA0) /* ASCII字元編碼是00-7F,返回一個位元組資料即可,GB2312編碼是A1A1到FEFE,需要返回兩個位元組
資料,這裡以第一個位元組作為判斷即可 */
  {
    return *(const U16 GUI_UNI_PTR *)s; 
  }
  return *(const U8 GUI_UNI_PTR *)s;
}

/*********************************************************************
*
*       _GetCharSize
*
* Purpose:
*   Return the number of bytes of the current character.
*/
static int _GetCharSize(const char GUI_UNI_PTR * s) {
  GUI_USE_PARA(s); 
if((*s) > 0xA0) /* ASCII字元編碼是00-7F,返回一個位元組大小,GB2312編碼是A1A1到FEFE,需要返回兩個位元組大小,
這裡以第一個位元組作為判斷即可 */

  {
    return 2; 
  }
  return 1;
}

/*********************************************************************
*
*       _CalcSizeOfChar
*
* Purpose:
*   Return the number of bytes needed for the given character.
*/
static int _CalcSizeOfChar(U16 Char) {
  GUI_USE_PARA(Char);
  if(Char > 0xA0A0) /* ASCII字元編碼是00-7F,返回一個位元組大小,GB2312編碼是A1A1到FEFE,需要返回兩個位元組大
小,這裡以A0A0作為判斷*/
  {
    return 2;
  }
  return 1;
}

/*********************************************************************
*
*       _Encode
*
* Purpose:
*   Encode character into 1/2/3 bytes.
*/
static int _Encode(char *s, U16 Char) {
  if(Char > 0xA0A0) /* ASCII字元編碼是00-7F,返回一個位元組大小,GB2312編碼是A1A1到FEFE,需要返回兩個位元組大
小,這裡以A0A0作為判斷,並將編碼中賦值給形參s */
  {
    *((U16 *)s) = (U16)(Char);
    return 2;
  }
  *s = (U8)(Char);
  return 1;
}

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/
/*********************************************************************
*
*       _API_Table
*/
const GUI_UC_ENC_APILIST GUI__API_TableNone = {
  _GetCharCode,     /*  return character code as U16 */
  _GetCharSize,     /*  return size of character: 1 */
  _CalcSizeOfChar,  /*  return size of character: 1 */
  _Encode           /*  Encode character */
};

const GUI_UC_ENC_APILIST GUI_UC_None = {
  _GetCharCode,     /*  return character code as U16 */
  _GetCharSize,     /*  return size of character: 1 */
  _CalcSizeOfChar,  /*  return size of character: 1 */
  _Encode           /*  Encode character */
};

31.5.3 檔案GUI_Font12.c,GUI_Font16.c等

這幾個檔案都是同一個架構,我們這裡以GUI_Font16.c檔案為例進行說明。

#include "GUI.h"
#include "GUI_Type.h"


GUI_CONST_STORAGE GUI_CHARINFO GUI_FontHZ16_CharInfo[2] =  // ----------(1)
{     
    {  8,    8,  1, (void *)"A16"},      
    {  16,  16,  2, (void *)"H16"},           
}; 

GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ16_PropHZ= {   // ----------(2)
      0xA1A1,  
      0xFEFE,  
      &GUI_FontHZ16_CharInfo[1], 
      (void *)0,  
}; 

GUI_CONST_STORAGE  GUI_FONT_PROP GUI_FontHZ16_PropASC= {  // ----------(3)
      0x0000,  
      0x007F,  
      &GUI_FontHZ16_CharInfo[0], 
      (void GUI_CONST_STORAGE *)&GUI_FontHZ16_PropHZ,  
}; 

GUI_CONST_STORAGE  GUI_FONT GUI_FontHZ16 =              // ----------(4)
{ 
      GUI_FONTTYPE_USER,
      16,  
      16,  
      1,   
      1,   
      (void GUI_CONST_STORAGE *)&GUI_FontHZ16_PropASC,
}; 
GUI_CONST_STORAGE  GUI_FONT GUI_FontHZ16x2 =            // ----------(5)
{ 
      GUI_FONTTYPE_USER,
      16,  
      16,  
      2,   
      2,   
      (void GUI_CONST_STORAGE *)&GUI_FontHZ16_PropASC 
};

1、第1個語句:GUI_CONST_STORAGE GUI_CHARINFO GUI_FontHZ16_CharInfo[2]

瞭解這個函式得了解GUI_CHARINFO的定義,定義如下:

typedef struct {
  U8 XSize;
  U8 XDist;
  U8 BytesPerLine;
  const unsigned char * pData;
} GUI_CHARINFO;

第1個成員U8 Xsize是點陣字元的X軸方向的長度。

第2個成員U8 XDist是實際顯示時X軸方向的長度。

第3個成員 U8 BytesPerLine是每行點陣資料需要的位元組數。

第4個成員const unsigned char *pData用來做一些字元標記。

認識這幾個變數成員後,如下兩個變數成員也就不難理解了:

{ 8, 8, 1, (void *)"A16"}

表示此點陣字元的X軸長度是8個畫素點,實際顯示也是8個畫素點,顯示一行需要1個位元組就行,用字元A16作為識別標誌,這一行的整體作用是用來識別16點陣ASCII。

{ 16, 16, 2, (void *)"H16"}

表示此點陣字元的X軸長度是16個畫素點,實際顯示也是16個畫素點,顯示一行需要2個位元組就行,用字元H16作為識別標誌,這一行的整體作用是用來識別16點陣漢字。

2、第2個語句:GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ16_PropHZ

瞭解這個函式得了解GUI_FONT_PROP的定義,定義如下:

typedef struct GUI_FONT_PROP {
  U16P First;                                  /* First character               */
  U16P Last;                                   /* Last character                */
  const GUI_CHARINFO         * paCharInfo;     /* Address of first character    */
  const struct GUI_FONT_PROP * pNext;          /* Pointer to next               */
} GUI_FONT_PROP;

第1個成員U16P First是第一個字元的編碼值。

第2個成員U16P First是最後一個字元的編碼值。

第3個成員 const GUI_CHARINFO * paCharInfo記錄了第一個字元的儲存位置。

第4個成員const struct GUI_FONT_PROP * pNext用來指向下一個GUI_FONT_PROP定義的字元區間。

對結構體GUI_FONT_PROP的定義瞭解後,這裡定義的如下四個引數:

0xA1A1,

0xFEFE,

&GUI_FontHZ16_CharInfo[1],

(void *)0,

表示此編碼區的字元是從0XA1A1開始到0XFEFE。字元的位置由GUI_FontHZ16_CharInfo[1]記錄,沒有指向下一個編碼範圍。這裡的編碼就是GB2312的編碼範圍。

3、第3個語句:GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ16_PropASC

有了上面第二句的解釋,第三句也比較好理解了。

這裡表示此編碼區的字元是從0x0000開始到0x007F。字元的位置由GUI_FontHZ16_CharInfo[0]記錄,指向下一個編碼範圍就是第二句的GB2312編碼區。這裡的編碼就是ASCII的編碼範圍。

4、第4個語句:GUI_CONST_STORAGE GUI_FONT GUI_FontHZ16

此句就是根據前面25.3小節末尾新建立字型型別所定義的一個16點陣字型。

GUI_CONST_STORAGE  GUI_FONT GUI_FontHZ16 =  
{ 
GUI_FONTTYPE_USER,     /* 表示新定義的字型型別 */
16,                    /* 字型Y軸方向的點陣高度 */
16,                    /* 字型Y軸方向的實際顯示點陣高度 */
1,                     /* 字型X軸方向放大到1倍,就是原始大小 */
1,                     /* 字型Y軸方向放大到1倍 ,就是原始大小 */
(void GUI_CONST_STORAGE*)&GUI_FontHZ16_PropASC,/* 此字型型別從GUI_FontHZ16_PropASC */
};

5、第5個語句:GUI_CONST_STORAGE GUI_FONT GUI_FontHZ16x2

跟第4句一樣,區別的地方就是將字型放大了1倍。

GUI_CONST_STORAGE  GUI_FONT GUI_FontHZ16 =  
{ 
GUI_FONTTYPE_USER,     /* 表示新定義的字型型別 */
16,                    /* 字型Y軸方向的點陣高度 */
16,                    /* 字型Y軸方向的實際顯示點陣高度 */
2,                     /* 字型X軸方向放大到2倍 */
2,                     /* 字型Y軸方向放大到2倍 */
(void GUI_CONST_STORAGE*)&GUI_FontHZ16_PropASC,/* 此字型型別從GUI_FontHZ16_PropASC */
};

關於這個幾個移植檔案就跟大家講解這麼多。這個就是GUI_Font16.C的講解,其它的GUI_Font12.c, GUI_Font24.c和GUI_Font32.c是一樣的。

31.6 GB2312字型檔使用方法

移植好之後,使用就比較容易了,下面分兩步進行說明

第1步:宣告12點陣,16點陣,24點陣和32點陣的字型,放在了MainTask.h檔案裡面,方便呼叫,程式程式碼中只需包含這個標頭檔案就可以了。

/*
*********************************************************************************************************
*                                          字型宣告
*********************************************************************************************************
*/
extern GUI_CONST_STORAGE  GUI_FONT GUI_FontHZ32;
extern GUI_CONST_STORAGE  GUI_FONT GUI_FontHZ24;
extern GUI_CONST_STORAGE  GUI_FONT GUI_FontHZ16;
extern GUI_CONST_STORAGE  GUI_FONT GUI_FontHZ12;

有幾種點陣字型檔就通過extern宣告幾個,方便外部檔案呼叫。

第2步:使用方法。

比如設定按鈕的字型使用16點陣,呼叫如下設定函式即可。

BUTTON_SetFont(hWin, &GUI_FontHZ16); /* hWin是按鈕的控制代碼 */

設定其它控制元件是一樣的。另外注意,使用GB編碼沒有煩人的檔案編碼設定問題了,從這一點來說,比使用前面章節講的XBF格式和SIF格式的Unicode編碼方便很多了。

31.7 內部Flash和SPI Flash程式除錯下載配置(重要必看)

將下面兩個地方配置後,就可以像使用內部Flash一樣使用SPI Flash進行除錯了。並且這種方式可以方便的除錯程式,內部Flash和外部Flash都做除錯。

31.7.1 將字型檔檔案轉換為C陣列格式檔案

為了方便將bin檔案新增到MDK工程中,我們這裡使用小軟體B2C.exe將其轉換為C格式檔案(此軟體已經放到本章配套例子V7-538_emWin6.x實驗_全字型檔實現,GB2312編碼(SPI Flash RTOS)的Doc檔案裡面。

轉換後生成的檔案命名為gb2312.c :

const unsigned char _acgb2312[2097152UL + 1] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00,
  0x0C, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00  省略未寫

}

31.7.2 設定字型檔檔案到外部SPI Flash。

下面將流點陣圖檔案下載到SPI Flash,需要大家先在這裡新增SPI Flash地址範圍:

然後設定資原始檔到外部SPI Flash:滑鼠右擊檔案分組GUI/Font,選擇Options。

31.7.3 下載配置

注意這裡一定要夠大,否則會提示演算法檔案無法載入:

我們這裡是將其加到DTCM中,即首地址為0x20000000,大家也可以儲存到任意其它RAM地址,只要空間還夠載入演算法檔案即可。推薦使用AXI SRAM(地址0x24000000),因為這塊RAM空間足夠大。

如果要下載程式到內部Flash和外部SPI Flash裡面,需要做如下配置,兩個下載演算法都要新增進來:

31.8 實驗例程說明(RTOS)

配套例子:

V7-538_emWin6.x實驗_全字型檔實現,GB2312編碼(SPI Flash RTOS)

實驗目的:

  1. 學習emWin的GB2312編碼全字型檔的使用方法。
  2. emWin功能的實現在MainTask.c檔案裡面。

實驗內容:

1、K1按鍵按下,串列埠或者RTT列印任務執行情況(串列埠波特率115200,資料位8,奇偶校驗位無,停止位1)。

2、(1) 凡是用到printf函式的全部通過函式App_Printf實現。

(2) App_Printf函式做了訊號量的互斥操作,解決資源共享問題。

3、預設上電是通過串列埠列印資訊,如果使用RTT列印資訊:

MDK AC5,MDK AC6或IAR通過使能bsp.h檔案中的巨集定義為1即可

#define Enable_RTTViewer 1

4、各個任務實現的功能如下:

App Task Start 任務 :啟動任務,這裡用作BSP驅動包處理。

App Task MspPro任務 :訊息處理,這裡用作LED閃爍。

App Task UserIF 任務 :按鍵訊息處理。

App Task COM 任務 :暫未使用。

App Task GUI 任務 :GUI任務。

μCOS-III任務除錯資訊(按K1按鍵,串列埠列印):

RTT 列印資訊方式:

程式設計:

任務棧大小分配:

μCOS-III任務棧大小在app_cfg.h檔案中配置:

#define APP_CFG_TASK_START_STK_SIZE 512u

#define APP_CFG_TASK_MsgPro_STK_SIZE 2048u

#define APP_CFG_TASK_COM_STK_SIZE 512u

#define APP_CFG_TASK_USER_IF_STK_SIZE 512u

#define APP_CFG_TASK_GUI_STK_SIZE 2048u

任務棧大小的單位是4位元組,那麼每個任務的棧大小如下:

App Task Start 任務 :2048位元組。

App Task MspPro任務 :8192位元組。

App Task UserIF 任務 :2048位元組。

App Task COM 任務 :2048位元組。

App Task GUI 任務 :8192位元組。

系統棧大小分配:

μCOS-III的系統棧大小在os_cfg_app.h檔案中配置:

#define OS_CFG_ISR_STK_SIZE 512u

系統棧大小的單位是4位元組,那麼這裡就是配置系統棧大小為2KB

emWin動態記憶體配置:

GUIConf.c檔案中的配置如下:

#define EX_SRAM   1/*1 used extern sram, 0 used internal sram */

#if EX_SRAM
#define GUI_NUMBYTES  (1024*1024*24)
#else
#define GUI_NUMBYTES  (100*1024)
#endif

通過巨集定義來配置使用內部SRAM還是外部的SDRAM做為emWin的動態記憶體,當配置:

#define EX_SRAM 1 表示使用外部SDRAM作為emWin動態記憶體,大小24MB。

#define EX_SRAM 0 表示使用內部SRAM作為emWin動態記憶體,大小100KB。

預設情況下,本教程配套的所有emWin例子都是用外部SDRAM作為emWin動態記憶體。

emWin介面顯示效果:

800*480解析度介面效果。

31.9 實驗例程說明(裸機)

配套例子:

V7-537_emWin6.x實驗_全字型檔實現,GB2312編碼(SPI Flash裸機)

實驗目的:

  1. 學習emWin的的SIF格式全字型檔的生成和使用方法,Unicode編碼
  2. emWin功能的實現在MainTask.c檔案裡面。

emWin介面顯示效果:

800*480解析度介面效果。

emWin動態記憶體配置:

GUIConf.c檔案中的配置如下:

#define EX_SRAM   1/*1 used extern sram, 0 used internal sram */

#if EX_SRAM
#define GUI_NUMBYTES  (1024*1024*24)
#else
#define GUI_NUMBYTES  (100*1024)
#endif

通過巨集定義來配置使用內部SRAM還是外部的SDRAM做為emWin的動態記憶體,當配置:

#define EX_SRAM 1 表示使用外部SDRAM作為emWin動態記憶體,大小24MB。

#define EX_SRAM 0 表示使用內部SRAM作為emWin動態記憶體,大小100KB。

預設情況下,本教程配套的所有emWin例子都是用外部SDRAM作為emWin動態記憶體。

31.10 總結

本章節為大家講解的GB2312編碼格式字型是可以用於專案實戰的,對於習慣了GB2312編碼的使用者來說是個很好的選擇。這種方式的字型可以儲存到任何外部儲存介質中。

微信公眾號:armfly_com 安富萊論壇:www.armbbs.cn 安富萊淘寶:https://armfly.taobao.com