跳转至

LuatOS 字体使用说明

作者:江访 | 最后修改:2026-06-11

一、概述

本文档面向需要在 LuatOS UI相关核心库中使用文字显示的客户,涵盖以下内容:

  • 字体类型:固件内置 ttf 矢量字体、固件内置灰度点阵字体、固件内置单色点阵字体、文件系统中的 ttf 文件、文件系统中的 bin 点阵字体文件
  • 使用场景:airui 核心库、lcd 核心库、eink 核心库、u8g2 核心库
  • 字体制作:ttf 精简提取、bin 点阵字体生成

注意事项

  1. 固件内置 ttf 数据:大部分支持 airui 的模块固件内置了 MiSans-Demibold 字体的 ttf 数据,涵盖 GB2312 一级和二级汉字,直接在 airui 或 hzfont 中选用即可。
  2. 固件内置灰度点阵数据:部分没有内置 ttf 的固件(如 Air8000 15/115 16/116号固件)内置了16号灰度单色点阵数据,供 airui 直接使用。
  3. 12 号单色点阵中文字体:lcd、eink、u8g2 核心库的固件内仅内置了 12 号单色点阵中文字体,更大字号的中文点阵字体均未内置。如需大号中文显示,建议使用 hzfont 矢量字体。
  4. bin 点阵字体文件:仅 lcd 核心库支持通过 lcd.setFontFile() 加载使用。
  5. ttf 字体文件:可烧录到文件系统或外置存储,各核心库加载方式不同。
  6. hzfont 矢量字体初始化差异
  7. airui — 使用 airui.font_load({type="hzfont", ...})不需要调用 hzfont.init()
  8. lcd — 需要先调用 hzfont.init(),再调用 lcd.drawHzfontUtf8()
  9. eink — 直接调用 eink.drawHzfont()不需要先执行 hzfont.init();仅支持固件内置 ttf
  10. u8g2 — 调用 u8g2.SetHzFont() 配置,之后使用 u8g2.DrawUTF8() 渲染,不需要先执行 hzfont.init()

二、LuatOS UI功能的核心库和字体关系

2.1 字体关系图

2.2 名称解释

类型名称说明
核心库airui核心库airui 是基于 LVGL 9.4 版本进行图形层封装的 LuatOS 核心库,把常用组件、事件管理、输入和基础视觉主题封装为更易上手的 Lua 接口,便于在支持 LuatOS 的设备和 PC 上统一开发。
lcd核心库LCD 核心库是一个功能丰富的显示屏控制接口,支持多种接口类型的 LCD 屏幕,包括 SPI、QSPI、RGB 等。该模块提供了显示屏初始化、图形绘制、文本显示、图像处理、屏幕休眠、唤醒等功能。 LCD 核心库适合简单图形显示和绘制,不适合较复杂UI的设计和开发。
u8g2核心库u8g2 图形处理库是 LuatOS 的显示屏驱动库,支持多种 OLED 和 LCD 单色屏幕,提供丰富的图形绘制功能。
eink核心库eink墨水屏操作库是LuatOS的电子墨水屏驱动库,支持微雪电子多种尺寸和型号的黑白电子墨水屏。
字体分类灰度点阵字库固件内固定字号大小灰度显示字体点阵数据
矢量字库支持12-255号字体大小,灰度显示的字体数据,分为固件内置和自定义2种
单色点阵字库由单色点阵构成的固定大小的字体点阵数据,分为固件内置和自定义2种
字体固件内置的ttf数据大部分支持airui的模块固件内置了ttf数据,使用的MiSans-Demibold字体,初始化后可灰度显示12-255号 GB2312 一级和二级汉字和英文
固件内置airui 16号灰度字体点阵数据固件内无法完整放下内置的ttf数据时,选择了内置一个固定16号字体大小的灰度字体点阵数据
文件系统中的ttf文件固件内未内置,可以自定义生成并烧录到模块文件系统、外置sd/tf卡、外置nand_flash、外置nor_flash中的.ttf文件
固件内置单色英文字体点阵数据固件内置支持以点阵显示12、16、18、20、22、24、32号英文字母及英文符号的数据
固件内置单色英文+12号中文字体点阵数据固件内置支持以12、16、18、20、22、24、32号点阵显示英文字母及英文符号,加上以12号点阵显示中文的数据
文件系统中的bin文件自定义生成的单色点阵字体数据

