ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LVGL动态加载中文字体:FreeType引擎集成与嵌入式GUI优化实践

LVGL动态加载中文字体:FreeType引擎集成与嵌入式GUI优化实践

1. 项目缘起:为什么要在LVGL中动态加载中文字体?

在嵌入式GUI开发中,尤其是使用LVGL这类轻量级图形库时,中文字体的支持一直是个老大难问题。很多开发者,包括我自己,都经历过这样的阶段:为了显示几个汉字,不得不把整个庞大的中文字库(比如16x16点阵字库)编译进固件,结果就是固件体积瞬间膨胀几百KB甚至几MB。这对于资源捉襟见肘的MCU来说,简直是不可承受之重。更别提那些需要显示多种字号、甚至需要字体加粗、斜体等效果的复杂界面了,传统的点阵字库方案几乎无能为力。

我最近在做一个基于STM32和LVGL的智能家居中控屏项目,UI设计稿里用到了阿里巴巴普惠体,要求能动态切换不同大小的标题和正文。如果还用老方法,光是字体文件就能把Flash占满。这时,lv_lib_freetype库就成了我的救命稻草。这个库的本质,是让LVGL能够利用成熟的FreeType字体渲染引擎,直接读取存储在外部存储器(如SPI Flash、SD卡)上的标准TTF或OTF字体文件,在运行时按需渲染字形。这意味着,你不再需要把字体“烧死”在程序里,一个几MB的TTF文件放在SD卡上,程序里只需要增加几十KB的FreeType库代码开销,就能实现任意字号、任意字符的动态渲染,灵活性是点阵方案无法比拟的。

网络上关于lv_lib_freetype的讨论很多,但信息比较零散。有的只讲了库的移植,没讲清楚中文字体怎么用;有的用了英文字体做演示,一到中文就乱码;还有的卡在字体缓存配置上,导致渲染效率低下。本文的目的,就是结合我实际在STM32F4平台上集成“阿里巴巴普惠体”的完整过程,把其中的核心原理、关键配置、踩坑细节和优化技巧一次性讲透。无论你是刚接触LVGL的新手,还是正在为字体问题头疼的老鸟,相信都能从中找到答案。

2. 核心组件解析:lv_lib_freetype与FreeType引擎

在动手之前,我们必须先理清几个核心概念和它们之间的关系。很多人一开始会混淆lvgllv_lib_freetypeFreeType,导致配置时张冠李戴。

2.1 FreeType:专业的字体渲染引擎

FreeType是一个用C语言编写的高质量、可移植的字体引擎。它不负责显示,只负责一件事:读取字体文件(TTF, OTF等),并根据你指定的字符编码(如Unicode)、字体大小、渲染模式等参数,计算出这个字符对应的“字形(Glyph)”轮廓的位图数据。你可以把它想象成一个高度专业化的“字形生成器”。它的输出是原始的点阵数据以及该字形在字体中的度量信息(如宽度、高度、基线位置等)。

2.2 lv_lib_freetype:连接LVGL与FreeType的桥梁

lv_lib_freetype是LVGL官方提供的一个“适配层”库。LVGL本身有一套定义好的字体接口(lv_font_t)。lv_lib_freetype库的作用,就是实现这套接口,但其内部的具体函数(如获取字形位图)是通过调用FreeType库的API来完成的。简单来说,它把FreeType引擎“包装”成了LVGL能够识别和使用的“字体驱动”。

这里有一个关键点:lv_lib_freetype库有两个版本,或者说两种模式,这直接影响了我们的使用方式:

  • 版本1(已废弃):早期版本,需要用户手动管理FreeType库的初始化和资源。现在基本不再使用。
  • 版本2(当前主流):LVGL 8.x 之后推荐使用的版本。它内部集成了一个简化版的FreeType(通常称为lv_freetype),或者允许你链接外部的完整FreeType库。我们通常使用其内部集成简化版的模式,这样最省事,因为它已经处理好了FreeType的初始化、内存管理和多实例问题。

2.3 工作流程梳理

