【第3版emWin教程】第32章 emWin6.x的矢量字体(支持汉字全字库,Unicode编码,QSPI Flash方案)

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

第32章       emWin6.x的矢量字体(支持汉字全字库,Unicode编码,QSPI Flash方案)

本期教程跟大家讲解矢量字体的相关知识,矢量字体最大的好处就是可以任意放大或者缩小字体,而且字体的显示效果不失真。矢量字体也有缺点,即非常消耗内存。但是本教程配套开发板的STM32H7是支持外接SDRAM和支持内存映射方式的QSPI Flash,这样就有大容量的空间供矢量字体使用了。

32.1 初学者重要提示

32.2 下载算法存放位置(操作前必看)

32.3 矢量字体介绍

32.4 emWin对矢量字体的支持

32.5 矢量字体库的移植方法

32.6 矢量字体库的使用方法

32.7 内部Flash和QSPI Flash程序调试下载配置(重要必看)

32.8 实验例程说明(RTOS)

32.9 实验例程说明(裸机)

32.10 总结

 

 

32.1 初学者重要提示

1、  使用STM32H7+大容量的SDRAM或者内存映射方式QSPI Flash来实现矢量字体具有一定的实战意义,可用于实际项目。

2、  实验中发现了以下三个问题,给大家分享下:

  • 不是所有电脑端的矢量字体都可以显示,测试发现有些无法正常显示,估计是emWin库不支持。
  • 不能显示太大的字体,测试发现130点阵之后就无法显示了。
  • 显示比较大的字体,STM32H7的图形性能完全跟的上。

3、  矢量字体也是用的Unicode编码,这点要特别注意。

4、  矢量字体所有API函数在emWin手册中都有讲解,下图是中文版手册里面API函数的位置

 

下图是英文版手册里面API函数的位置:

 

32.2 下载算法存放位置(操作前必看)

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

编译例子:V7-060_QSPI Flash的MDK下载算法制作,生成的算法文件位于此路径下:

 

生成算法文件后,需要大家将其存到到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:

  •   第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。
  •   第2种:MDK的安装目录 \ARM\Flash里面。

 

32.3 矢量字体介绍

下面的内容来中文版wiki百科,讲的非常好,特此转载过来:https://zh.wikipedia.org/wiki/%E7%9F%A2%E9%87%8F%E5%AD%97%E4%BD%93

目前主流的矢量字体格式有3种:Type1,TrueType和OpenType,这三种格式都是与平台无关的。

Type1全称PostScript Type1,是1985年由Adobe公司提出的一套矢量字体标准,由于这个标准是基于PostScript Description Language(PDL),而PDL又是高端打印机首选的打印描述语言,所以Type1迅速流行起来。但是Type1是非开放字体,Adobe对使用Type1的公司征收高额的使用费。

TrueType是1991年由Apple公司与Microsoft公司联合提出另一套矢量字标准。

Type1使用三次贝塞尔曲线来描述字形,TrueType则使用二次贝塞尔曲线来描述字形。所以Type1的字体比TrueType字体更加精确美观。一个误解是,Type1字体比TrueType字体占用空间多。这是因为同样描述一个圆形,二次贝塞尔曲线只需要8个关键点和7段二次曲线;而三次贝塞尔曲线则需要12个关键点和11段三次曲线。然而实际情况是一般来说 Type1比TrueType要小10%左右。这是因为对于稍微复杂的字形,为了保持平滑,TrueType必须使用更多的关键点。由于现代大部分打印机都是使用PDL作为打印描述语言,所以Type1字体打印的时候不会产生形变,速度快;而TrueType则需要翻译成PDL,由于曲线方程的变化,还会产生一定的形变,不如Type1美观。

这么说来,Type1应该比TrueType更具有优势,为什么如今的计算机上TrueType反而比Type1使用更广泛呢?这是因为第一:Type1由于字体方程的复杂,所以在屏幕上渲染的时候,花费的时间多,解决方案是大部分Type1字体嵌入了点阵字体,这样渲染快,但是边缘不光滑,比较难看。很多ps文档和ps转换的pdf文档都是这样,在计算机上浏览的时候字体很难看,但是打印出来很美观。TrueType则渲染比较快,可以平滑的显示在屏幕上,看上去很美观。

