第1章:项目概览——从 ESP32 到 SiFli 的移植

同一个 EPUB 阅读器,换一块芯片、换一套 SDK,究竟要改哪些东西?

🔬

本章导师:费曼

核心方法论:第一性原理,从"为什么"出发

「上一季我们用一块十几美元的 ESP32 把 EPUB 电子书"榨"上了墨水屏。这季换平台了:思澈科技的 SF32、RT-Thread、SCons……名字全是新的,但请记住,移植的第一步永远是问"为什么"——为什么书还是那本书,代码却不能直接搬过来?把平台的差异想清楚,后面每一章都是在给这个问题填空。」

1.1 零基础铺垫:SiFli / SF32 与「移植」是什么

先回答一个很多人会问的问题:SiFli(思澈科技,官网 sifli.com)是谁?它是一家总部在南京的芯片设计公司,专注物联网与低功耗场景,产品主打"一颗芯片把 CPU、内存、蓝牙、各种外设全包了"。而 SF32 就是思澈的 MCU 平台(微控制器平台,即一套以低功耗 SoC 为核心的芯片家族)。这套平台旗下有 SF32LB52X、SF32LB55X、SF32LB56X、SF32LB57X、SF32LB58X 五条产品线——从 SDK 里内置的蓝牙、音频、DFU 升级等中间件能看出,它是面向蓝牙低功耗物联网应用的平台。本项目用到的芯片,在板级配置里写得很清楚:CHIP = 'SF32LB52X'。如果你完全没碰过思澈,只把它想成"一个国产的、类似乐鑫 ESP32 的 MCU 品牌"就够了——上季的乐鑫(Espressif)做 ESP32,这季的思澈(SiFli)做 SF32,两家都在做"小芯片、大生态"。

那么 "移植"(porting)是什么意思?一句话说人话:把跑在 A 平台上的项目,改造到 B 平台上去跑。注意关键词是"改造"——不是"重写",也不是"照抄"。因为下面三样东西一旦换了平台,几乎必然跟着变:芯片不同(寄存器、内存布局、外设引脚都换了)、SDK 不同(SDK 是"软件开发工具包",芯片厂商提供的驱动与函数库集合;ESP32 用乐鑫的 ESP-IDF,SF32 用思澈的 SiFli SDK)、构建方式不同(上季用 PlatformIO + CMake,这季用 SCons)。而业务逻辑——EPUB 怎么解析、文字怎么换行分页、翻页怎么渲染——这些跟芯片无关的代码,恰恰是移植时最希望能"原样搬走"的部分。

本教程的主角,就是这个问题的活样本:EPD_Reader-main/ 仓库里的 epdiy-epub/ 项目,就是 diy-esp32-epub-reader(上季那条「ESP32 电子墨水屏」技术线讲过)在 SF32-OED-EPD 系列开发板上的适配版。SF32-OED-EPD 是思澈配套的电子墨水屏(EPD)开发板系列,本仓库就带了 V1.1、V1.2 等多块板配置。它和你上一季用的 M5 Paper / LilyGo 一样,都是"单片机 + 墨水屏 + 电池"的阅读器硬件,只是大脑从 ESP32 换成了 SF32。

最后把两边的差异表列出来。这张表是全书的地基——后面每一章(构建、板级、显示、输入、电池……)都在处理这张表里的某一行。

维度上季:ESP32 平台本季:SiFli SF32 平台
芯片厂商乐鑫科技(Espressif)思澈科技(SiFli)
芯片型号ESP32(双核 Xtensa)SF32LB52X(Cortex-M33 系)
开发框架ESP-IDFSiFli SDK(基于 RT-Thread 定制)
操作系统FreeRTOSRT-Thread(可选 FreeRTOS)
构建系统PlatformIO / CMakeSCons(Python)
配置方式sdkconfig / menuconfigKconfig + proj.conf / board.conf
目标板M5 Paper / LilyGo T5 等SF32-OED-EPD V1.1 / V1.2 系列
# SiFli-SDK/…/version.txt(SDK 版本号,本仓库所用)
v2.5.0
# epdiy-epub/sf32-oed-epd_v11/hcpu/rtconfig.py(芯片 / 核 / 目标名,节选)
CHIP = 'SF32LB52X'
CORE = "HCPU"
TARGET_NAME = 'bf0_ap'
JLINK_DEVICE = 'SF32LB52X_NOR'
费曼提示