当我们调用lv_label_set_text(label, “你好”)时,背后的流程是这样的:

  1. LVGL的标签对象需要渲染“你”和“好”两个字。
  2. 标签对象关联了一个使用lv_lib_freetype创建的字体对象。
  3. LVGL通过字体接口,向lv_lib_freetype请求“你”字的字形数据。
  4. lv_lib_freetype收到请求,将字符码(如Unicode值)、字体大小等信息传递给其内部的FreeType引擎。
  5. FreeType引擎打开对应的TTF文件,找到“你”字对应的字形轮廓,根据当前大小进行缩放、抗锯齿等渲染操作,生成位图数据和度量信息。
  6. lv_lib_freetype将这些数据按照LVGL要求的格式进行封装,返回给LVGL。
  7. LVGL拿到位图数据,将其绘制到屏幕上对应的位置。

整个过程是动态的、按需进行的。只有屏幕上实际用到的字符,才会触发FreeType的渲染操作。为了提升性能,lv_lib_freetype还设计了缓存机制,将渲染过的字形位图暂时保存起来,避免同一字符在短时间内重复渲染。

3. 实战准备:工程配置与字体资源获取

理论清晰后,我们进入实战环节。我以最常见的STM32CubeIDE+LVGL v8.3.x+lv_lib_freetype为例,目标是在外部SPI Flash(如W25Q64)上存储“阿里巴巴普惠体”,并在LVGL中动态加载使用。

3.1 获取并集成lv_lib_freetype库

首先,你需要确保你的工程中已经正确移植了LVGL核心库。然后,从LVGL的GitHub仓库获取lv_lib_freetype库。

注意:直接从Release页面下载的LVGL包可能不包含lv_lib_freetype,最好从GitHub仓库的/src/extra/libs/freetype目录获取最新版本。

lv_lib_freetype的源文件(.c.h)和lv_freetype(内部简化版FreeType)的源文件添加到你的工程中。在CubeIDE中,通常意味着:

  1. 在项目资源管理器中,右键点击项目 ->Import...->File System,选择本地lv_lib_freetype文件夹,导入srclv_conf(如果有)目录下的所有.c.h文件。
  2. 在项目属性中,确保这些新添加的源文件的包含路径(Include paths)已经正确设置。通常需要添加lv_lib_freetypelv_freetype的头文件所在目录。

3.2 修改lv_conf.h关键配置

lv_conf.h是LVGL的核心配置文件,我们需要启用并配置FreeType支持。

/* 1. 启用lv_lib_freetype库 */ #define LV_USE_FREETYPE 1 /* 2. 如果lv_lib_freetype版本 >= 2.2.0,建议启用此宏以使用更简单的API */ #define LV_FREETYPE_USE_LVGL_PORT 1 /* 3. 配置FreeType缓存大小,这对性能至关重要! * LV_FREETYPE_CACHE_FT_FACES: 缓存的字体文件实例数量。如果你只用一个字体文件,设为4足够。 * LV_FREETYPE_CACHE_FT_SIZES: 缓存的字体大小实例数量。如果你用了10、16、24三种字号,建议设为8(略多于实际值)。 * LV_FREETYPE_CACHE_FT_GLYPH_CNT: 缓存的字形位图数量。这是最重要的参数!中文常用字几千个,但屏幕同时显示的可能就几十上百个。 * 建议初始设置为256或512。如果出现频繁的文字闪烁(缓存命中率低),再酌情增大。但增大会消耗更多内存。 */ #define LV_FREETYPE_CACHE_FT_FACES 4 #define LV_FREETYPE_CACHE_FT_SIZES 8 #define LV_FREETYPE_CACHE_FT_GLYPH_CNT 512 /* 4. 定义LVGL用于FreeType内存分配的函数(可选但推荐)。 * 如果你的系统有内存管理(如FreeRTOS的pvPortMalloc),可以在这里指定,让FreeType使用你的内存池。 * 如果未定义,FreeType会使用标准库的malloc/free。 */ #define LV_FREETYPE_MALLOC your_malloc_function #define LV_FREETYPE_FREE your_free_function

3.3 获取并处理阿里巴巴普惠体