第二个原因是Type1的高额使用费,使得Type1没有被所有的操作系统所支持。Windows家族只有OS/2和windows 2000及之后的版本从操作系统级别开始支持Type1。由于这个问题,Adobe只好在其所有的产品中嵌入Adobe Type Manager(ATM)作为渲染引擎。

OpenType则是Type1与TrueType之争的最终产物。1995年,Adobe公司和Microsoft公司开始联手开发一种兼容Type1和TrueType,并且真正支持Unicode的字体,后来在发布的时候,正式命名为OpenType。OpenType可以嵌入Type1和TrueType,这样就兼有了二者的特点,无论是在屏幕上察看还是打印,质量都非常优秀。可以说OpenType是一个三赢的结局,无论是Adobe、Microsoft还是最终用户,都从OpenType中得到了好处。Windows家族从Windows 2000开始,正式支持OpenType。打开系统的字体目录(一般是C:\Windows\Fonts\或C:\Winnt\Fonts),可以看到:一个红色A的图标的是点阵字体,两个重叠的T的图标是TrueType字体,一个O的图标就是OpenType字体。

下面是XP系统中字体的部分截图,其中矢量字体扩展名ttf,点阵字体的扩展名是fon。

 

Win7系统中已经变成如下这种样子:

 

32.4 emWin对矢量字体的支持

emWin对矢量字体库的支持是基于David Turner、Robert Wilhelm和Werner Lembergr的FreeType字体库,该库可在www.freetype.org下免费获得。emWin对该库的使用符GUI\TrueType\FTL. txt下的FreeType授权许可。emWin对该库进行了少许改编,添加了带有GUI函数的应用层。emWin软件包中也是没有矢量字体库的,需要大家在SEGEER官网地址https://www.segger.com/downloads/emwin/emWin_FreeType 下载。

矢量字体基于矢量图形,矢量的优势在于可以无损的放缩。而点阵字体虽然也可以放缩,但不是矢量的,放缩后锯齿很明显。并且项目中需要多种字体大小支持的话,需要几种字体支持,就需要生成几种点阵字库,非常占空间,而矢量字体仅需要一个字体库就可以了。特别是显示大字体,矢量字体库的优势更明显。

通过矢量字体带来无损放缩的同时,也是有缺点的。使用矢量字体的话,每个字符在绘制前需要光栅化为位图,为避免每次绘制字符时都进行光栅化,通常用字体引擎缓存点阵数据。这要求CPU速度快、RAM足够。当前emWin对矢量字体的支持是以总线方式寻址的,与第30章讲解的SIF格式字体是类似的。

TrueType矢量字体的硬件要求如下:

32.5 矢量字体库的移植方法

跟第23章讲解的PNG库一样,emWin的库中也是不含有矢量库的,需要用户自行添加,添加也比较简单,只需用户把源码文件添加到工程里面就可以使用了。

矢量库的下载地址:https://www.segger.com/downloads/emwin/emWin_FreeType 。下载软件包后进行解压,当前这个版本的库已经被存到本章节配套例子的Doc文件夹:

 

32.5.1   MDK版本移植说明

  • 第1步:在 emWin工程-->emWin文件夹-->新建一个TrueType文件夹,将矢量字体库里面的源码文件全部复制到此文件夹里面(其它任意文件夹都是可以的,不限制)。

 

  • 第2步:将矢量库的所有.C格式的源码文件添加到MDK工程里面,下面是部分源码文件的截图。

 

  • 第3步:添加矢量库的头文件路径,添加完毕后别忘了点击OK。

 

  • 第4步:修改系统堆(heap)大小,这一步非常关键。因为矢量库要用到函数malloc和free,而这种函数是从系统堆空间里面申请内存的,鉴于矢量库非常的消耗动态内存,这里将32MB SDRAM的最后1MB空间给系统堆使用,设置如下:

 

Heap_Size:表示堆大小设置为1MB。

_heap_base:表示堆起始地址为0xC1F00000,即32MB SDRAM最后1MB空间的起始地址。

_heap_linmit:表示堆结束地址0xC1FFFFFF,即32MB SDRAM最后1MB空间的结束地址。

除了malloc和free要用到堆空间,部分C标准库的其它函数也要用到堆空间,所以一定要及时初始化SDRAM,防止用到堆空间的时候,SDRAM还没有初始化,将导致系统崩溃。当前是将SDRAM的初始化放在了bsp.c文件的bsp_Init函数开始的地方,之前执行的程序都没有用到C标准库,所以可以放在这里。

  • 第5步:最后一步,添加好库文件并且修改完毕后,验证是否已经添加成功,可以进行一次全编译,全编译后MDK会有几个警告和两个错误。

 

