目录
方式一 使用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); //测试是否正常显示汉字