给"移植"画一张最容易懂的图:把一套房子从北京搬到上海——户型(芯片)不同,得改水电(驱动);物业(SDK)不同,得改缴费方式(API);家具(业务逻辑)只要能塞进新户型,就尽量原样搬。所以移植的第一步不是动手改代码,而是先盘点:哪些是"家具"(能搬),哪些是"水电"(必须重排)。后面 1.3 的仓库解剖,本质上就是在做这份盘点。

1.2 项目由来:diy-esp32-epub-reader 的 SiFli 适配版

这个项目不是从零开始写的。它的 README「项目简介」用一句话交代了身世:基于 atomic14 的 diy-esp32-epub-reader 进行适配,运行于 SiFli SF32-OED-EPD 系列开发板,用于 EPUB 电子书阅读与低功耗场景。也就是说,它是一位"移植者"(不是原项目作者)拿上季那个开源阅读器,在思澈平台上重新落地。为什么选它来移植?因为它的"家具"质量足够好:EPUB 解析、zip 解包、HTML 解析、换行分页、渲染抽象,这些核心模块都放在独立的 lib/Epub/ 里,和硬件平台解耦得很干净——这正是适合移植的仓库长相。

那到底继承了什么、改了什么?先说继承的"家具":lib/Epub/ 四个子模块原样搬了过来——EpubList(书目 / 阅读器 / 目录,顺带内含 tinyxml2)、RubbishHtmlParser(XHTML 解析,产出 TextBlock / ImageBlock)、Renderer(渲染抽象与图像合成)、ZipFile(包内文件寻址)。配套的第三方库也基本沿用了:miniz-2.2.0(zip 解压)、tjpgd3(JPEG 解码)、png/PNGdec(PNG 解码)、epdiy(墨水屏驱动)。连示例书 disk/pg43-images.epub 都和上季仓库里的那本古登堡书同名同源。

再说"水电"级别的改动,这才是移植的精华。第一,操作系统换了:上季用 FreeRTOS + ESP-IDF 的消息队列,这季用 RT-Thread,主循环里从 rt_mq_recv(ui_queue, …) 接收按键/触控事件(消息队列是 RTOS 里任务间通信的通道,相当于"任务的邮箱")。第二,渲染器重写了:新增 src/boards/SF32PaperRenderer.h,对接 SF32 的墨水屏控制器(EPIC/LCDC),而不是 epdiy 的并行驱动。第三,板级抽象整个换了血src/boards/ 下不再是 LilyGo / Epdiy / M5Paper,而是 battery(电池)、controls(按键/触控)、display_dbi、display_spi、touch(FT5446U / FT6336U / GT967 三款触控芯片)。第四,追加了大量上季没有的功能:中英文显示、动态字体加载(font_manager.c)、阅读设置页(reading_settings.cpp)、触控区域管理(UIRegionsManager.cpp)、电量/充电/低功耗状态机。

类别直接继承(能搬的家具)重写 / 新增(必须重排的水电)
EPUB 解析lib/Epub/ 全部四个子模块
第三方库miniz、tjpgd3、PNGdec、epdiy
操作系统 / 框架FreeRTOS → RT-Thread;ESP-IDF → SiFli SDK
渲染Renderer 抽象接口SF32PaperRenderer(对接 EPIC/LCDC)
板级 / 输入boards/ 重写:电池、按键+触控、DBI/SPI 屏驱
新功能中英文显示、动态字体、阅读设置、状态机
# epdiy-epub/README.md · 项目简介
本项目基于 atomic14/diy-esp32-epub-reader 进行适配,
运行于 SiFli SF32-OED-EPD 系列开发板,用于 EPUB 电子书阅读与低功耗场景。
# lib/Epub/(继承自上游的核心解析库,四个子模块)
lib/Epub/
  EpubList/          书目 / 阅读器 / 目录(内含 tinyxml2)
  Renderer/          渲染抽象与图像合成
  RubbishHtmlParser/ XHTML 解析(TextBlock / ImageBlock)
  ZipFile/           包内文件寻址(配合 miniz)
费曼提示

判断"一个移植算成功还是算重写",只要数一件东西:上游的 lib/Epub 还在不在。它还在,说明 EPUB 解析这条主线被完整保住了,后续所有章节都在讲"新平台怎么喂数据给它、怎么把结果画出来"。它要是没了,那就不叫移植,叫借鉴思想重写——难度完全是两个量级。看到 lib/Epub 原样躺着,这本移植教程的地基就稳了。

1.3 仓库解剖:epdiy-epub 与 SiFli-SDK