解决办法是将下面两个函数形参的void删掉即可

 

至此,矢量字体库就添加成功了。剩下就可以调用矢量库的API函数了。

32.6 矢量字体库的使用方法

矢量字体的使用通过下面四步就可以实现:

第1步:定义16点阵大小,24点阵大小,32点阵大小,48点阵大小,72点阵大小和120点阵大小的格式字体。

/*
*********************************************************************************************************
*                                      定义矢量字体
*********************************************************************************************************
*/
GUI_TTF_CS Cs0, Cs1, Cs2, Cs3, Cs4, Cs5;
GUI_TTF_DATA Data;
GUI_FONT Font16, Font24, Font32, Font48, Font72, Font120;

这里对定义矢量字体用到的两个结构体变量做如下介绍。

GUI_TTF_CS结构体变量:

GUI_TTF_DATA结构体变量:


第2步:创建16点阵大小,24点阵大小,32点阵大小,48点阵大小,72点阵大小和120点阵大小的格式字体。

创建前要先将矢量字体库存到SD卡中,然后将其加载到SDRAM里面,这个矢量字体是来自电脑系统自带,电脑系统是WIN7 64bit,路径:C:\Windows\Fonts(已经将这个字体存到本章节配套例子的Doc文件夹下)。


 

大小是10MB,其它类型的矢量字体也是可以的,只要不超过QSPI Flash的32MB容量即可:

/*
*********************************************************************************************************
*    函 数 名: LoadFontTTF
*    功能说明: 初始化
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void LoadFontTTF() 
{
#if 1
    char *_acBuffer;
    GUI_HMEM hMem;
    
    /* 申请一块内存空间 并且将其清零 */
    hMem = GUI_ALLOC_AllocZero(sizeof(_acsong));
    
    /* 将申请到内存的句柄转换成指针类型 */
    _acBuffer = GUI_ALLOC_h2p(hMem);

    memcpy(_acBuffer, _acsong, sizeof(_acsong));
    
    /* 设置参数 */
    Data.pData = _acBuffer; 
    Data.NumBytes = sizeof(_acsong); 
#else
    /* 设置参数 */
    Data.pData = _acsong; 
    Data.NumBytes = sizeof(_acsong); 
#endif

    /* 设置第1种字体显示方式 */
    Cs0.pTTF = &Data;       /* 矢量字体数据地址 */
    Cs0.PixelHeight = 16;   /* 字体高度 */
    Cs0.FaceIndex = 0;
    
    /* 设置第2种字体显示方式 */
    Cs1.pTTF = &Data;       /* 矢量字体数据地址 */
    Cs1.PixelHeight = 24;   /* 字体高度 */
    Cs1.FaceIndex = 0;
    
    
    /* 设置第3种字体显示方式 */
    Cs2.pTTF = &Data;       /* 矢量字体数据地址 */
    Cs2.PixelHeight = 32;   /* 字体高度 */
    Cs2.FaceIndex = 0;
    
    /* 设置第4种字体显示方式 */
    Cs3.pTTF = &Data;      /* 矢量字体数据地址 */
    Cs3.PixelHeight = 48;  /* 字体高度 */
    Cs3.FaceIndex = 0;
    
    
    /* 设置第5种字体显示方式 */
    Cs4.pTTF = &Data;      /* 矢量字体数据地址 */
    Cs4.PixelHeight = 72;  /* 字体高度 */
    Cs4.FaceIndex = 0; 
    
    /* 设置第6种字体显示方式 */
    Cs5.pTTF = &Data;        /* 矢量字体数据地址 */
    Cs5.PixelHeight = 120; /* 字体高度 */
    Cs5.FaceIndex = 0;
    

    /* 创建6种字体 */
    GUI_TTF_CreateFontAA(&Font16, &Cs0);
    GUI_TTF_CreateFontAA(&Font24, &Cs1);
    GUI_TTF_CreateFontAA(&Font32, &Cs2);
    GUI_TTF_CreateFontAA(&Font48, &Cs3);
    GUI_TTF_CreateFontAA(&Font72, &Cs4);
    GUI_TTF_CreateFontAA(&Font120, &Cs5);
    
    f_close(&file);
}