2.3 各核心库字体支持速查

使用内置字体速查

核心库 固件内置 ttf 矢量字体 固件内置16号灰度点阵 固件内置单色点阵英文字体 固件内置 12 号单色点阵中文字体
airui
lcd
eink
u8g2

加载外部 ttf 能力速查

核心库 加载外部 ttf 加载接口 使用接口
airui airui.font_load({type="hzfont", path="/xxx.ttf"}) 组件自动使用
lcd hzfont.init("/xxx.ttf") lcd.drawHzfontUtf8()
eink —(仅支持内置 ttf) eink.drawHzfont()
u8g2 u8g2.SetHzFont("/xxx.ttf") u8g2.DrawUTF8()

三、LuatOS 固件与字体的关系

3.1 airui 固件与字体关系

支持 airui 的固件 内置 ttf 数据 内置灰度点阵 文件系统 ttf 推荐使用方式
大部分支持 airui 的固件 可选 5.1.1 使用固件内置 ttf 数据
Air8000 15/115、16/116 号固件 可选 5.1.2 仅使用固件内置灰度点阵数据(纯灰度点阵)
5.1.3 使用内置灰度点阵 + 文件系统 ttf 文件(叠加外置 ttf)
Air8101 必选 5.1.4 仅使用文件系统中的 ttf 文件

3.2 lcd 固件与字体关系