打开仓库根目录 EPD_Reader-main/,它只有两层,像一门"必修课 + 教科书":epdiy-epub/ 是项目代码(12 个一级目录,固件、脚本、板级配置都在这),SiFli-SDK/ 是思澈平台的 SDK(官方提供的固件开发框架,内含一个带 40 字节哈希长名的子目录,是克隆下来的 SDK 本体)。搞不清"哪些是作者写的、哪些是厂商给的",读这个仓库就会乱——记住口诀:业务逻辑在 epdiy-epub,平台能力在 SiFli-SDK。

先看 epdiy-epub/。它按功能分成几摊:src/ 是核心业务逻辑,lib/ 是解析库与第三方依赖,project/ 是构建脚本(SCons + Kconfig),sf32-oed-epd_base/sf32-oed-epd_v11/sf32-oed-epd_v12/sf32-oed-epd_v12_spi/ 是四套板级配置(后三套是不同硬件版本,base 是它们共用的公共配置),disk/ 是内置 Flash 里打包的样书,font/ 是内置中文字体(DroidSansFallback.ttf),waveform/ 是墨水屏波形文件与解码库,scripts/tools/ 是开发期用的字体/图片/波形转换工具。

再聚焦到 src/,这是后面十几章的主战场,先认几个"门面"文件:main.cpp 是程序入口,跑一个 UI 状态机主循环;epub_screen.cpp 负责各页面(主页、书库、目录、阅读、设置…)的绘制与事件处理;reading_settings.cpp 是阅读设置页(字体/字号/字重/行距/边距 + 持久化到 /settings.cfg);font_manager.c 是字体管理器,扫描 TF 卡 /fonts 目录动态加载外部字体;UIRegionsManager.cpp 管理触控区域(点击区域动态注册与命中检测);boards/ 是硬件抽象层,下面分 battery(电池)、controls(输入)、display_dbi(8-bit 并行墨水屏)、display_spi(SPI 墨水屏)、touch(触控芯片)五个子目录。

至于 SiFli-SDK/ 顶层,你只要知道四大件就够了:drivers/(HAL 硬件抽象层与芯片寄存器定义,类似 ESP-IDF 的 soc/ 与 hal/)、rtos/(RT-Thread / FreeRTOS 与 OS 抽象层)、middleware/(思澈自研组件:蓝牙、音频、DFU 升级等)、external/(第三方组件,SDK 里甚至自带 LVGL、mbedTLS、FFmpeg 等)。普通应用开发基本碰不到它——构建时 SCons 会自动把需要的部分编进来。

仓库 / 目录职责是谁写的
epdiy-epub/src/核心业务逻辑(状态机、页面、字体、设置)本项目作者
epdiy-epub/lib/EPUB 解析库 + 第三方依赖(miniz/epdiy…)上游 + 开源库
epdiy-epub/project/SCons 构建脚本 + Kconfig.proj 配置本项目作者
epdiy-epub/sf32-oed-epd_*/四套板级配置(v11 / v12 / v12_spi / base)本项目作者
epdiy-epub/disk/ font/ waveform/样书、内置字体、波形文件等资源素材 + 工具生成
SiFli-SDK/思澈固件开发框架(HAL / RTOS / 中间件)芯片厂商
# epdiy-epub/ 目录结构(节选)
epdiy-epub/
  disk/                   内置 Flash 样书(3 个 EPUB)
  font/                   内置字体 DroidSansFallback.ttf
  lib/                    EPUB 解析 + 图像解码 + epdiy
  project/                SCons 构建脚本 + Kconfig.proj
  sf32-oed-epd_base/      V1.1 / V1.2 公用板级配置
  sf32-oed-epd_v11/       V1.1 板级配置
  sf32-oed-epd_v12/       V1.2 板级配置
  sf32-oed-epd_v12_spi/   V1.2 + SPI 墨水屏配置
  scripts/                字体 / 图片生成工具
  src/                    核心业务逻辑
  tools/                  波形 xlsx → C 数组工具
  waveform/               墨水屏波形文件与解码库
# epdiy-epub/src/ 关键文件一览
src/
  main.cpp             程序入口:UI 状态机主循环
  epub_screen.cpp      各页面绘制与事件处理
  reading_settings.cpp 阅读设置 + 持久化 /settings.cfg
  font_manager.c       字体管理器(扫描 TF 卡 /fonts)
  UIRegionsManager.cpp 触控区域管理
  boards/              硬件抽象:battery/controls/display_dbi/display_spi/touch
