前言 上篇 中介绍了如何在ESP32上运行LVGL,现在终于可以开始奔着第一篇 文章末尾的效果去了.
准备工作 软件准备 访问https://download.savannah.gnu.org/releases/freetype/ 下载源码,解压后重命名为freetype并复制到项目lib文件夹下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方式创建字体了.
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