支持 lcd 的固件 内置单色点阵英文字体 内置 12 号单色点阵中文字体 hzfont 支持 bin 字体支持
Air780EHM/EGH/EHV/Air8000 等 √(14/114 号固件,见 5.2.3 使用 hzfont 矢量字体 √(见 5.2.2 使用 bin 字体文件
Air780EPM √(见 5.2.2 使用 bin 字体文件
Air8101 √(102/104 号固件,见 5.2.3 使用 hzfont 矢量字体 √(见 5.2.2 使用 bin 字体文件

注意:hzfont 支持需对应固件版本,bin 字体文件仅 lcd 核心库支持。

3.3 eink 固件与字体关系

支持 eink 的固件 内置单色点阵英文字体 内置 12 号单色点阵中文字体 hzfont 支持
Air780EHM/EHV/EGH/Air8000 等 √(仅内置 ttf,见 5.4.2 使用 hzfont 矢量字体

注意:eink 的 hzfont 仅支持固件内置 ttf 数据,不支持加载外部 ttf 文件。

3.4 u8g2 固件与字体关系

支持 u8g2 的固件 内置单色点阵英文字体 内置 12 号单色点阵中文字体 hzfont 支持
Air780EHM/EHV/EGH/Air8000/Air8101 等 √(支持内置/外置 ttf,见 5.3.2 使用 hzfont 矢量字体

注意:u8g2 的 hzfont 支持通过 path 参数加载外部 ttf 文件。


四、非固件内置字体的制作

4.1 ttf 字体文件的制作

4.1.1 选择 ttf 字体类型

选择你获得授权使用的 ttf 格式文件,比如:宋体、黑体,假设选择的是小米字体

4.1.2 从字体中提取所需字符

使用 LuatOS_Font_Tools.exe 工具从字体中提取所需字符,生成精简后的 ttf 文件。

  1. 导入你的.ttf文件

  1. 选择需要的字符集 可快捷选择字符集、手动选择字符集、输入自定义内容,比如:勾选一二级中文+英语+法语

  1. 将选择的字符集从当前.ttf文件中提取出来生成精简后的 ttf 文件。

4.2 bin 字体文件的制作和烧录

bin 点阵字体文件使用 u8g2_font_tool 工具生成,仅 lcd 核心库支持通过 lcd.setFontFile() 加载使用。

4.2.1 注意事项

  • 字体和软件需要放到非中文目录下
  • 一定要能显示完整的字体名才能进行转换
  • 字体命名长度不能超过 23 个字符,例如 dinglie_16_chinese.bin

4.2.2 选择字体类型

选择你获得授权使用的 ttf 或 otf 格式的字体文件,假设选择的是小米字体

4.2.3 从字体中提取所需字符

  1. 下载并打开 u8g2_font_tool 字体生成工具

  2. 导入你的 .ttf 文件并选择映射表

  3. 选择字体
  4. 选择映射表:自定义(手动输入所需字符)或 常用中文/全部中文(完整字库)
  5. 设置字体大小和间距
  6. 点击字体转换,生成 .bin 文件

4.3 字体文件的烧录

ttf 和 bin 字体文件的烧录方式一致,均支持以下四种方式。

4.3.1 烧录方式一:烧录到模块文件系统

将字体文件单独放入一个空文件夹中,烧录固件时选择对应的文件夹,然后选择下载底层和脚本进行烧录。

使用方式:5.1 airui 核心库5.2 lcd 核心库5.3 u8g2 核心库

4.3.2 烧录方式二:烧录到模块脚本空间

将字体文件加载到脚本和资源列表,随代码一起烧录。

使用方式:5.1 airui 核心库5.2 lcd 核心库5.3 u8g2 核心库

4.3.3 烧录方式三:使用 sd/tf 卡中的字体文件

  1. 将生成的字体文件复制到 sd/tf 卡的根目录。
  2. 参考 FAT32 文件系统挂载 sd/tf 卡。
  3. /sd/MyFont.ttf 作为对应接口的 path 参数即可使用。
-- 初始化 SPI1 和 TF 卡
local spi_id, pin_cs = 0, 8
gpio.setup(pin_cs, 1)

spi_sd_device = spi.deviceSetup(spi_id, pin_cs, 0, 0, 8, 24000000)

-- 挂载 TF 卡
local max_retries = 3
local retry_count = 0
local mount_success = false

while retry_count < max_retries and not mount_success do
    result, err = fatfs.mount(fatfs.SPI, "/sd", spi_sd_device)
    if result then
        mount_success = true
        log.info("SD", "挂载成功")
    else
        retry_count = retry_count + 1
        sys.wait(500)
    end
end

使用方式:5.1 airui 核心库5.2 lcd 核心库5.3 u8g2 核心库

4.3.4 烧录方式四:使用外置 nand_flash、nor_flash 中的字体文件

  1. 参考 SPI Flash 通用驱动挂载 flash。
local spi_id, pin_cs, speed = 0, 8, 24000000

little_flash_spi_device = spi.deviceSetup(spi_id, pin_cs, 0, 0, 8, speed)
if little_flash_spi_device then
    little_flash_device = lf.init(little_flash_spi_device)
    if little_flash_device then
        local ok = lf.mount(little_flash_device, "/little_flash")
        if not ok then
            ok = lf.mount(little_flash_device, "/little_flash")
        end
    end
end
  1. 通过 HTTP 下载字体到 flash,或从模块文件系统复制到外挂 flash。
-- 方式一:通过 HTTP 下载字体文件到外挂 flash
http.request("GET", "https://example.com/path/MyFont.ttf", nil, nil,
    { dst = "/little_flash/MyFont.ttf" },
    function(result, status, header, body)
        if result then
            log.info("flash", "字体下载成功")
        end
    end
)

-- 方式二:从模块文件系统复制字体到外挂 flash
local ttf_data = io.readFile("/MyFont.ttf")
if ttf_data then
    io.writeFile("/little_flash/MyFont.ttf", ttf_data)
end
  1. 使用方式:5.1 airui 核心库5.2 lcd 核心库5.3 u8g2 核心库

五、字体文件的使用

5.1 airui 核心库

5.1.1 使用固件内置 ttf 数据

airui.font_load({
    type = "hzfont",
    path = nil,         -- nil 表示使用固件内置的 TTF 数据
    size = 20,
    cache_size = 1048,
    antialias = 1,
})

airui.label({
    text = "中文显示测试",
    x = 10, y = 50, w = 100, h = 200,
})

5.1.2 仅使用固件内置灰度点阵数据

使用固件内置的灰度字体点阵数据时,不需要调用 airui.font_load() 接口。

airui.label({
    text = "中文显示测试",
    x = 10, y = 50, w = 100, h = 200,
})

5.1.3 使用内置灰度点阵 + 文件系统 ttf 文件

-- 使用内置灰度点阵(无需 font_load,组件直接使用)
airui.label({
    text = "中文显示测试",
    x = 10, y = 50, w = 100, h = 200,
})

-- 叠加加载外部 ttf 文件,支持更大字号和抗锯齿
airui.font_load({
    type = "hzfont",
    path = "/luadb/MyFont.ttf",  -- ttf 文件随代码一起烧录时
    size = 16,
    cache_size = 256,
    antialias = 1,
    global = false
})

airui.label({ text = "你好LuatOS123", x = 10, y = 100, w = 300, h = 200, font_size = 36, font = "hzfont"})

5.1.4 仅使用文件系统中的 ttf 文件

airui.font_load({
    type = "hzfont",
    path = "/MyFont.ttf",        -- ttf 文件烧录到内置文件系统时
    size = 16,
    cache_size = 256,
    antialias = 1,
    load_to_psram = true,
})

airui.label({ text = "你好LuatOS123", x = 10, y = 100, w = 300, h = 200, font_size = 36, font = "hzfont"})

5.2 lcd 核心库

5.2.1 使用固件内置单色点阵字体

支持的字体常量:

  • 英文字体:lcd.font_opposansm12 / lcd.font_opposansm16 / lcd.font_opposansm18 / lcd.font_opposansm20 / lcd.font_opposansm22 / lcd.font_opposansm24 / lcd.font_opposansm32
  • 12号中文字体:lcd.font_opposansm12_chinese(固件内仅内置此号)
  • 天气图标字体:lcd.font_open_iconic_weather_6x_t
  • 符号字体:lcd.font_unifont_t_symbols
lcd.init("st7796", { port = lcd.HWID_0, pin_pwr = 7, pin_rst = 19, direction = 0, w = 320, h = 480 })
lcd.setColor(0xFFFF, 0x0000)

-- 英文字体
lcd.setFont(lcd.font_opposansm12); lcd.drawStr(20, 30, "Hello 12")
lcd.setFont(lcd.font_opposansm24); lcd.drawStr(20, 60, "Hello 24")

-- 12号中文字体
lcd.setFont(lcd.font_opposansm12_chinese); lcd.drawStr(20, 130, "12号中文显示测试")

lcd.flush()

5.2.2 使用 bin 字体文件

lcd.init("st7796", { port = lcd.HWID_0, pin_pwr = 7, pin_rst = 19, direction = 0, w = 320, h = 480 })

-- 烧录到脚本分区时用 /luadb/,烧录到文件系统时用 /
local result = lcd.setFontFile("/luadb/dinglie_16_chinese.bin")

lcd.setColor(0xFFFF, 0x0000)
lcd.setFont(lcd.font_opposansm12_chinese)
lcd.drawStr(20, 30, "自定义点阵字体显示测试")
lcd.flush()

5.2.3 使用 hzfont 矢量字体

lcd 核心库需要先调用 hzfont.init(),再使用 lcd.drawHzfontUtf8()

lcd.init("st7796", { port = lcd.HWID_0, pin_pwr = 7, pin_rst = 19, direction = 0, w = 320, h = 480 })
lcd.setupBuff(nil, true)
lcd.autoFlush(false)

-- 初始化 hzfont(无参数使用固件内置字库)
hzfont.init()
-- 或加载外部 ttf:hzfont.init("/luadb/MyFont.ttf")

lcd.setColor(0x0000, 0xFFFF)
lcd.drawHzfontUtf8(10, 30, "合宙LuatOS 20号", 20, 0xF800, 1)
lcd.drawHzfontUtf8(10, 80, "合宙LuatOS 40号", 40, 0x07E0, 1)
lcd.flush()

5.3 u8g2 核心库

5.3.1 使用固件内置单色点阵字体

支持的字体常量:

  • 英文字体:u8g2.font_opposansm10 / u8g2.font_opposansm12 / u8g2.font_opposansm16 / u8g2.font_opposansm18 / u8g2.font_opposansm20 / u8g2.font_opposansm22 / u8g2.font_opposansm24 / u8g2.font_opposansm32
  • 中文字体:u8g2.font_opposansm12_chinese(固件内仅内置此号)
local result = u8g2.begin({
    ic = "ssd1306", direction = 0, mode = "spi_hw_4pin",
    spi_id = 0, spi_res = 20, spi_dc = 21, spi_cs = 24,
}, { width = 128, height = 64 })

u8g2.SetFontMode(1)

-- 英文字体
u8g2.SetFont(u8g2.font_opposansm12); u8g2.DrawUTF8("Hello 12", 0, 16)
u8g2.SetFont(u8g2.font_opposansm24); u8g2.DrawUTF8("Hello 24", 0, 60)

-- 12号中文字体
u8g2.ClearBuffer()
u8g2.SetFont(u8g2.font_opposansm12_chinese); u8g2.DrawUTF8("12号中文测试", 0, 20)

u8g2.SendBuffer()

5.3.2 使用 hzfont 矢量字体

u8g2 核心库通过 u8g2.SetHzFont() 加载 hzfont,不需要先调用 hzfont.init()。支持通过 path 参数加载外部 ttf 文件。

local result = u8g2.begin({
    ic = "ssd1306", direction = 0, mode = "spi_hw_4pin",
    spi_id = 0, spi_res = 20, spi_dc = 21, spi_cs = 24,
}, { width = 128, height = 64 })

-- u8g2.SetHzFont()                     -- 使用固件内置字库
-- u8g2.SetHzFont("/luadb/MyFont.ttf")  -- 从文件系统加载 ttf
u8g2.SetHzFont()

u8g2.DrawUTF8("合宙LuatOS 矢量字体", 0, 30)
u8g2.SendBuffer()

注意: 1. u8g2 核心库不支持加载 .bin 字体文件。 2. 使用 u8g2.SetHzFont() 后,后续的 u8g2.DrawUTF8() 自动使用矢量字体渲染。 3. 如需回到内置点阵字体,可再次调用 u8g2.SetFont(u8g2.font_xxx)


5.4 eink 核心库

5.4.1 使用固件内置单色点阵字体

支持的字体常量:

  • 英文字体:eink.font_opposansm12 / eink.font_opposansm16 / eink.font_opposansm18 / eink.font_opposansm20 / eink.font_opposansm22 / eink.font_opposansm24 / eink.font_opposansm32
  • 中文字体:eink.font_opposansm12_chinese(固件内仅内置此号)
-- eink 初始化前必须先初始化 SPI
local spi_id, pin_cs, pin_dc, pin_busy, pin_reset = 0, 31, 30, 32, 29

spi_eink = spi.deviceSetup(spi_id, pin_cs, 0, 0, 8, 20 * 1000 * 1000, spi.MSB, 1, 1)

eink.init(eink.MODEL_1in54_V2,
    { port = "device", pin_dc = pin_dc, pin_busy = pin_busy, pin_rst = pin_reset }, spi_eink)

eink.setWin(200, 200, 0)
eink.clear(1, true)

-- 英文字体
eink.setFont(eink.font_opposansm22); eink.print(35, 34, "LuatOS-eink", 0)
eink.setFont(eink.font_opposansm12); eink.print(20, 60, "Hello 12", 0)

-- 12号中文字体
eink.setFont(eink.font_opposansm12_chinese); eink.print(20, 120, "12号中文显示测试", 0)

eink.show(0, 0, true)

5.4.2 使用 hzfont 矢量字体

eink 核心库通过 eink.drawHzfont() 直接渲染 hzfont 矢量字体,不需要先调用 hzfont.init()。注意该接口仅支持固件内置 ttf,不支持加载外部 ttf 文件。

-- eink 初始化前必须先初始化 SPI
local spi_id, pin_cs, pin_dc, pin_busy, pin_reset = 0, 31, 30, 32, 29

spi_eink = spi.deviceSetup(spi_id, pin_cs, 0, 0, 8, 20 * 1000 * 1000, spi.MSB, 1, 1)

eink.init(eink.MODEL_1in54_V2,
    { port = "device", pin_dc = pin_dc, pin_busy = pin_busy, pin_rst = pin_reset }, spi_eink)

eink.setWin(200, 200, 0)
eink.clear(1, true)

-- 直接使用 eink.drawHzfont(无需 hzfont.init())
eink.drawHzfont(20, 40, "合宙LuatOS 16号", 16, 1)
eink.drawHzfont(20, 80, "合宙LuatOS 24号", 24, 1)
eink.drawHzfont(20, 130, "合宙LuatOS 36号", 36, 1)

eink.show(0, 0, true)

注意: 1. eink 核心库不支持加载 .bin 字体文件。 2. eink.drawHzfont() 直接调用即可,不要额外执行 hzfont.init()。 3. eink.drawHzfont() 仅支持固件内置 ttf,不支持外部 ttf。