• 【第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软包安装目录里面:KeilSTM32H7xx_DFP2.6.0CMSISFlash(软包版本不同,数值2.6.0不同)。
    •   第2种:MDK的安装目录 ARMFlash里面。

     

    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:WindowsFonts或C:WinntFonts),可以看到:一个红色A的图标的是点阵字体,两个重叠的T的图标是TrueType字体,一个O的图标就是OpenType字体。

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

     

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

     

    32.4 emWin对矢量字体的支持

    emWin对矢量字体库的支持是基于David Turner、Robert Wilhelm和Werner Lembergr的FreeType字体库,该库可在www.freetype.org下免费获得。emWin对该库的使用符GUITrueTypeFTL. 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:WindowsFonts(已经将这个字体存到本章节配套例子的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动态内存使用。

    微信公众号:armfly_com 安富莱论坛:www.armbbs.cn 安富莱淘宝:https://armfly.taobao.com
  • 相关阅读:
    hdu4355(三分求极值)
    Codeforces Round #107 (Div. 1) (快速求质因子、vector)
    2012哈工程ACM暑期集训图论专场(练习赛第7场)题解
    2012 MultiUniversity Training Contest 6
    Codeforces Beta Round #97 (Div. 2)
    Codeforces Round #134 (Div. 2) 并查集
    C++ Boost foreach
    Design : 如何设计View
    15个最优秀的酒店网页免费模板
    在C++中使用tr1实现functor/函数指针/成员函数指针
  • 原文地址:https://www.cnblogs.com/armfly/p/15200210.html
Copyright © 2020-2023  润新知