费曼提示

读任何嵌入式仓库,先问三件事:入口在哪(main)、状态在哪(状态机)、硬件差异藏在哪(boards)。这个仓库分别对应 src/main.cppsrc/type.hAppUIStatesrc/boards/。把这三个锚点钉进脑子里,剩下的目录都是它们的"补给"。

1.4 功能亮点一瞥:中文、字体、设置与状态机

这个移植版相比上游最大的进步,是中文支持。上季的 ESP32 版本只内置英文字体,而这版用 FreeType 加载了内置的 DroidSansFallback(一款开源中文字体,存放在 Flash XIP 区),开箱就能显示中英文。为了控制资源占用,README 特意说明默认未启用粗体/斜体中文字库——中文全字库动辄几 MB,粗斜体再翻倍,对嵌入式存储是巨大负担,所以只保底、不铺张。配合 font_manager.c,它甚至支持动态字体加载:往 TF 卡 /fonts 目录丢任意 .ttf / .otf 字体文件(最多 64 个),开机就会出现在阅读设置的字体列表里,选中后由 FreeType 直接读文件渲染。

阅读设置 UI 是另一大亮点。从"功能设置 → 阅读设置"或阅读页覆盖操作层进入,可以调字体、字号(24/28/32/36/40/44/48 px)、字重(正常/中粗/粗体)、行距(1.0x–2.0x)、边距(5–20 px),保存后以 key=value 文本格式持久化到 TF 卡 /settings.cfg,下次开机自动加载;指定字体不可用时自动回退内置字体。这在上季的三按键极简版里是完全不敢想的。

输入也从"三个按键"升级成"按键 + 触控"双通道。按键保留了 UP / DOWN / SELECT 语义,并新增 UPGLIDE(上滑)呼出阅读页覆盖操作层;触控芯片支持 FT5446U / FT6336U / GT967 三款,采用"先选中再确认"机制降低误触。操作说明里主页面点击左右区域切换选项、点击中间确认,书库页点击书籍项再点击确认进入目录,阅读页左右区域翻页、上滑呼出覆盖层——一套完整的触控导航。

最值得先打个照面的是 UI 状态机与低功耗AppUIState 枚举定义了从主页面、书库、目录、阅读、阅读设置、功能设置、欢迎页,到低电量页、充电页、关机页共 12 个状态(还有天气相关状态,1.5 会讲到)。主循环 5 小时无交互进入关机页(常量 TIMEOUT_SHUTDOWN_TIME);无操作达到设置超时后进入欢迎页(类似熄屏);电量低于阈值进低电量页并抑制普通操作;充电状态变化只在百分比或状态真正变化时才刷屏,避免无效刷新。

设置项可选值说明
字体Default(内置)及 TF 卡 /fonts 全部 .ttf / .otf最多 64 个外部字体,FreeType 动态加载
字号24 / 28 / 32 / 36 / 40 / 44 / 48 px像素单位
字重正常 / 中粗 / 粗体
行距1.0x / 1.2x / 1.4x / 1.6x / 1.8x / 2.0x
边距5 / 8 / 11 / 14 / 17 / 20 px页面边距
保存退出确认后应用并持久化写入 /settings.cfg
/* src/type.h:AppUIState 状态枚举(节选) */
typedef enum {
  MAIN_PAGE,               // 主页面
  SELECTING_EPUB,          // 书库
  SELECTING_TABLE_CONTENTS, // 目录
  READING_EPUB,            // 阅读
  READING_SETTINGS,        // 阅读设置
  SETTINGS_PAGE,           // 功能设置
  WELCOME_PAGE,            // 欢迎(超时熄屏)
  LOW_POWER_PAGE,          // 低电量
  CHARGING_PAGE,           // 充电
  SHUTDOWN_PAGE            // 关机
} AppUIState;
/* src/epub_screen.cpp:超时与全刷周期选项(设置页可选值) */
static const int kTimeoutOptions[] = {5, 10, 30, 60, 0}; // 单位:分钟,0 为不关机
static const int kFullRefreshOptions[] = {5, 10, 20, 0};  // 全刷周期(次)
static int full_refresh_idx = 1;  // 默认 10 次全刷后做一次整屏刷新
费曼提示