阿里巴巴普惠体是阿里官方开源的一款优秀中文字体,字形优美,适合屏幕显示。我们可以从阿里官方GitHub仓库(Alibaba-Cloud-Community)下载。

  1. 下载Alibaba-PuHuiTi-Regular.ttf(常规体)文件。
  2. 关键一步:将TTF文件转换为C数组或直接存储到外部Flash。
    • 方法A(推荐,便于管理):使用lv_font_conv工具或在线转换网站,将TTF文件中你需要的字符子集(例如GB2312包含的6000多常用汉字)转换成C数组文件。但这种方法又回到了静态字体的老路,失去了动态加载的灵活性。不推荐用于此动态加载场景。
    • 方法B(本方案核心):将完整的.ttf文件以二进制形式直接存储到你的外部SPI Flash的某个固定地址。例如,使用Flash编程工具(如J-Flash)或者通过单片机程序,在初始化时将存放在SD卡或通过串口发送过来的.ttf文件数据写入到SPI Flash的0x000000地址开始的空间。
    • 你需要实现一个简单的文件系统访问接口,让lv_lib_freetype能够读取到这些数据。对于简单的固定地址存储,我们可以实现一个“包装器”函数。

3.4 实现字体文件读取接口

FreeType默认通过fopenfread等标准C库函数读取字体文件。在嵌入式无文件系统环境下,我们需要重写这些接口。lv_lib_freetype允许我们注册自定义的文件读取器。

假设我们的字体文件已经烧录到SPI Flash的0x00000000地址,大小为font_size字节。我们需要实现一个lv_fs_drv_t驱动:

// 首先,在lv_conf.h中启用文件系统 #define LV_USE_FS_FREETYPE 1 #define LV_FS_FREETYPE_LETTER 'S' // 定义一个驱动器号,例如'S'代表SPI Flash // 在应用程序中实现 #include “lvgl/lvgl.h” #include “lv_freetype.h” #include “your_spi_flash_driver.h” // 你的SPI Flash底层驱动 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) { // 我们忽略path,因为我们只有一个字体文件在固定位置 // 返回一个非NULL的指针作为文件句柄,这里简单返回一个静态变量地址 static int dummy_handle = 0; if(strcmp(path, “S:/alibaba.ttf”) == 0) { // 检查路径,假设我们约定路径为 “S:/alibaba.ttf” return &dummy_handle; } return NULL; } static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br) { // file_p 是 fs_open 返回的句柄,这里我们不用它 static uint32_t read_offset = 0; // 静态变量记录读取位置,这不是线程安全的!仅作示例。 // 实际项目中,你应该将读取偏移量存储在更合理的地方(例如句柄指向的结构体)。 // 从SPI Flash的 read_offset 处读取 btr 字节到 buf spi_flash_read_data(read_offset, buf, btr); *br = btr; // 设置实际读取的字节数 read_offset += btr; // 更新偏移量 return LV_FS_RES_OK; } static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence) { // 实现seek,设置读取偏移量 // 同样,需要更新存储偏移量的变量 // ... return LV_FS_RES_OK; } static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p) { // 关闭“文件”,这里可以重置读取偏移量 // ... return LV_FS_RES_OK; } void lv_port_fs_init(void) { static lv_fs_drv_t fs_drv; lv_fs_drv_init(&fs_drv); fs_drv.letter = ‘S’; fs_drv.open_cb = fs_open; fs_drv.read_cb = fs_read; fs_drv.seek_cb = fs_seek; fs_drv.close_cb = fs_close; lv_fs_drv_register(&fs_drv); }

注意:上面的fs_readfs_seek实现是高度简化的,静态变量read_offset在多处访问字体文件时会导致错乱。正确的做法是在fs_open时动态分配一个包含当前文件偏移量的上下文结构体,并将指针返回作为file_p。在fs_readfs_seekfs_close中操作这个结构体。这是第一个容易踩的坑。

4. 核心步骤:初始化、创建字体与使用

配置好底层接口后,就可以在应用层进行初始化和使用了。

4.1 FreeType库初始化

在LVGL初始化之后,硬件初始化之后,调用FreeType的初始化函数。

#include “lv_freetype.h” void app_init(void) { lv_init(); // ... 你的显示、输入设备初始化 ... lv_port_fs_init(); // 初始化我们自定义的文件系统驱动 // 初始化 lv_freetype // 第一个参数:告诉FreeType最多同时缓存多少个字体文件(face),应 <= lv_conf.h中的 LV_FREETYPE_CACHE_FT_FACES // 第二个参数:告诉FreeType最多同时缓存多少种字号(size),应 <= LV_FREETYPE_CACHE_FT_SIZES // 第三个参数:最大缓存字形数,应 <= LV_FREETYPE_CACHE_FT_GLYPH_CNT // 第四个参数:是否在初始化时预加载一些基本字形(如ASCII),1表示开启,可以加速初始文本显示。 lv_freetype_init(4, 8, 512, 1); }

