目錄
方式一 使用FontCvtST.exe將需要使用的漢子製作成字庫,字庫以數組方式存在於c文件中
方式二 使用“點陣字庫生成器V4.0 易木雨軟件工作室”生成完整的字庫,字庫存放在SPI FLASH
本章完整的代碼見:
方式一 使用FontCvtST.exe將需要使用的漢子製作成字庫,字庫以數組方式存在於c文件中
(Tips:該博客假設已經成功移植了STemWin,
RGB屏可參考博客:https://blog.csdn.net/Ningjianwen/article/details/92781418
TFTLCD屏可參考博客:https://blog.csdn.net/Ningjianwen/article/details/90703933)
1.FontCvtST.exe軟件位於STM32Cube_FW_F7_V1.15.0\Middlewares\ST\STemWin\Software, 雙擊打開該軟件
2. 在彈出的選擇框中做如下選擇:
重點:如果需要顯示中文,Encodeing必須選擇"16 Bit UNICODE";字體選擇也必須是包含中文的。
3. 軟件打開後,默認包含所有文字。點擊“Edit-> Disable all characters”菜單,不包含任何字體。
(Tips:白色背景爲選中文字,灰色背景爲未選中文件)
4. 新建一個txt文檔,在該文檔中編寫需要加入的文字,比如“你好”:
重點:該txt文檔的編碼格式必須時UCS-2 Little Endian編碼,否則FontCvtST.exe無法識別。
5. 回到FontCvtST.exe軟件,單擊“Edit->Read pattern file ...”後,選中第4步創建的文本文檔
(Tips:如果彈出“The following character codes found in ...”的對話框,是因爲txt文檔的編碼方式沒有選擇對)
6. 單擊“File -> Save As ...” 生成一個“微軟雅黑24.c”的文件(文件名可以改爲英文的),將該文件添加到我們的工程裏。
#include "GUI.h"
#ifndef GUI_CONST_STORAGE
#define GUI_CONST_STORAGE const
#endif
/* The following line needs to be included in any file selecting the
font.
*/
extern GUI_CONST_STORAGE GUI_FONT GUI_Fonthello24;
/* Start of unicode area <CJK Unified Ideographs> */
GUI_CONST_STORAGE unsigned char acGUI_Fonthello24_4F60[ 51] = { /* code 4F60 */
_____X__,_X______,________,
____XX__,XX______,________,
____X___,X_______,________,
____X___,XXXXXXXX,X_______,
___X___X,________,X_______,
__XX__XX,___X___X,X_______,
__XX__X_,___X___X,________,
_XXX____,___X____,________,
XX_X____,X__X__X_,________,
X__X____,X__X___X,________,
___X___X,___X___X,________,
___X___X,___X____,X_______,
___X__XX,___X____,XX______,
___X__X_,___X____,_X______,
___X____,___X____,________,
___X____,___X____,________,
___X____,XXX_____,________};
GUI_CONST_STORAGE unsigned char acGUI_Fonthello24_597D[ 51] = { /* code 597D */
___X____,________,________,
___X____,XXXXXXXX,X_______,
___X____,_______X,X_______,
__X_____,______XX,________,
XXXXXXX_,_____XX_,________,
__X___X_,____XX__,________,
__X___X_,____X___,________,
_X____X_,____X___,________,
_X___XXX,XXXXXXXX,XX______,
_X___X__,____X___,________,
_XX__X__,____X___,________,
___XXX__,____X___,________,
____X___,____X___,________,
___X_XX_,____X___,________,
__X___XX,____X___,________,
_XX_____,____X___,________,
X_______,_XXX____,________};
GUI_CONST_STORAGE GUI_CHARINFO_EXT GUI_Fonthello24_CharInfo[2] = {
{ 18, 17, 0, 4, 18, acGUI_Fonthello24_4F60 } /* code 4F60 */
,{ 18, 17, 0, 4, 18, acGUI_Fonthello24_597D } /* code 597D */
};
GUI_CONST_STORAGE GUI_FONT_PROP_EXT GUI_Fonthello24_Prop2 = {
0x597D /* first character */
,0x597D /* last character */
,&GUI_Fonthello24_CharInfo[ 1] /* address of first character */
,(GUI_CONST_STORAGE GUI_FONT_PROP_EXT *)0 /* pointer to next GUI_FONT_PROP_EXT */
};
GUI_CONST_STORAGE GUI_FONT_PROP_EXT GUI_Fonthello24_Prop1 = {
0x4F60 /* first character */
,0x4F60 /* last character */
,&GUI_Fonthello24_CharInfo[ 0] /* address of first character */
,&GUI_Fonthello24_Prop2 /* pointer to next GUI_FONT_PROP_EXT */
};
GUI_CONST_STORAGE GUI_FONT GUI_Font24 = {
GUI_FONTTYPE_PROP_EXT /* type of font */
,24 /* height of font */
,24 /* space of font y */
,1 /* magnification x */
,1 /* magnification y */
,{&GUI_Fonthello24_Prop1}
,19 /* Baseline */
,10 /* Height of lowercase characters */
,14 /* Height of capital characters */
};
7. 通過以下三條語句就可以在LCD顯示“你好”兩個漢字
GUI_UC_SetEncodeUTF8(); // 設置編碼
GUI_SetFont(&GUI_Font24); // 設置字體
GUI_DispString("你好\n");
方式二 使用“點陣字庫生成器V4.0 易木雨軟件工作室”生成完整的字庫,字庫存放在SPI FLASH
1. 使用點陣字庫軟件生成字庫,操作方式如下
- 編碼選擇“936 中文(GBK)”
- 點擊“常用”下拉框,選擇24×24
- 字庫格式選擇“DZK”
- 掃描模式按照默認的就行。
- 單擊“創建”按鈕生成字庫,生成的字庫可以手動修改爲GBK24.FON
- 重複以上過程,在第二步時分別選擇12×12、16×16、32×32生成GBK12.FON、GBK16.FON、GBK32.FON,至此字庫文件就準備好了
2. 編寫更新字庫函數並將字庫文件寫入SPI FLASH
update_font函數用到的W25Q256驅動函數,可參考博客:https://blog.csdn.net/Ningjianwen/article/details/96477565
update_font函數用到的SD卡驅動函數以及UNIGBK.BIN文件的生成,可參考博客:https://blog.csdn.net/Ningjianwen/article/details/97237706
update_font函數如下所示,在調用該函數更新字庫時,需要將UNIGBK.BIN,GBK12.FON,GBK16.FON,GBK24.FON,GBK32.FON幾個文件放入SD卡的FONT目錄下。
#define FONTINFOADDR 1024*1024*25 //前面25M被fatfs佔用了;從25M地址以後開始存放字庫;
//字庫信息結構體定義
//用來保存字庫基本信息,地址,大小等
__packed typedef struct
{
uint8_t fontok; //字庫存在標誌,0XAA,字庫正常;其他,字庫不存在
uint32_t ugbkaddr; //unigbk的地址
uint32_t ugbksize; //unigbk的大小
uint32_t f12addr; //gbk12地址
uint32_t gbk12size; //gbk12的大小
uint32_t f16addr; //gbk16地址
uint32_t gbk16size; //gbk16的大小
uint32_t f24addr; //gbk24地址
uint32_t gbk24size; //gbk24的大小
uint32_t f32addr; //gbk32地址
uint32_t gbk32size; //gbk32的大小
}_font_info;
//字庫存放在磁盤中的路徑
char* const FONT_PATH[5] =
{
"FONT/UNIGBK.BIN", //UNIGBK.BIN的存放位置
"FONT/GBK12.FON", //GBK12的存放位置
"FONT/GBK16.FON", //GBK16的存放位置
"FONT/GBK24.FON", //GBK24的存放位置
"FONT/GBK32.FON", //GBK32的存放位置
};
_font_info ftinfo;
/***************************************************************************************
* @brief 更新字體文件,UNIGBK,GBK12,GBK16,GBK24,GBK32一起更新
* @input src:字庫來源磁盤."0:",SD卡;"1:",FLASH盤,"2:",U盤.
* @return
***************************************************************************************/
void update_font(void)
{
uint8_t rval;
UINT br;
/*#### 1.finnd UNIGBK,GBK12,GBK16,GBK24,GBK32,calculate the total size.######## */
fontSectorSize = 0;
for(uint8_t i = 0; i < 5; i++) //
{
strcpy(full_font_path, SDPath); //copy src內容到pname
strcat(full_font_path, FONT_PATH[i]); //追加具體文件路徑
retSD = f_open(&SDFile, (const TCHAR*)full_font_path, FA_READ); //嘗試打開
if(retSD){
rval |= 1 << 7; //標記打開文件失敗
break; //出錯了,直接退出
}
fontSectorSize += SDFile.obj.objsize;
f_close(&SDFile);
}
if(rval == 0) //字庫文件都存在.
{
printf("開始更新字庫!!");
fontSectorSize = fontSectorSize / 4096 + (fontSectorSize%4096 ? 1 : 0);
/*#### 2.Erase sector #################################################### */
for(uint32_t i = 0; i < fontSectorSize; i++) //先擦除字庫區域,提高寫入速度
{
W25QXX_EraseSector((FONTINFOADDR / 4096) + i); //需要擦除的扇區
}
for(uint8_t i = 0; i < 5; i++) //依次更新UNIGBK,GBK12,GBK16,GBK24,GBK32
{
offx = 0;
strcpy(full_font_path, SDPath); //copy src內容到pname
strcat(full_font_path, FONT_PATH[i]); //追加具體文件路徑
if(f_open(&SDFile, (const TCHAR*)full_font_path, FA_READ) == FR_OK) {
switch(i)
{
case 0: //更新UNIGBK.BIN
ftinfo.ugbkaddr = FONTINFOADDR + sizeof(ftinfo);//信息頭之後,緊跟UNIGBK轉換碼錶
ftinfo.ugbksize = SDFile.obj.objsize; //UNIGBK大小
flashaddr = ftinfo.ugbkaddr;
break;
case 1:
ftinfo.f12addr = ftinfo.ugbkaddr + ftinfo.ugbksize; //UNIGBK之後,緊跟GBK12字庫
ftinfo.gbk12size = SDFile.obj.objsize; //GBK12字庫大小
flashaddr = ftinfo.f12addr; //GBK12的起始地址
break;
case 2:
ftinfo.f16addr = ftinfo.f12addr + ftinfo.gbk12size; //GBK12之後,緊跟GBK16字庫
ftinfo.gbk16size = SDFile.obj.objsize; //GBK16字庫大小
flashaddr = ftinfo.f16addr; //GBK16的起始地址
break;
case 3:
ftinfo.f24addr = ftinfo.f16addr + ftinfo.gbk16size; //GBK16之後,緊跟GBK24字庫
ftinfo.gbk24size = SDFile.obj.objsize; //GBK24字庫大小
flashaddr = ftinfo.f24addr; //GBK24的起始地址
break;
case 4:
ftinfo.f32addr = ftinfo.f24addr + ftinfo.gbk24size; //GBK24之後,緊跟GBK32字庫
ftinfo.gbk32size = SDFile.obj.objsize; //GBK32字庫大小
flashaddr = ftinfo.f32addr; //GBK32的起始地址
break;
}
/*#### 3.copy font frome SD card to SPI flash. ##################### */
while(retSD == FR_OK)//死循環執行
{
retSD = f_read(&SDFile, font_buf, 4096, (UINT *)&br);//讀取數據
if(retSD != FR_OK) break;
W25QXX_Write_NoCheck(font_buf, offx + flashaddr, 4096);//將讀出來的字庫數據寫入SPI Flash
offx += br;
if(br != 4096) break;
}
}
f_close(&SDFile);
}
//全部更新好了
ftinfo.fontok = 0xAA;
W25QXX_Write((uint8_t*)&ftinfo, FONTINFOADDR, sizeof(ftinfo)); //保存字庫信息
GUI_SetFont(&GUI_FontHZ32); // 設置字體
printf("成功更新字庫!!\n");
}
}
3. 使用STemWin定義字庫信息
下面代碼使用STemWin中的變量定義了12×12、16×16、24×24、32×32字庫信息,可以只定義自己使用的字庫。
/*-----12×12字體----------------------------------------------------------*/
GUI_CONST_STORAGE GUI_CHARINFO GUI_FontHZ12_CharInfo[2] =
{
{ 6, 6, 1, (void*)"0"}, //
{ 12, 12, 2, (void*)"0"}, //表示此點陣字符的X軸長度是12個像素點,實際顯示也是12個像素點,顯示一行需要2字節,使用字符“0”作爲標識
};
GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ12_PropHZ = {
0x4081,
0xFFFF,
&GUI_FontHZ12_CharInfo[1],
(void *)0,
};
GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ12_PropASC = {
0x0000,
0x007F,
&GUI_FontHZ12_CharInfo[0],
(void GUI_CONST_STORAGE *)&GUI_FontHZ12_PropHZ,
};
GUI_CONST_STORAGE GUI_FONT GUI_FontHZ12 =
{
GUI_FONTTYPE_PROP_USER,
12,
12,
1,
1,
(void GUI_CONST_STORAGE *)&GUI_FontHZ12_PropASC
};
/*-----16×16字體----------------------------------------------------------*/
GUI_CONST_STORAGE GUI_CHARINFO GUI_FontHZ16_CharInfo[2] =
{
{ 8, 8, 1, (void*)"0"},
{ 16, 16, 2, (void*)"0"},
};
GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ16_PropHZ = {
0x4081,
0xFFFF,
&GUI_FontHZ16_CharInfo[1],
(void *)0,
};
GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ16_PropASC = {
0x0000,
0x007F,
&GUI_FontHZ16_CharInfo[0],
(void GUI_CONST_STORAGE *)&GUI_FontHZ16_PropHZ,
};
GUI_CONST_STORAGE GUI_FONT GUI_FontHZ16 =
{
GUI_FONTTYPE_PROP_USER,
16,
16,
1,
1,
(void GUI_CONST_STORAGE *)&GUI_FontHZ16_PropASC
};
/*-----24×24字體----------------------------------------------------------*/
GUI_CONST_STORAGE GUI_CHARINFO GUI_FontHZ24_CharInfo[2] =
{
{ 12, 12, 2, (void*)"0"},
{ 24, 24, 3, (void*)"0"},
};
GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ24_PropHZ = {
0x4081,
0xFFFF,
&GUI_FontHZ24_CharInfo[1],
(void *)0,
};
GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ24_PropASC = {
0x0000,
0x007F,
&GUI_FontHZ24_CharInfo[0],
(void GUI_CONST_STORAGE *)&GUI_FontHZ24_PropHZ,
};
GUI_CONST_STORAGE GUI_FONT GUI_FontHZ24 =
{
GUI_FONTTYPE_PROP_USER,
24,
24,
1,
1,
(void GUI_CONST_STORAGE *)&GUI_FontHZ24_PropASC
};
GUI_CONST_STORAGE GUI_FONT GUI_FontHZ24x2 =
{
GUI_FONTTYPE_PROP_USER,
24,
24,
2,
2,
(void GUI_CONST_STORAGE *)&GUI_FontHZ24_PropASC
};
/*-----32×32字體----------------------------------------------------------*/
GUI_CONST_STORAGE GUI_CHARINFO GUI_FontHZ32_CharInfo[2] =
{
{ 16, 16, 2, (void*)"0"},
{ 32, 32, 4, (void*)"0"},
};
GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ32_PropHZ = {
0x4081,
0xFFFF,
&GUI_FontHZ32_CharInfo[1],
(void *)0,
};
GUI_CONST_STORAGE GUI_FONT_PROP GUI_FontHZ32_PropASC = {
0x0000,
0x007F,
&GUI_FontHZ32_CharInfo[0],
(void GUI_CONST_STORAGE *)&GUI_FontHZ32_PropHZ,
};
GUI_CONST_STORAGE GUI_FONT GUI_FontHZ32 =
{
GUI_FONTTYPE_PROP_USER,
32,
32,
1,
1,
(void GUI_CONST_STORAGE *)&GUI_FontHZ32_PropASC
};
4. 編寫GUIPROP_X_DispChar與GUIPROP_X_GetCharDistX函數
/*********************************************************************
*
* GUIPROP_DispChar
*
* Purpose:
* This is the routine that displays a character. It is used by all
* other routines which display characters as a subroutine.
*/
void GUIPROP_X_DispChar(U16P c)
{
int BytesPerLine;
GUI_DRAWMODE DrawMode = GUI_pContext->TextMode;
const GUI_FONT_PROP GUI_UNI_PTR *pProp = GUI_pContext->pAFont->p.pProp;
//搜索定位字庫數據信息
for (; pProp; pProp = pProp->pNext)
{
if ((c >= pProp->First) && (c <= pProp->Last)) break;
}
if (pProp)
{
GUI_DRAWMODE OldDrawMode;
const GUI_CHARINFO GUI_UNI_PTR * pCharInfo = pProp->paCharInfo;
GUI_GetDataFromMemory(pProp, c);//取出字模數據
BytesPerLine = pCharInfo->BytesPerLine;
OldDrawMode = LCD_SetDrawMode(DrawMode);
LCD_DrawBitmap(GUI_pContext->DispPosX, GUI_pContext->DispPosY,
pCharInfo->XSize, GUI_pContext->pAFont->YSize,
GUI_pContext->pAFont->XMag, GUI_pContext->pAFont->YMag,
1, /* Bits per Pixel */
BytesPerLine,
&GUI_FontDataBuf[0],
&LCD_BKCOLORINDEX
);
/* Fill empty pixel lines */
if (GUI_pContext->pAFont->YDist > GUI_pContext->pAFont->YSize)
{
int YMag = GUI_pContext->pAFont->YMag;
int YDist = GUI_pContext->pAFont->YDist * YMag;
int YSize = GUI_pContext->pAFont->YSize * YMag;
if (DrawMode != LCD_DRAWMODE_TRANS)
{
LCD_COLOR OldColor = GUI_GetColor();
GUI_SetColor(GUI_GetBkColor());
LCD_FillRect(GUI_pContext->DispPosX, GUI_pContext->DispPosY + YSize,
GUI_pContext->DispPosX + pCharInfo->XSize,
GUI_pContext->DispPosY + YDist);
GUI_SetColor(OldColor);
}
}
LCD_SetDrawMode(OldDrawMode); /* Restore draw mode */
GUI_pContext->DispPosX += pCharInfo->XDist * GUI_pContext->pAFont->XMag;
}
}
/*********************************************************************
*
* GUIPROP_GetCharDistX
*/
int GUIPROP_X_GetCharDistX(U16P c)
{
const GUI_FONT_PROP GUI_UNI_PTR * pProp = GUI_pContext->pAFont->p.pProp;
for (; pProp; pProp = pProp->pNext)
{
if ((c >= pProp->First) && (c <= pProp->Last))break;
}
return (pProp) ? (pProp->paCharInfo)->XSize * GUI_pContext->pAFont->XMag : 0;
}
/***************************************************************************************
* @brief 獲取字模數據
* @input
* @return
***************************************************************************************/
static void GUI_GetDataFromMemory(const GUI_FONT_PROP GUI_UNI_PTR *pProp, U16P c)
{
unsigned char qh,ql;
unsigned char i;
unsigned long foffset;
unsigned char t;
unsigned char *mat;
U8 size,csize;//字體大小
U16 BytesPerFont;
GUI_FONT EMWINFONT;
EMWINFONT = *GUI_pContext->pAFont;
BytesPerFont = GUI_pContext->pAFont->YSize * pProp->paCharInfo->BytesPerLine; //每個字模的數據字節數
if (BytesPerFont > BYTES_PER_FONT) BytesPerFont = BYTES_PER_FONT;
//判斷字體的大小
if(memcmp(&EMWINFONT,&GUI_FontHZ12,sizeof(GUI_FONT)) == 0) size=12; //12字體
else if(memcmp(&EMWINFONT,&GUI_FontHZ16,sizeof(GUI_FONT)) == 0) size=16; //16字體
else if(memcmp(&EMWINFONT,&GUI_FontHZ24,sizeof(GUI_FONT)) == 0) size=24; //24字體
else if(memcmp(&EMWINFONT,&GUI_FontHZ32,sizeof(GUI_FONT)) == 0) size=32; //32字體
csize = (size/8+((size%8)?1:0))*(size); //得到字體一個字符對應點陣集所佔的字節數
memset(GUI_FontDataBuf,0,csize); //先清零
if (c < 0x80) //英文字符地址偏移算法
{
switch(size)
{
case 12:
for(t=0;t<12;t++) GUI_FontDataBuf[t]=emwin_asc2_1206[c-0x20][t];
break;
case 16:
for(t=0; t<16; t++) GUI_FontDataBuf[t] = emwin_asc2_1608[c-0x20][t];
break;
case 24:
for(t=0;t<48;t++) GUI_FontDataBuf[t]=emwin_asc2_2412[c-0x20][t];
break;
case 32:
for(t=0;t<64;t++) GUI_FontDataBuf[t]=emwin_asc2_3216[c-0x20][t];
break;
}
if(c=='\r'||c=='\n') memset(GUI_FontDataBuf,0,csize);
}else{
ql=c/256;
qh=c%256;
if(qh<0x81||ql<0x40||ql==0xff||qh==0xff)//非常用漢字
{
for(i=0;i<(size*2);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(GUI_FontDataBuf,foffset+ftinfo.f12addr,csize);
break;
case 16:
W25QXX_Read(GUI_FontDataBuf,foffset + ftinfo.f16addr,csize);
break;
case 24:
W25QXX_Read(GUI_FontDataBuf,foffset+ftinfo.f24addr,csize);
break;
case 32:
W25QXX_Read(GUI_FontDataBuf,foffset+ftinfo.f32addr,csize);
break;
}
}
}
5. 新建GUI_UC_EncodeNone.c文件重寫_GetCharCode、_GetCharSize、_CalcSizeOfChar等函數
這一步是必須的,漏了這一步也能編譯成功,但是不能正常顯示。
/*********************************************************************
*
* _GetCharCode
*
* Purpose:
* Return the UNICODE character code of the current character.
*/
static U16 _GetCharCode(const char GUI_UNI_PTR * s)
{
if((*s) >= 0X81)
{
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) >= 0X81)
{
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 > 0X4081)
{
return 2;
}
return 1;
}
/*********************************************************************
*
* _Encode
*
* Purpose:
* Encode character into 1/2/3 bytes.
*/
static int _Encode(char *s, U16 Char)
{
if(Char >0X4081)
{
*((U16*)s) = (U16)(Char);
return 2;
}
*s = (U8)(Char);
return 1;
}
/*********************************************************************
*
* Static data
*
**********************************************************************
*/
/*********************************************************************
*
* _API_Table
*/
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 */
};
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
};
6. 使用字體
- W25QXX_Read((uint8_t*)&ftinfo, FONTINFOADDR, sizeof(ftinfo));//讀出ftinfo結構體數據
- GUI_SetFont(&GUI_FontHZ16); // 設置字體(字體位於外部SPI FLASH)
- GUI_DispStringAt("設置字體爲16!!",100,160); //測試是否正常顯示漢字