前言 上篇 中介绍了如何在ESP32上运行LVGL,现在终于可以开始奔着第一篇 文章末尾的效果去了.
准备工作 软件准备 访问https://download.savannah.gnu.org/releases/freetype/ 下载源码,解压后重命名为freetype并复制到项目lib文件夹下 我下载的是目前(2021.12.11)最新版本freetype-2.11.1.tar.gz(较老的版本不支持可变字体操作) 然后去这个Repo 获取SD和SPI库,复制到项目lib文件夹下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 . ├── include │ └── README ├── lib │ └── README │ └── lvgl_freetype │ └── lvgl │ └── freetype │ └── SD │ └── SPI │ └── TFT_eSPI ├── platformio.ini ├── src │ └── main.cpp │ └── Port │ └── lv_port_disp.cpp │ └── lv_port_disp.h └── test └── README
到freetype文件夹下,创建FreeType的library.json 和library.properties 文件library.properties 内容如下
1 2 3 4 5 6 7 8 9 10 11 name=FreeType version=2.11.1 author=David Turner, Robert Wilhelm, Werner Lemberg maintainer=Werner Lemberg sentence=A freely available software library to render fonts. paragraph=It is written in C, designed to be small, efficient, highly customizable, and portable while capable of producing high-quality output (glyph images) of most vector and bitmap font formats.documentation. category=Font url=https://freetype.org/ architectures=* repository=https://gitlab.freedesktop.org/freetype/freetype license=GNU
library.json 内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 { "name" : "freetype" , "version" : "2.11.1" , "description" : "Software library to render fonts" , "keywords" : "freetype" , "license" : "FreeType License" , "repository" : { "type" : "git" , "url" : "https://gitlab.freedesktop.org/freetype" } , "frameworks" : "*" , "platforms" : "*" , "build" : { "srcFilter" : [ "+<base/ftsystem.c>" , "+<base/ftmm.c>" , "+<base/ftinit.c>" , "+<base/ftdebug.c>" , "+<base/ftbase.c>" , "+<base/ftbbox.c>" , "+<base/ftglyph.c>" , "+<base/ftbdf.c>" , "+<bdf/bdf.c>" , "+<cff/cff.c>" , "+<truetype/truetype.c>" , "+<sfnt/sfnt.c>" , "+<smooth/smooth.c>" , "+<cache/ftcache.c>" , "+<gzip/ftgzip.c>" , "+<base/ftbitmap.c>" ] , "flags" : [ "-DFT2_BUILD_LIBRARY" , "-I include" ] , "includeDir" : "devel" } }
硬件准备 和上篇相比,多了一个SD卡模块.
起初也尝试了直接把字体文件存在SPIFFS里面,减少涉及到的硬件,奈何速度慢的感人,于是回到这里重新写了SD卡版本.
2022.02.08: 发现LVGL自8.1.1-dev(正式版为8.2.0)开始,内置的lv_freetype模块已经支持FT_New_Memory_Face方式创建字体了. 如果没有内存卡的话,请将字体文件命名为Lite.ttf放到源码src文件夹内, 在platformio.ini内添加
1 2 board_build.embed_txtfiles = src/Lite.ttf
并在main.cpp中添加
1 2 3 extern const uint8_t font_start[] asm ("_binary_src_Lite_ttf_start" );extern const uint8_t font_end[] asm ("_binary_src_Lite_ttf_end" );extern const size_t size = font_end - font_end - 1 ;
创建字体时候额外添加如下内容
1 2 3 4 5 static lv_ft_info_t info;info.mem = font_start; info.mem_size = size;
有SD卡的情况下
名称
数量
备注
图例
ESP32 开发板
1
\
1.54寸LCD
1
驱动ST7789,分辨率240x240
杜邦线若干
N
\
Micro SD卡模块和卡
1
\
接线和点亮屏幕请去参考LGVL配合FreeType为可变字体设置字重-ESP32篇(上)
Micro SD卡模块的接线如下
ESP32引脚名称
Micro SD卡模块引脚名称
GND
GND
G26
MISO
G13
MOSI
G14
SCK
G15
CS
5V(Vin)
VCC
准备完毕,开干 启用LVGL的FreeType 将lv_conf.h内的宏定义内容, 并启用FTC_SBitCache_Lookup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #define LV_USE_FREETYPE 0 #define LV_USE_FREETYPE 1 #if LV_USE_FREETYPE #define LV_FREETYPE_CACHE_SIZE (16 * 1024) #if LV_FREETYPE_CACHE_SIZE >= 0 #define LV_FREETYPE_SBIT_CACHE 1 #define LV_FREETYPE_CACHE_FT_FACES 0 #define LV_FREETYPE_CACHE_FT_SIZES 0 #endif #endif
0设置为1
配置FreeType的文件系统 创建一个命为ft_fs_port.cpp的文件,内容为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include "FS.h" #include "SD.h" #include "SPIFFS.h" extern "C" {typedef void lvbe_FILE;lvbe_FILE *lvbe_fopen (const char *filename, const char *mode) { File f = SD.open (filename, mode); if (f) { File *f_ptr = new File (f); *f_ptr = f; return f_ptr; } return nullptr ; } int lvbe_fclose (lvbe_FILE *stream) { File *f_ptr = (File *)stream; f_ptr->close (); delete f_ptr; return 0 ; } size_t lvbe_fread (void *ptr, size_t size, size_t count, lvbe_FILE *stream) { File *f_ptr = (File *)stream; int32_t ret = f_ptr->read ((uint8_t *)ptr, size * count); if (ret < 0 ) { ret = 0 ; } return ret; } int lvbe_fseek (lvbe_FILE *stream, long int offset, int origin) { File *f_ptr = (File *)stream; fs::SeekMode mode = fs::SeekMode::SeekSet; if (SEEK_CUR == origin) { mode = fs::SeekMode::SeekCur; } else if (SEEK_END == origin) { mode = fs::SeekMode::SeekEnd; } bool ok = f_ptr->seek (offset, mode); return ok ? 0 : -1 ; } int lvbe_ftell (lvbe_FILE *stream) { File *f_ptr = (File *)stream; return f_ptr->position (); } }
打开位于freetype文件夹下的devel\ft2build.h 并添加
1 2 3 4 5 6 7 8 9 10 #ifndef FT2BUILD_H_ #define FT2BUILD_H_ #define FT_CONFIG_MODULES_H <ftmodule.h> #define FT_CONFIG_OPTIONS_H <ftoption.h> #define FT_CONFIG_STANDARD_LIBRARY_H <ftstdlib.h> #include <freetype/config/ftheader.h> #endif
在同级目录下创建ftstdlib.h (文末Repo内有完整版)文件,内容从freetype\include\freetype\config\ftstdlib.h
复制
修改file handling部分的内容为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <stdio.h> typedef void lvbe_FILE;extern lvbe_FILE * lvbe_fopen (const char * filename, const char * mode ) ;extern int lvbe_fclose (lvbe_FILE * stream) ;extern size_t lvbe_fread (void * ptr, size_t size, size_t count, lvbe_FILE * stream) ;extern int lvbe_fseek (lvbe_FILE * stream, long int offset, int origin ) ;extern int lvbe_ftell (lvbe_FILE * stream) ;#define FT_FILE lvbe_FILE #define ft_fclose lvbe_fclose #define ft_fopen lvbe_fopen #define ft_fread lvbe_fread #define ft_fseek lvbe_fseek #define ft_ftell lvbe_ftell #define ft_sprintf sprintf
在同级目录下创建ftmodule.h ,启用所需模块 内容为
1 2 3 FT_USE_MODULE ( FT_Driver_ClassRec, tt_driver_class )FT_USE_MODULE ( FT_Module_Class, sfnt_module_class )FT_USE_MODULE ( FT_Renderer_Class, ft_smooth_renderer_class )
在同级目录下创建 ftoption.h**, 内容从freetype\include\freetype\config\ftoptions.h
复制,详细的配置过长,故此处省略,见文末Repo内**
此时FreeType的基本功能配置完成
主要的涉及到freetype\devel 下的4个文件
1 2 3 4 5 6 │ └── freetype │ └── devel │ └── ft2build.h │ └── ftmodule.h │ └── ftoption.h │ └── ftstdlib.h
测试FreeType移植结果 打开main.cpp 随便写点内容,烧录到ESP32上运行查看效果.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include <Arduino.h> #include "./Port/lv_port_disp.h" #include "SD.h" #include "SPI.h" bool SD_Init () { pinMode (22 , INPUT); SPIClass *sd_spi = new SPIClass (HSPI); if (!SD.begin (15 , *sd_spi, 12000000 , "" )) { Serial.println ("Card Mount Failed" ); return false ; } uint8_t cardType = SD.cardType (); if (cardType == CARD_NONE) { Serial.println ("No SD card attached" ); return false ; } Serial.print ("SD Card Type: " ); if (cardType == CARD_MMC) { Serial.println ("MMC" ); } else if (cardType == CARD_SD) { Serial.println ("SDSC" ); } else if (cardType == CARD_SDHC) { Serial.println ("SDHC" ); } else { Serial.println ("UNKNOWN" ); } uint64_t cardSize = SD.cardSize () / (1024 * 1024 ); Serial.printf ("SD Card Size: %lluMB, used size:%llu\n" , cardSize, SD.usedBytes ()); return true ; } void setup () { Serial.begin (115200 ); Port_Init (); if (!SD_Init ()) { Serial.println ("SPIFFS Mount Failed" ); }; static lv_ft_info_t info; info.name = "/archivo.ttf" ; info.weight = 18 ; info.style = FT_FONT_STYLE_NORMAL; if (!lv_ft_font_init (&info)) { LV_LOG_ERROR ("create failed." ); } else { LV_LOG_ERROR ("create done." ); } static lv_style_t style; lv_style_init (&style); lv_style_set_text_font (&style, info.font); lv_style_set_text_color (&style, lv_color_black ()); lv_obj_t *altria = lv_label_create (lv_scr_act ()); lv_obj_t *shirou = lv_label_create (lv_scr_act ()); lv_label_set_text (altria, "Toou.\nAnata wa watashi no masuta ka?" ); lv_label_set_text (shirou, "Master?" ); lv_obj_set_style_text_font (shirou, info.font, 0 ); lv_obj_center (shirou); xTaskNotifyGive (handleTaskLvgl); } void loop () {}
运行效果如下
拓展LVGL的FreeType 就像前篇内容中的一样,需要手动地为当前版本LVGL添加可变字体支持.
lv_freetype.h 内的lv_ft_info_t 修改为
1 2 3 4 5 6 7 8 9 typedef struct { const char * name; const void * mem; size_t mem_size; lv_font_t * font; uint16_t height; uint16_t weight; uint16_t style; } lv_ft_info_t ;
然后去lv_freetype.c 里添加可变字体操作代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 FT_MM_Var *amaster = NULL ; FT_Error err = FT_Get_MM_Var(face, &amaster); if (err) { LV_LOG_ERROR("FT_Get_MM_Var error:%d\n" , err); return err; } FT_Fixed w = dsc->weight << 16 ; if (w > amaster->axis->maximum) { w = amaster->axis->maximum; } err = FT_Set_Var_Design_Coordinates(face, 1 , &w); if (err) { LV_LOG_ERROR("FT_Set_Var_Design_Coordinates error:%d\n" , err); return err; } FT_Done_MM_Var(library, amaster);
别忘记我们已经修改了weight的含义,此时字体大小已经由height决定了
回到main.cpp,在创建字体初时候,设置weight为100
1 2 3 4 5 static lv_ft_info_t info;info.name = "/archivo.ttf" ; info.height = 18 ; info.weight = 100 ; info.style = FT_FONT_STYLE_NORMAL;
然后烧录到ESP32,可以看到字重已经降低了.
动起来吧,字重 利用LVGL的lv_timer_t或者lv_anim_t动态的修改字重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 typedef struct _VF_Label { lv_obj_t *label; lv_ft_info_t *font; } VF_Label; static void onTimer (lv_timer_t *timer) { static uint16_t weight = 100 ; static uint16_t size = 18 ; VF_Label *vfl = (VF_Label *)(timer->user_data); if (vfl == nullptr ) { printf ("vfl == nullptr\n" ); } if (vfl == nullptr ) { printf ("vfl->font->font == nullptr\n" ); } lv_ft_font_destroy (vfl->font->font); printf ("lv_ft_font_destroy\n" ); vfl->font->name = "/archivo.ttf" ; vfl->font->height = size; vfl->font->weight = weight; vfl->font->style = FT_FONT_STYLE_NORMAL; lv_ft_font_init (vfl->font); printf ("lv_ft_font_init\n" ); size = 48 ; weight += 100 ; if (weight > 600 ) { weight = 100 ; } lv_obj_set_style_text_font (vfl->label, vfl->font->font, 0 ); }
1 2 3 lv_timer_t *weightTimer = lv_timer_create (onTimer, 50 , &label);xTaskNotifyGive (handleTaskLvgl);
效果图
环境: 1 2 3 4 5 6 7 Espressif 32 (3.4.0) > ESP32 Pico Kit framework-arduinoespressif32 3.10006.210326 (1.0.6) tool-esptoolpy 1.30100.210531 (3.1.0) toolchain-xtensa32 2.50200.97 (5.2.0) <lvgl> 8.1.1-dev <TFT_eSPI> 2.3.89 esptool.py v3.1
参考资料
Repo