4.2 创建动态字体对象

这是最关键的一步。我们使用lv_freetype_font_create来创建一个字体对象。

// 创建阿里巴巴普惠体,常规风格,大小为30像素 lv_font_t * font_alibaba_30 = lv_freetype_font_create(“S:/alibaba.ttf”, 30, LV_FREETYPE_FONT_STYLE_NORMAL); if(font_alibaba_30 == NULL) { LV_LOG_ERROR(“Failed to create font!”); // 检查:1. 路径是否正确 2. 文件系统驱动是否工作 3. TTF文件是否损坏 4. 缓存参数是否太小 }
  • 第一个参数:字体文件路径。必须匹配你在自定义文件系统驱动中识别的路径格式(如“S:/alibaba.ttf”)。
  • 第二个参数:字体大小,单位是像素(px)。这是指字体的高度(大致对应字体的em尺寸),实际渲染出的字符高度可能略小于此值。
  • 第三个参数:字体风格。LV_FREETYPE_FONT_STYLE_NORMAL是常规体。如果你的TTF文件包含粗体(Bold)、斜体(Italic)等样式,也可以选择对应的风格,但前提是字体文件内部确实包含了这些变体。阿里巴巴普惠体是独立文件(Regular, Bold),所以这里用NORMAL

4.3 在LVGL对象中使用字体

创建成功后,这个lv_font_t*指针就可以像任何其他LVGL内置字体一样使用了。

// 创建一个标签 lv_obj_t * label = lv_label_create(lv_scr_act()); lv_label_set_text(label, “阿里巴巴普惠体 - 30px”); lv_obj_set_style_text_font(label, font_alibaba_30, 0); // 应用字体 lv_obj_center(label); // 再创建一个不同大小的字体 lv_font_t * font_alibaba_16 = lv_freetype_font_create(“S:/alibaba.ttf”, 16, LV_FREETYPE_FONT_STYLE_NORMAL); lv_obj_t * label2 = lv_label_create(lv_scr_act()); lv_label_set_text(label2, “小字号文本 - 16px”); lv_obj_set_style_text_font(label2, font_alibaba_16, 0); lv_obj_align_to(label2, label, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);

4.4 字体对象的生命周期管理

非常重要!通过lv_freetype_font_create创建的字体对象,不会在LVGL内部自动释放。如果你需要动态切换字体(比如改变字号),或者程序运行中不再需要某个字体,必须手动销毁,否则会导致内存泄漏。

// 当不再需要 font_alibaba_30 时 lv_freetype_font_delete(font_alibaba_30); font_alibaba_30 = NULL;

注意:确保在销毁字体对象之前,所有使用该字体的UI对象(如label)已经被删除,或者已经切换到了其他字体。否则,访问已销毁字体的UI对象会导致程序崩溃。

5. 性能优化与疑难杂症排查

动态字体渲染虽然灵活,但相比静态点阵字体,对CPU和内存的消耗更大。在实际项目中,不进行优化很可能会导致界面卡顿。

5.1 缓存策略调优

lv_lib_freetype的性能核心在于缓存。lv_conf.h中的三个缓存宏需要根据你的实际场景仔细调整。

  • LV_FREETYPE_CACHE_FT_GLYPH_CNT(字形缓存数):这是最重要的参数。设置太小,缓存命中率低,频繁的渲染操作会严重拖慢速度,表现为文字在滚动或重新显示时有明显的绘制延迟或闪烁。设置太大,会浪费RAM。调试方法:可以先设一个较大的值(如1024),确保功能正常。然后,在运行你的典型应用场景(遍历所有界面)后,通过调试接口(如果有)或打印日志,查看实际缓存使用量,再调整到一个合理的安全值(例如,最大使用量的1.5倍)。
  • 监控缓存命中率lv_freetype库内部可能有统计信息。你可以尝试修改其源码,添加对缓存命中/未命中次数的计数,并在运行时输出,这是定位性能瓶颈最直接的方法。

