EUBF管理器通过提供方便的字体请求接口来链接上层 LCD 等渲染程序和下层存储介质(USB 或 SD)。
除此之外,EUBF 管理器还对于每个快速槽位维护一个最近最少使用(LRU)缓存,进一步减少读取文件产生的 IO 开销。
不过,由于缓存机制可能会很大,所以缓存池开辟在外载的 SDRAM 中。
为了更好的展示缓存区配置,将 sdram 的接口一并展示:
#ifndef __SDRAM_W9825G6KH_H
#define __SDRAM_W9825G6KH_H
#include "stm32f4xx_hal.h"
/* * W9825G6KH 容量为 32MB (256Mbit)
* 原理图片选连接至 FMC_SDNE0,映射到 STM32 的 SDRAM Bank 1
*/
#define SDRAM_BANK_ADDR ((uint32_t)0xC0000000)
#define SDRAM_SIZE ((uint32_t)0x2000000) // 32MB
/* FMC 命令超时时间 */
#define SDRAM_TIMEOUT ((uint32_t)0xFFFF)
/* W9825G6KH 模式寄存器配置定义 */
#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
/* 外部引入 CubeMX 生成的 SDRAM 句柄 */
extern SDRAM_HandleTypeDef hsdram1;
/* ====================================== 其它外设的配置统一在这里管理 =========================================*/
/* ------------------------------------------------------------------------------------------
* [显存内存映射配置]
* 依赖于 sdram.h 中的 SDRAM_BANK_ADDR (如 0xC0000000)
* ------------------------------------------------------------------------------------------*/
/* 定义全屏显存首地址 (默认存放在 SDRAM 的起始位置) */
#define LCD_FRAME_BUFFER SDRAM_BANK_ADDR
#define LCD_FRAME_BUFFER_SIZE (320 * 480 * 2) // 320x480 分辨率,RGB565 格式每像素 2 字节,共 307200 字节
/* 定义字库/图片缓存首地址 (跳过全屏显存的大小) */
#define FONT_CACHE_BUFFER (SDRAM_BANK_ADDR + LCD_FRAME_BUFFER_SIZE)
#define FONT_CACHE_BUFFER_SIZE (320 * 480 * 2)
/* * EUBF 字库 LRU 缓存区域 - 分配 6MB
* 紧接着显存区域之后。供我们定义的 10 个超级槽位使用。
*/
#define EUBF_CACHE_BASE_ADDR (FONT_CACHE_BUFFER + FONT_CACHE_BUFFER_SIZE)
#define EUBF_CACHE_SIZE (6 * 1024 * 1024)
/* 函数声明 */
void SDRAM_InitSequence(void);
void SDRAM_WriteBuffer8(uint32_t addr, uint8_t *data, uint32_t len);
void SDRAM_ReadBuffer8(uint32_t addr, uint8_t *data, uint32_t len);
void SDRAM_WriteBuffer16(uint32_t addr, uint16_t *data, uint32_t len);
void SDRAM_ReadBuffer16(uint32_t addr, uint16_t *data, uint32_t len);
void SDRAM_WriteBuffer32(uint32_t addr, uint32_t *data, uint32_t len);
void SDRAM_ReadBuffer32(uint32_t addr, uint32_t *data, uint32_t len);
uint8_t SDRAM_Test(void);
#endif /* __SDRAM_W9825G6KH_H *//**
******************************************************************************
* @file eubf_manager.h
* @brief EUBF 字库管理器 (无状态架构版)
* @author 文止墨
* @date 2026-3-16
* @note https://github.com/S-R-Afraid/Embedded-Unicode-Bitmap-Font-Format
******************************************************************************
*/
#ifndef __EUBF_MANAGER_H
#define __EUBF_MANAGER_H
#include <stdint.h>
//#include "ff.h"
#include "../../ThirdParty/MyLib/SDRAM/sdram.h"
#define EUBF_MAX_SLOTS 10
#define EUBF_MAX_CACHE_NODES 256
#define EUBF_MAX_BITMAP_SIZE 2048
#define EUBF_OK 0
#define EUBF_ERR_NO_FILE -1
#define EUBF_ERR_FORMAT -2
#pragma pack(push, 1)
/**
* @brief 单个字符的 LRU 缓存节点 (存放在 SDRAM)
*/
typedef struct {
uint16_t unicode;
uint32_t last_used_tick;
uint8_t box_w;
uint8_t box_h;
int8_t x_offset;
int8_t y_offset;
uint16_t advance;
uint8_t bitmap_data[EUBF_MAX_BITMAP_SIZE];
} EUBF_GlyphCache;
/**
* @brief 单个字库槽位管理结构 (存放在 SDRAM)
*/
typedef struct {
uint8_t is_occupied;
char font_name[32];
uint8_t font_size;
uint16_t line_height;
uint16_t bpp;
uint16_t missing_glyph;
uint32_t page_dir_offset;
uint32_t page_table_offset;
uint32_t glyph_offset_offset;
uint32_t glyph_data_offset;
EUBF_GlyphCache cache_pool[EUBF_MAX_CACHE_NODES];
} EUBF_Slot;
#pragma pack(pop)
/* 指向 SDRAM 的基地址指针 */
#define g_eubf_slots ((EUBF_Slot *)EUBF_CACHE_BASE_ADDR)
/* ========================================================================= *
* 底层 IO 移植接口定义 (由用户实现并注册)
* ========================================================================= */
typedef struct {
/** * @brief 打开文件接口
* @return 返回文件描述符(fd)或者槽位号(>=0),失败返回 -1
*/
int8_t (*Open)(const char *path);
/** * @brief 关闭文件接口
*/
void (*Close)(int8_t fd);
/** * @brief 绝对偏移量读取接口 (对齐你的 FastSlot_Read 逻辑)
* @return 0: 成功,其他: 失败
*/
uint8_t (*ReadAt)(int8_t fd, uint32_t offset, uint8_t *buf, uint32_t len);
/**
* @brief 根目录路径 (例如 "0:/" 代表 SD卡,"1:/" 代表 U盘)
*/
const char *RootPath;
} EUBF_Port_Config_t;
/* ========================================================================= *
* 核心对外 API
* ========================================================================= */
/**
* @brief 初始化管理器并注册底层 IO 接口
* @param config 用户配置的底层 IO 函数指针结构体
*/
void EUBF_Init(EUBF_Port_Config_t *config);
/**
* @brief 直接获取字形信息 (底层自动完成加载、切换、LRU 淘汰与 U 盘读取)
*/
const EUBF_GlyphCache* EUBF_Get_Glyph(const char *font_name, uint8_t size, uint16_t unicode);
/**
* @brief 获取指定字库的行高
*/
uint16_t EUBF_Get_Line_Height(const char *font_name, uint8_t size);
/**
* @brief 获取字符串在指定字库下的总渲染宽度 (像素)
*/
uint16_t EUBF_Get_Text_Width(const char *font_name, uint8_t size, const char *str);
/**
* @brief U 盘断开时的紧急清理与安全切断
*/
void EUBF_Notify_Disconnected(void);
#endif /* __EUBF_MANAGER_H *//**
******************************************************************************
* @file eubf_manager.c
* @brief EUBF 字库管理器 (无状态架构 & 工业级硬件容错版)
******************************************************************************
*/
#include "eubf_manager.h"
#include <string.h>
#include <stdio.h>
/* 外部系统资源 */
extern uint32_t HAL_GetTick(void);
/* ========================================================================= *
* 内部函数声明
* ========================================================================= */
static uint32_t read_u32_le_safe(int8_t fd, uint32_t offset);
static uint16_t read_u16_le_safe(int8_t fd, uint32_t offset);
static const char* Translate_Font_Alias(const char *request_name);
static int8_t Find_Available_Slot(void);
static uint8_t UTF8_To_Unicode(const char *utf8_str, uint16_t *unicode);
static int8_t EUBF_Read_Glyph_From_Disk(EUBF_Slot *slot, uint16_t unicode, EUBF_GlyphCache *target_node);
static int8_t EUBF_Get_Or_Load_Slot(const char *font_name, uint8_t size);
/* ========================================================================= *
* 内部资源
* ========================================================================= */
static EUBF_Port_Config_t s_io_port; // 保存用户传入的底层接口
static int8_t s_file_fds[EUBF_MAX_SLOTS]; // 存储底层返回的 fd (如 FastSlot 的槽位号)
static uint8_t s_meta_temp_buf[16] __attribute__((aligned(4)));
static uint32_t s_slot_last_used_tick[EUBF_MAX_SLOTS] = {0};
static int8_t s_last_matched_slot = -1;
/* ========================================================================= *
* EUBF Header
* ========================================================================= */
#pragma pack(push,1)
typedef struct {
char magic[4];
uint16_t version;
char font_name[32];
uint16_t font_size;
uint16_t ascent;
uint16_t descent;
uint16_t line_height;
uint16_t max_advance;
uint8_t bpp;
uint8_t padding1;
uint16_t max_width;
uint16_t max_height;
uint32_t glyph_count;
uint16_t missing_glyph;
uint16_t padding2;
uint32_t page_dir_offset;
uint32_t page_table_offset;
uint32_t glyph_offset_offset;
uint32_t glyph_data_offset;
char source_ttf[64];
uint32_t ttf_crc;
} EUBF_RawHeader;
#pragma pack(pop)
/* ========================================================================= *
* 字体别名
* ========================================================================= */
typedef struct {
const char *alias;
const char *real_dir;
} EUBF_AliasMap;
static const EUBF_AliasMap g_font_aliases[] = {
{"华康金文体", "DFJinWenW3-GB"},
{"白路彤彤手写体", "bailutongtongshouxieti"},
{"字酷堂板桥体", "zktbqt"},
{"峄山碑篆体", "ysbzt"},
{"余繁新语","yufanxinyu"}
};
#define ALIAS_TABLE_SIZE (sizeof(g_font_aliases) / sizeof(g_font_aliases[0]))
/* ========================================================================= *
* 初始化 (依赖注入)
* ========================================================================= */
void EUBF_Init(EUBF_Port_Config_t *config)
{
if (config != NULL) {
s_io_port = *config; // 拷贝用户配置
}
for (int i = 0; i < EUBF_MAX_SLOTS; i++)
{
g_eubf_slots[i].is_occupied = 0;
s_slot_last_used_tick[i] = 0;
s_file_fds[i] = -1;
}
s_last_matched_slot = -1;
}
/* ========================================================================= *
* 安全读取辅助函数 (改为 Offset-based 偏移量读取)
* ========================================================================= */
static uint32_t read_u32_le_safe(int8_t fd, uint32_t offset)
{
if (s_io_port.ReadAt(fd, offset, s_meta_temp_buf, 4) == 0) { // 0表示成功
return (uint32_t)(s_meta_temp_buf[0] | (s_meta_temp_buf[1] << 8) |
(s_meta_temp_buf[2] << 16) | (s_meta_temp_buf[3] << 24));
}
return 0xFFFFFFFF;
}
static uint16_t read_u16_le_safe(int8_t fd, uint32_t offset)
{
if (s_io_port.ReadAt(fd, offset, s_meta_temp_buf, 2) == 0) {
return (uint16_t)(s_meta_temp_buf[0] | (s_meta_temp_buf[1] << 8));
}
return 0xFFFF;
}
/* ========================================================================= *
* Slot 自动调度
* ========================================================================= */
static int8_t EUBF_Get_Or_Load_Slot(const char *font_name, uint8_t size)
{
if (font_name == NULL) return -1;
const char *actual_dir = Translate_Font_Alias(font_name);
/* 快速路径 */
if (s_last_matched_slot >= 0 &&
g_eubf_slots[s_last_matched_slot].is_occupied)
{
if (g_eubf_slots[s_last_matched_slot].font_size == size &&
strcmp(g_eubf_slots[s_last_matched_slot].font_name, actual_dir) == 0)
{
s_slot_last_used_tick[s_last_matched_slot] = HAL_GetTick();
return s_last_matched_slot;
}
}
/* 查找已加载字体 */
for (int8_t i = 0; i < EUBF_MAX_SLOTS; i++)
{
if (g_eubf_slots[i].is_occupied &&
g_eubf_slots[i].font_size == size &&
strcmp(g_eubf_slots[i].font_name, actual_dir) == 0)
{
s_slot_last_used_tick[i] = HAL_GetTick();
s_last_matched_slot = i;
return i;
}
}
/* 查找可用 slot */
int8_t slot_idx = Find_Available_Slot();
EUBF_Slot *slot = &g_eubf_slots[slot_idx];
if (slot->is_occupied && s_file_fds[slot_idx] >= 0)
{
s_io_port.Close(s_file_fds[slot_idx]); // 使用用户接口关闭旧文件
s_file_fds[slot_idx] = -1;
slot->is_occupied = 0;
}
// 拼接路径,使用动态注册的 RootPath
static char path_buf[128];
snprintf(path_buf, sizeof(path_buf), "%sasset/font/%s/%d.eubf",
s_io_port.RootPath, actual_dir, size);
// 调用底层 Open 接口 (比如 USB_FastSlot_Open_UTF8)
int8_t fd = s_io_port.Open(path_buf);
if (fd < 0) return -1;
s_file_fds[slot_idx] = fd; // 保存底层返回的句柄/槽位号
// 读取 Header (偏移量 0)
EUBF_RawHeader head;
if (s_io_port.ReadAt(fd, 0, (uint8_t*)&head, sizeof(EUBF_RawHeader)) != 0 ||
memcmp(head.magic, "EUBF", 4) != 0)
{
s_io_port.Close(fd);
s_file_fds[slot_idx] = -1;
return -1;
}
strcpy(slot->font_name, actual_dir);
slot->font_size = size;
slot->line_height = head.line_height;
slot->bpp = head.bpp;
slot->missing_glyph = head.missing_glyph;
slot->page_dir_offset = head.page_dir_offset;
slot->glyph_offset_offset = head.glyph_offset_offset;
slot->glyph_data_offset = head.glyph_data_offset;
for (uint16_t c = 0; c < EUBF_MAX_CACHE_NODES; c++)
{
slot->cache_pool[c].unicode = 0xFFFF;
slot->cache_pool[c].last_used_tick = 0;
}
slot->is_occupied = 1;
s_slot_last_used_tick[slot_idx] = HAL_GetTick();
s_last_matched_slot = slot_idx;
return slot_idx;
}
/* ========================================================================= *
* 获取 Glyph
* ========================================================================= */
const EUBF_GlyphCache*
EUBF_Get_Glyph(const char *font_name,
uint8_t size,
uint16_t unicode)
{
int8_t slot_idx = EUBF_Get_Or_Load_Slot(font_name, size);
if (slot_idx < 0)
return NULL;
EUBF_Slot *slot = &g_eubf_slots[slot_idx];
int16_t target_index = -1;
int16_t oldest_index = 0;
uint32_t oldest_tick = 0xFFFFFFFF;
uint32_t current_tick = HAL_GetTick();
for (int i = 0; i < EUBF_MAX_CACHE_NODES; i++)
{
if (slot->cache_pool[i].unicode == unicode)
{
slot->cache_pool[i].last_used_tick = current_tick;
return &slot->cache_pool[i];
}
if (slot->cache_pool[i].unicode == 0xFFFF)
{
target_index = i;
}
else if (target_index == -1 &&
slot->cache_pool[i].last_used_tick < oldest_tick)
{
oldest_tick = slot->cache_pool[i].last_used_tick;
oldest_index = i;
}
}
if (target_index == -1)
target_index = oldest_index;
EUBF_GlyphCache *node = &slot->cache_pool[target_index];
if (EUBF_Read_Glyph_From_Disk(slot, unicode, node) == 0)
{
node->unicode = unicode;
node->last_used_tick = current_tick;
return node;
}
return NULL;
}
/* ========================================================================= *
* 获取行高
* ========================================================================= */
uint16_t EUBF_Get_Line_Height(const char *font_name, uint8_t size)
{
int8_t slot_idx = EUBF_Get_Or_Load_Slot(font_name, size);
if (slot_idx < 0)
return 0;
return g_eubf_slots[slot_idx].line_height;
}
/* ========================================================================= *
* 计算字符串宽度
* ========================================================================= */
uint16_t EUBF_Get_Text_Width(const char *font_name,
uint8_t size,
const char *str)
{
if (!str)
return 0;
if (EUBF_Get_Or_Load_Slot(font_name, size) < 0)
return 0;
uint16_t total = 0;
const char *p = str;
while (*p)
{
uint16_t code;
uint8_t len = UTF8_To_Unicode(p, &code);
if (len == 0)
break;
const EUBF_GlyphCache *glyph =
EUBF_Get_Glyph(font_name, size, code);
if (glyph)
total += glyph->advance;
p += len;
}
return total;
}
/* ========================================================================= *
* USB 断开
* ========================================================================= */
void EUBF_Notify_Disconnected(void)
{
for (int i = 0; i < EUBF_MAX_SLOTS; i++)
g_eubf_slots[i].is_occupied = 0;
s_last_matched_slot = -1;
}
/* ========================================================================= *
* 从磁盘读取 glyph (全面使用 ReadAt 接口)
* ========================================================================= */
static int8_t EUBF_Read_Glyph_From_Disk(EUBF_Slot *slot, uint16_t unicode, EUBF_GlyphCache *target_node)
{
int8_t s_idx = (slot - g_eubf_slots);
int8_t fd = s_file_fds[s_idx]; // 获取绑定的底层文件槽位号
// 1. 读 Page 偏移
uint32_t page_offset = read_u32_le_safe(fd, slot->page_dir_offset + ((unicode >> 8) * 4));
uint16_t glyph_id = 0xFFFF;
// 2. 读 Glyph ID
if (page_offset != 0xFFFFFFFF) {
glyph_id = read_u16_le_safe(fd, page_offset + ((unicode & 0xFF) * 2));
}
if (page_offset == 0xFFFFFFFF || glyph_id == 0xFFFF) {
glyph_id = slot->missing_glyph;
}
// 3. 读数据偏移
uint32_t rel_addr = read_u32_le_safe(fd, slot->glyph_offset_offset + (glyph_id * 4));
// 4. 读 Glyph 头部信息 (8个字节)
if (s_io_port.ReadAt(fd, slot->glyph_data_offset + rel_addr, s_meta_temp_buf, 8) != 0) {
return -1;
}
target_node->box_w = s_meta_temp_buf[0];
target_node->box_h = s_meta_temp_buf[1];
target_node->x_offset = (int8_t)s_meta_temp_buf[2];
target_node->y_offset = (int8_t)s_meta_temp_buf[3];
target_node->advance = (uint16_t)(s_meta_temp_buf[4] | (s_meta_temp_buf[5] << 8));
if (target_node->box_h == 0 && target_node->advance == 0) return -1;
// 5. 读点阵数据
if (target_node->box_h > 0 && target_node->box_w > 0)
{
uint32_t b_size = ((target_node->box_w * slot->bpp + 7) / 8) * target_node->box_h;
if (b_size > 0 && b_size <= EUBF_MAX_BITMAP_SIZE)
{
// 继续从上面读完8字节的偏移量往后读数据
if (s_io_port.ReadAt(fd, slot->glyph_data_offset + rel_addr + 8, target_node->bitmap_data, b_size) != 0) {
return -1;
}
}
}
return 0;
}
/* ========================================================================= *
* 字体别名转换
* ========================================================================= */
static const char*
Translate_Font_Alias(const char *request_name)
{
for (int i = 0; i < ALIAS_TABLE_SIZE; i++)
{
if (strcmp(request_name, g_font_aliases[i].alias) == 0)
return g_font_aliases[i].real_dir;
}
return request_name;
}
/* ========================================================================= *
* Slot LRU
* ========================================================================= */
static int8_t Find_Available_Slot(void)
{
int8_t oldest_slot = 0;
uint32_t oldest_tick = 0xFFFFFFFF;
for (int8_t i = 0; i < EUBF_MAX_SLOTS; i++)
{
if (g_eubf_slots[i].is_occupied == 0)
return i;
if (s_slot_last_used_tick[i] < oldest_tick)
{
oldest_tick = s_slot_last_used_tick[i];
oldest_slot = i;
}
}
return oldest_slot;
}
/* ========================================================================= *
* UTF8 → Unicode
* ========================================================================= */
static uint8_t UTF8_To_Unicode(const char *utf8_str,
uint16_t *unicode)
{
uint8_t first = (uint8_t)utf8_str[0];
if (first < 0x80)
{
*unicode = first;
return 1;
}
else if ((first & 0xE0) == 0xC0)
{
*unicode =
((first & 0x1F) << 6) |
(utf8_str[1] & 0x3F);
return 2;
}
else if ((first & 0xF0) == 0xE0)
{
*unicode =
((first & 0x0F) << 12) |
((utf8_str[1] & 0x3F) << 6) |
(utf8_str[2] & 0x3F);
return 3;
}
else if ((first & 0xF8) == 0xF0)
{
/* 4字节UTF8 → 截断为16bit */
*unicode =
((utf8_str[2] & 0x3F) << 6) |
(utf8_str[3] & 0x3F);
return 4;
}
return 0;
}
EUBF 字体管理器用于在嵌入式系统中动态加载和管理 .eubf 字库文件,并为上层显示驱动提供字符字形数据。该管理器负责:
设计目标:
整体结构如下:
应用层 / LCD驱动
│
│ (font_name + size + unicode)
▼
EUBF Font Manager
│
├── Slot Manager
│ ├── 字库加载
│ └── LRU淘汰
│
├── Glyph Cache
│ └── LRU字形缓存
│
├── UTF8解析器
│
└── FATFS 文件读取
│
▼
.eubf 字库文件.eubf 文件由以下部分组成:
┌──────────────┐
│ Header │
├──────────────┤
│ Page Dir │
├──────────────┤
│ Page Tables │
├──────────────┤
│ Glyph Offset │
├──────────────┤
│ Glyph Data │
└──────────────┘typedef struct {
char magic[4]; // "EUBF"
uint16_t version;
char font_name[32];
uint16_t font_size;
uint16_t ascent;
uint16_t descent;
uint16_t line_height;
uint16_t max_advance;
uint8_t bpp;
uint16_t max_width;
uint16_t max_height;
uint32_t glyph_count;
uint16_t missing_glyph;
uint32_t page_dir_offset;
uint32_t page_table_offset;
uint32_t glyph_offset_offset;
uint32_t glyph_data_offset;
char source_ttf[64];
uint32_t ttf_crc;
} EUBF_RawHeader;系统维护多个字体槽位:
EUBF_Slot g_eubf_slots[EUBF_MAX_SLOTS]每个 Slot 表示一个 已加载的字体文件。
EUBF_Slot
│
├─ font_name
├─ font_size
├─ line_height
├─ bpp
│
├─ page_dir_offset
├─ glyph_offset_offset
├─ glyph_data_offset
│
└─ cache_pool[]使用 LRU(Least Recently Used)策略:
如果存在空Slot
使用空Slot
否则
淘汰最久未使用字体时间戳来源:
HAL_GetTick()每个字体 Slot 内部维护:
EUBF_MAX_CACHE_NODES个字形缓存节点。
结构:
typedef struct {
uint16_t unicode;
uint8_t box_w;
uint8_t box_h;
int8_t x_offset;
int8_t y_offset;
uint16_t advance;
uint32_t last_used_tick;
uint8_t bitmap_data[EUBF_MAX_BITMAP_SIZE];
} EUBF_GlyphCache;请求 glyph
│
▼
检查 cache_pool
│
┌────┴────┐
│ │
命中 未命中
│ │
返回 读取磁盘
│ │
更新LRU 写入缓存缓存淘汰策略:
LRU
读取 glyph 的完整流程:
Unicode
│
▼
PageDir 查找
│
▼
PageTable 查找
│
▼
GlyphID
│
▼
GlyphOffset
│
▼
GlyphDatapage = unicode >> 8page_offset = page_dir[page]glyph_id = page_table[unicode & 0xFF]glyph_offset = glyph_offset_table[glyph_id]支持:
| UTF8长度 | Unicode范围 |
| 1字节 | ASCII |
| 2字节 | 扩展拉丁 |
| 3字节 | CJK |
函数:
UTF8_To_Unicode()返回:
返回值 = UTF8长度示例:
UTF8: E4 B8 AD
Unicode: 0x4E2D/* 定义 SD卡 的 EUBF 底层 IO 接口配置 */
EUBF_Port_Config_t font_sd_config = {
.Open = SD_FastSlot_Open_UTF8, // 完美的接口匹配!
.Close = (void (*)(int8_t))SD_FastSlot_Close, // 强转一下避免警告
.ReadAt = SD_FastSlot_Read, // 完美的参数匹配!
.RootPath = "0:/" // SD卡的根目录
};
EUBF_Init(&font_sd_config);/* 定义 U 盘的 EUBF 底层 IO 接口配置 */
EUBF_Port_Config_t font_usb_config = {
.Open = USB_FastSlot_Open_UTF8,
.Close = (void (*)(int8_t))USB_FastSlot_Close, // 强转以消除编译警告
.ReadAt = USB_FastSlot_Read,
.RootPath = "1:/" // 注意:在 FatFs 中,U 盘的根目录通常是 "1:/" 或 "USBHPath"
};
EUBF_Init(&font_usb_config);初始化字体管理器。通过修改初始化结构体,您只需要提供读写接口就可以很方便的适配 USB、SD、或 flash 等存储介质。
const EUBF_GlyphCache*
EUBF_Get_Glyph(
const char *font_name,
uint8_t size,
uint16_t unicode
);输入:
font_name 字体名称
size 字号
unicode 字符编码返回:
GlyphCache 指针
uint16_t EUBF_Get_Line_Height(
const char *font_name,
uint8_t size
);uint16_t EUBF_Get_Text_Width(
const char *font_name,
uint8_t size,
const char *str
);输入:
UTF8字符串
返回:
字符串像素宽度
void EUBF_NotifyDisconnected(void)功能:
清空所有Slot
避免 FATFS 句柄失效。
字体文件路径:
USB:/asset/font/{font}/{size}.eubf示例:
USB:/asset/font/DFJinWenW3-GB/24.eubf为了让上层使用 中文字体名称:
"华康金文体"
系统会自动映射:
DFJinWenW3-GB
别名表:
static const EUBF_AliasMap g_font_aliases[]示例:
static const EUBF_AliasMap g_font_aliases[] = {
{"华康金文体", "DFJinWenW3-GB"},
{"白路彤彤手写体", "bailutongtongshouxieti"},
{"字酷堂板桥体", "zktbqt"},
{"峄山碑篆体", "ysbzt"},
{"余繁新语","yufanxinyu"}
};LCD 驱动只需:
const EUBF_GlyphCache *glyph;
glyph = EUBF_Get_Glyph("华康金文体", 24, unicode);
if (glyph)
{
draw_bitmap(
glyph->bitmap_data,
glyph->box_w,
glyph->box_h
);
}上层 无需管理:
当前版本复杂度:
| 操作 | 复杂度 |
| Slot查找 | O(N) |
| Glyph缓存查找 | O(N) |
| Glyph磁盘读取 | O(1) |
典型性能:
ASCII缓存命中: < 5µs
中文缓存命中: < 5µs
磁盘读取: 0.5 ~ 2ms假设:
EUBF_MAX_SLOTS = 3
EUBF_MAX_CACHE_NODES = 32
EUBF_MAX_BITMAP_SIZE = 256RAM 使用:
Slot结构 ≈ 200B
GlyphCache ≈ 300B × 32
总计/Slot ≈ 9KB
3 Slots ≈ 27KB推荐优化:
减少一次 FAT seek。
性能提升:
约 30%
当前:
O(N)
优化:
O(1)
支持:
Emoji
扩展汉字
RLE / LZ4。
EUBF Font Manager 提供:
特点:
上层零管理
低耦合
可扩展
适合嵌入式GUI系统