电子墨水屏有个物理特性叫"残影"——同一块区域反复局部刷新会留灰影,所以读屏器行业有个惯例:隔几次局刷就整屏"洗"一次。这个项目把"隔几次"做成了设置项 kFullRefreshOptions(5/10/20 次或每次全刷)。理解"状态机 + 低功耗 + 刷新策略"这三件套,你就抓住了墨水屏设备的灵魂——它们在后半本(第 6、11 章)会反复登场。

1.5 完成度与本文路线:一个「在途开发」的项目

这个项目最诚实的地方,是它没把自己包装成"已完成"。README 的「当前功能」清单,是已经实现的部分——中英文显示、双输入、阅读设置、动态字体、电量状态机,上面 1.4 讲的都在清单里。但仓库里同时躺着一批"未完成痕迹",它们是这本书最珍贵的第一手教材:比如 src/wheather.c天气功能(拼写还是 wheather),代码里已经定义了天气页、城市选择页两个状态,甚至接了"心知天气"的 HTTP API,但 README 的「当前功能」里一个字都没提——因为这套功能还没有完整接入主流程。

证据不止一处。打开 src/epub_screen.h,主页面的选项枚举 MainOption4 项(打开书库 / 进入设置 / 查看天气 / 继续阅读),而 README「主页面」一节只写了 3 个入口(打开书库 / 继续阅读 / 进入设置)——文档与代码出现了"漂移",漂的恰恰是天气。再看 src/boards/display_dbi/epd_display.c,墨水屏驱动里有一段 CopyToMixedGrayBuffer,当数据格式是单色 LCDC_PIXEL_FORMAT_MONO 时直接 RT_ASSERT(0); //Not implemented yet——即"单色混合灰"这条路径还没写。源代码里的 TODO 注释也不止一处。这些都是"在途开发"的铁证。

这里引出一条贯穿全书的阅读原则:当 README 与代码冲突时,以代码为准。README 是作者"想让你看到的",代码是"它现在真实的状态";一个诚实作者写 README 也会漏掉半成品。所以本教程从第 3 章起,每讲完一块,都会做一次「已实现 / 未实现」盘点(第 14、15 章会集中清算),让你亲眼看到"一个开源移植项目到底完成到什么程度"。这不只是知识,更是一种读源码的职业习惯。

最后是路线图。全书 16 章分五个分部:本季前两章(第 1-2 章)讲认识项目与平台构建;接着「硬件平台」(第 3-6 章)讲板级配置、显示驱动、输入、电池低功耗;「解析与渲染」(第 7-10 章)讲 EPUB 继承与中文适配、动态字体、阅读设置、渲染绘制;「UI 与状态机」(第 11-13 章)讲状态机总览、书库目录阅读流程、覆盖操作层与触控区域;最后「现状与展望」(第 14-16 章)做已实现盘点、未实现清单、二次开发与新屏移植。每走完一段,你都会更接近"自己动手改这个项目"。

类型内容证据 / 位置
已实现中英文显示、双输入、阅读设置、动态字体、电量状态机README「当前功能」
未完成(天气)天气页 / 城市选择页状态、HTTP 获取已写,但未列入功能清单src/wheather.cepub_screen.hWEATHER_PAGE
未完成(驱动)单色数据格式的混合灰拷贝路径epd_display.cRT_ASSERT(0); //Not implemented yet
文档漂移README 主页面写 3 入口,代码 MainOption 有 4 项(含查看天气)README「主页面」 vs epub_screen.h
其他痕迹源码中多处 TODO / FIXME 注释grep TODO src/
/* src/epub_screen.h:主页面选项——代码 4 项 vs README 3 项 */
typedef enum {
  OPTION_OPEN_LIBRARY = 0,   // 打开书库
  OPTION_ENTER_SETTINGS,       // 进入设置
  OPTION_WEATHER,              // 查看天气(README 未列出)
  OPTION_CONTINUE_READING,     // 继续阅读
  OPTION_COUNT
} MainOption;
/* src/boards/display_dbi/epd_display.c:未实现分支 */
if (hlcdc->Layer[HAL_LCDC_LAYER_DEFAULT].data_format == LCDC_PIXEL_FORMAT_MONO)
{
    RT_ASSERT(0); //Not implemented yet
}
注意

README 说主循环"默认 5 小时无交互进入关机页",代码里 TIMEOUT_SHUTDOWN_TIME 5 同时被两处使用:关机循环用 60 * 1000 * 60 * TIMEOUT_SHUTDOWN_TIME(小时级),而欢迎页超时经 screen_init(TIMEOUT_SHUTDOWN_TIME) 走的是 screen_get_timeout_shutdown_minutes()(分钟级)——同一个常量身兼"5 小时关机"与"5 分钟熄屏"两个角色。这不一定是 bug,但足以说明:移植项目的常量语义要对着 main.cpp 逐行核实,别只信 README 一句话。