第3步:加载到SDRAM后,使用就比较简单了。

用户只需调用函数GUI_UC_SetEncodeUTF8()使能UTF-8编码就可以使用矢量字体了,比如设置按钮的字体,调用如下设置函数即可。

BUTTON_SetFont(hWin,  &Font32);  /* hWin是按钮的句柄 */

第4步:最后一步切不可忘记设置汉字显示所在源文件的编码类型,具体MDK和IAR的设置方法请看第28章22.4小节(本章节配套的例子也是设置的MainTask,c文件),这一步绝对不可以省略,因为我们使用的矢量字体库也是Unicode编码。

通过这4步就实现矢量字体的显示了。另外注意,如果系统运行中不需要矢量字体了,可以通过函数GUI_TTF_DestroyCache 释放矢量字体所消耗的内存资源,通过函数GUI_ALLOC_AllocZero申请的空间,可以使用函数GUI_ALLOC_Free来释放。

32.7 内部Flash和QSPI Flash程序调试下载配置(重要必看)

将下面两个地方配置后,就可以像使用内部Flash一样使用QSPI Flash进行调试了。并且这种方式可以方便的调试程序,内部Flash和外部Flash都做调试。

32.7.1        将字库文件转换为C数组格式文件

为了方便将bin文件添加到MDK工程中,我们这里使用小软件B2C.exe将其转换为C格式文件(此软件已经放到本章配套例子V7-540_emWin6.x实验_矢量全字库,支持中文,Unicode编码(QSPI Flash RTOS)的Doc文件里面。

 

转换后生成的文件命名为song.c :

const unsigned char _acsong[10576012UL + 1] = {
  0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x04, 0x00, 0x20, 0x44, 0x53, 0x49, 0x47, 0x28, 0x0C, 0xE3, 0x96, 0x00, 0xA1, 0x45, 0x40, 0x00, 0x00, 0x1B, 0x4C, 0x47, 0x53, 0x55, 0x42, 0xBB, 0xCF, 0xB8, 0xF7, 0x00, 0xA0, 0x64, 0xF4,
  0x00, 0x00, 0x00, 0xFC, 0x4F, 0x53, 0x2F, 0x32, 0xD3, 0x94, 0x1D, 0x16, 0x00, 0x00, 0x01, 0xA8, 0x00, 0x00, 0x00, 0x60, 0x63, 0x6D, 0x61, 0x70, 0x3D, 0xE8, 0x75, 0xB8, 0x00, 0x00, 0xE7, 0xDC, 0x00, 0x00, 0x05, 0x5C, 0x63, 0x76, 0x74, 0x20,
  0x07, 0x29, 0x03, 0xF0, 
  省略未写

}

32.7.2        设置字库文件到外部QSPI Flash。

下面将流位图文件下载到QSPI Flash,需要大家先在这里添加QSPI Flash地址范围:

 

然后设置资源文件到外部QSPI Flash:鼠标右击文件分组GUI/Font,选择Options。

 

32.7.3 下载配置

注意这里一定要够大,否则会提示算法文件无法加载:

 

我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。

如果要下载程序到内部Flash和外部QSPI Flash里面,需要做如下配置,两个下载算法都要添加进来:

 

32.7.4 调试配置

注意这里一定要够大,否则会提示算法文件无法加载:

 

我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。

如果要做调试下载,需要做如下配置:

 

32.8 实验例程说明(RTOS)

配套例子:

V7-540_emWin6.x实验_矢量全字库,支持中文,Unicode编码(QSPI Flash RTOS)

实验目的:

  1. 学习emWin矢量字体库的使用方法,Unicode编码
  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分辨率界面效果。

 

32.9 实验例程说明(裸机)

配套例子:

V7-539_emWin6.x实验_矢量全字库,支持中文,Unicode编码(QSPI Flash裸机)

实验目的:

  1. 学习emWin矢量字体库的使用方法,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动态内存。

32.10  总结

本章节为大家讲解的矢量字体是可以用于项目实战的,实际项目中建议使用大容量的SDRAM或者内存映射方式的QSPI Flash,这样即使加载矢量字库后,还有大量空间供emWin动态内存使用。

 

posted @ 2021-08-28 15:40  硬汉嵌入式  阅读(681)  评论(0编辑  收藏  举报