5.2 避免频繁创建/销毁字体

创建字体对象(lv_freetype_font_create)是一个相对耗时的操作,因为它涉及打开字体文件、初始化FreeType的face和size对象。绝对不要在每帧渲染或频繁调用的回调函数中创建字体!正确的做法是:

  1. 预创建:在程序初始化阶段,创建所有可能用到的字体(如大小16, 24, 32的常规体和粗体),并保存其指针。
  2. 复用:在整个UI生命周期中,通过lv_obj_set_style_text_font来切换这些预创建的字体对象。

5.3 中文显示乱码或问号

这是最常见的问题,根本原因在于编码不一致

  • 源文件编码:确保你的.c源文件保存的编码是UTF-8(不带BOM)。在STM32CubeIDE中,可以在文件属性中查看和修改。
  • LVGL文本编码设置:在lv_conf.h中,确保LV_TXT_ENC设置为LV_TXT_ENC_UTF8。这是LVGL内部处理字符串的编码方式。
    #define LV_TXT_ENC LV_TXT_ENC_UTF8
  • 编译器设置:确保你的编译器将字符串常量当作UTF-8处理。对于GCC/ARM GCC,通常不需要特殊设置。如果问题依旧,可以尝试在字符串前显式指定编码(虽然不是标准C,但某些编译器支持):u8”中文”
  • 字体文件本身:确认你下载的阿里巴巴普惠体TTF文件是完整且支持中文的。可以用电脑上的字体查看器打开,看看是否能正常显示中文。

5.4 内存不足与崩溃

动态字体渲染消耗两部分主要内存:

  1. FreeType缓存内存:由lv_freetype_init的参数和lv_conf.h中的宏共同决定。这部分内存从LVGL的内存池(或你定义的LV_FREETYPE_MALLOC)中分配。如果设置过大,初始化时可能直接分配失败。
  2. 字形位图内存:每个缓存的字形位图都会占用内存。一个30px的中文字形,如果使用ARGB8888格式(32位色),大约占用30*30*4 ≈ 3.6KB。缓存512个这样的字形就需要近2MB的RAM!这显然在MCU上不现实。
    • 解决方案:在lv_conf.h中,务必启用LVGL的索引色(Indexed)或Alpha-only渲染,并降低颜色深度。例如,使用LV_COLOR_DEPTH 16甚至8,并设置LV_FREETYPE_BITMAP_FORMAT LV_FREETYPE_BITMAP_FORMAT_A8。这样,字形位图只存储透明度信息(A8格式,每个像素1字节),内存占用锐减至原来的1/4或更少。
    #define LV_COLOR_DEPTH 16 // 在 lv_freetype.h 或相关配置中,确保位图格式为A8 // lv_lib_freetype 通常会自动选择最高效的格式,但明确指定更安全。

5.5 渲染速度慢

除了缓存命中率低,渲染速度还可能受以下因素影响:

  • 抗锯齿:FreeType默认会进行抗锯齿渲染,质量好但更耗时。对于小像素屏,可以考虑关闭抗锯齿或使用单色渲染(LV_FREETYPE_RENDER_MODE_MONO),但这需要修改lv_lib_freetype的内部配置,不是所有版本都支持。
  • SPI Flash读取速度:字体文件存储在外部SPI Flash,首次加载字形时需要读取文件数据。确保你的SPI时钟配置在芯片和Flash允许的最高频率,并使用Quad SPI等更快模式(如果支持)。
  • CPU频率:复杂的矢量字体渲染(尤其是中文)是计算密集型任务。提升MCU主频能直接改善渲染速度。

6. 进阶技巧:字体混合使用与图标集成

在实际项目中,我们很少只使用一种字体。更常见的场景是:英文和数字使用LVGL内置的紧凑型字体(如lv_font_montserrat_16),中文则使用FreeType渲染的阿里巴巴普惠体。同时,界面中还需要图标字体(如FontAwesome)。

6.1 创建混合字体(Fallback Font)

LVGL支持字体回退(Fallback)机制。你可以创建一个字体链表,当第一个字体找不到某个字符时,会自动尝试下一个字体。