章末练习

练习 1:术语三连 入门

用一句话分别解释:SiFliSF32移植SDK。要求不看本页,先自己写,再对照 1.1 检查有没有把"芯片品牌 / 芯片平台 / 改造动作 / 厂商工具包"四个概念搞混。

提示

SiFli 是公司名,SF32 是产品线名,两者一个"人"一个"姓";SDK 是工具,移植是动作。

参考答案

SiFli(思澈科技)是南京的芯片设计公司;SF32 是思澈的 MCU 平台,旗下有 SF32LB52X/55X/56X/57X/58X 五条产品线;SDK 是芯片厂商提供的软件开发工具包(驱动与函数库集合),ESP32 用 ESP-IDF,SF32 用 SiFli SDK;移植是把跑在 A 平台的代码改造到 B 平台——芯片、SDK、构建方式几乎必然要换,业务逻辑尽量原样搬。

练习 2:仓库解剖 入门

打开 EPD_Reader-main/,指出以下五处的位置:(a) 固件程序入口在哪个文件?(b) UI 状态枚举定义在哪个文件、共几个状态?(c) 内置样书放在哪个目录?(d) 构建脚本在哪个目录?(e) 思澈平台 SDK 在哪个目录?

提示

对照 1.3 的仓库地图:入口在 src/,状态枚举在 src/type.h,书在 disk/,构建在 project/,SDK 在顶层。

参考答案

(a) src/main.cppint main()main_task,UI 状态机主循环);(b) src/type.hAppUIState,主流程 10 个状态 + 天气相关 2 个,共 12 个;(c) disk/no_oebps.epuboebps.epubpg43-images.epub);(d) project/(SConstruct / SConscript / Kconfig.proj);(e) SiFli-SDK/

练习 3:找"在途开发"的证据 进阶

对照 1.5,到源码里亲自核验两件事:(1) 主页面选项 MainOption 到底有几项、在哪定义、第 2 项是什么?(2) 用 grep -rn "Not implemented" src/ lib/ 找未实现注释,看它落在哪个函数、哪种数据格式的分支里。

提示

枚举在 src/epub_screen.h;未实现注释在 src/boards/display_dbi/epd_display.cCopyToMixedGrayBuffer 里,分支条件是 LCDC_PIXEL_FORMAT_MONO

参考答案

(1) MainOption 有 4 项:OPTION_OPEN_LIBRARYOPTION_ENTER_SETTINGSOPTION_WEATHER(查看天气,README 未列入)、OPTION_CONTINUE_READING,定义在 src/epub_screen.h。(2) 未实现注释位于 epd_display.cCopyToMixedGrayBuffer 函数:当图层数据格式为 LCDC_PIXEL_FORMAT_MONO(单色)时 RT_ASSERT(0),即"单色→混合灰"的拷贝路径尚未实现,只有 LCDC_PIXEL_FORMAT_A4(4-bit 灰)路径是通的。这正是"在途开发"的现场。

练习 4:第一性原理推演 挑战

用第一性原理回答:为什么移植到 SF32 后,第一个被重点解决的是中文字体,而不是别的功能?提示从"目标市场 / 字体体积 / 渲染链路"三个角度各想一层,并给出你的推理链。

提示

想三点:思澈的客户主要在哪、英文 95 个字符 vs 中文常用 6-7 千字的体积差、FreeType 在这种体积下还能不能塞进嵌入式存储与内存。

参考答案

(1) 市场:思澈主打国内物联网与消费电子,中文是基本盘,一个不显示中文的阅读器在国内没有意义——所以这是"能用"的底线而非可选项。(2) 体积:英文只需 ASCII 的 95 个字符,而常用中文按 GB2312 一级字表就约 3755 字,全字库通常数 MB;粗体/斜体再翻倍,嵌入式 Flash 扛不住,所以 README 明确"默认未启用粗体/斜体中文字库",只做单字重兜底。(3) 渲染链路:中文字形无法像英文字母那样简单拼宽,需要 FreeType 按字形描边,再叠加动态字体加载(TF 卡 /fonts)来缓解内置体积。结论:中文字体不是一个"加个功能",而是把字体体积、存储、渲染性能三者重新做平衡的系统工程——这正是移植中"业务逻辑之外的平台难题"的典型代表。