// 1. 创建英文字体(LVGL内置)和中文动态字体 lv_font_t * font_en_16 = &lv_font_montserrat_16; lv_font_t * font_cn_16 = lv_freetype_font_create(“S:/alibaba.ttf”, 16, LV_FREETYPE_FONT_STYLE_NORMAL); // 2. 设置中文字体为英文字体的回退字体 font_en_16->fallback = font_cn_16; // 3. 在标签中使用英文字体 lv_obj_set_style_text_font(label, font_en_16, 0); lv_label_set_text(label, “Hello 世界 123”); // “Hello”和“123”用montserrat渲染,“世界”用阿里巴巴普惠体渲染

这样,既能保证英文和数字的渲染效率(使用轻量的内置点阵字体),又能完美显示中文。

6.2 管理多个回退字体

如果需要多级回退(例如,英文 -> 中文 -> 图标),可以手动构建链表:

font_en_16->fallback = font_cn_16; font_cn_16->fallback = &lv_font_awesome_icon_16; // 假设这是图标字体

注意:回退链不宜过长,每次查找失败都会遍历链表,影响性能。

6.3 图标字体的动态加载

图标字体(如TTF格式的FontAwesome)本质上也是一种TTF文件,完全可以和阿里巴巴普惠体一样,用lv_lib_freetype来动态加载。步骤完全相同:

  1. fontawesome-webfont.ttf存入SPI Flash。
  2. lv_freetype_font_create创建图标字体对象,例如大小为20px。
  3. 在需要显示图标的地方,使用对应的Unicode字符码。例如,FontAwesome的“心形”图标Unicode是0xf004。在C代码中,你可以用LV_SYMBOL_HEART(如果lvgl定义了)或者直接写字符串“\xef\x80\x84”(这是U+F004的UTF-8编码)来设置文本。
lv_font_t * font_icon_20 = lv_freetype_font_create(“S:/fontawesome.ttf”, 20, LV_FREETYPE_FONT_STYLE_NORMAL); lv_obj_t * icon_label = lv_label_create(lv_scr_act()); lv_obj_set_style_text_font(icon_label, font_icon_20, 0); lv_label_set_text(icon_label, “\xef\x80\x84”); // 显示一个心形图标

通过这种方式,你可以将丰富的矢量图标库集成到你的嵌入式UI中,并且可以自由缩放,不受像素限制。

7. 项目总结与最终建议

lv_lib_freetype用于动态加载中文字体,是一个“用计算换存储,用灵活性换复杂度”的典型方案。经过这个项目的实践,我的体会是,它非常适合用于Flash资源紧张、但需要显示多种字号或大量中文、且CPU有一定余量的嵌入式场景。

几个关键决策点回顾:

  1. 存储 vs 计算:如果你的Flash足够大,且字体样式固定(就一两种字号),静态点阵字体方案依然是最简单、最快速的。反之,如果需要动态性、多字号、高质量,就选择FreeType方案。
  2. 缓存配置是灵魂LV_FREETYPE_CACHE_FT_GLYPH_CNT这个值需要你根据实际UI的文本量进行 profiling(性能剖析),找到一个平衡点。太大吃内存,太小卡界面。
  3. 内存格式是关键:务必使用A8(Alpha Only)位图格式来缓存字形,这是能在MCU上运行此方案的前提。这意味着你的显示驱动需要支持用单独的色值和Alpha掩膜位图进行混合绘制,LVGL的lv_draw_sw层是支持这种操作的。
  4. 文件读取接口要稳健:自定义文件系统驱动是第一个拦路虎。务必处理好文件偏移量(seek)和多文件访问的问题,否则会出现字体数据读错、乱码等难以排查的问题。建议参考LVGL官方文件系统驱动的实现。

最后的小技巧:在项目初期,可以先将TTF文件放在SD卡上,通过FatFS来读取。这样调试起来更方便(直接替换SD卡里的字体文件即可)。等功能稳定后,再移植到SPI Flash上,并将文件读取接口替换为直接读Flash。这种分阶段的方法能有效降低前期调试的复杂度。

整个集成过程就像搭积木,理解清楚FreeTypelv_lib_freetypeLVGL字体接口你的文件系统这四层之间的关系,然后一步步打通,剩下的就是针对具体性能表现的精细调优了。希望这篇详尽的踩坑记录,能帮你顺利地在下一个LVGL项目里用上漂亮又灵活的中文字体。

返回列表