恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

嵌入式显示中文字库:HZK/ASC点阵字模读取与寻址详解

  • 首页
  • 资讯中心
  • /
  • 嵌入式显示中文字库:HZK/ASC点阵字模读取与寻址详解

相关资讯

STM32驱动ADNS3080光流传感器实现高精度位移测量与里程计设计 2026/9/2 2:07:10
前端灵动动画实战:用微交互设计提升产品留存的完整方案 2026/9/2 2:02:10
104报文解析工具实战:从规约骨架到高级分析 2026/9/2 2:02:10

最新资讯

arm64 Docker安装全攻略:架构选择、虚拟化报错与容器部署
GDAL源码编译完全指南:从CMake配置到裁剪定制
STM32F407+OV7670实时图像显示实战:DCMI与DMA配置详解
GDAL/OGR编译实战:从源码配置到开箱即用的完整指南
ASIO2WASAPI:通用ASIO驱动层,让老声卡重获低延迟体验
Uber微服务演进:从单体到分布式架构的拆分实践

今日推荐

DeepSeek字幕翻译实战:从API调用到批量SRT转中文的完整方案
用Python搭建搞笑语音助手:从语音识别到语音合成全教程
ROS2阿克曼底盘仿真:从运动学原理到Nav2导航集成实践

本周热门

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析
数字电路时序基石:深入理解建立时间与保持时间
蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

嵌入式显示中文字库:HZK/ASC点阵字模读取与寻址详解

发布时间:2026/9/2 2:07:10
嵌入式显示中文字库:HZK/ASC点阵字模读取与寻址详解 简介一套覆盖HZK12至HZK48、ASC12至ASC48的完整点阵字库资源面向嵌入式、单片机及图形界面开发者解决项目开发中字库不全、格式分散或缺少读取代码的问题。压缩包共55个文件约7.53MB主要包含hzk系列汉字字库、asc系列ASCII字库以及配套的java、c读取代码和可运行exe、class文件txt说明文件可帮助快速理解字库格式与调用方式。资源整理了多尺寸汉字和ASCII点阵数据并提供Char2Mat、Num2Mat等转换示例便于直接移植到点阵屏或液晶显示项目中。当前已有1652人学习下载适合正在制作点阵文字、需要现成字库与读取代码的开发者参考使用。 做嵌入式显示的朋友一定绕不开一个坎中文字怎么画出来。开发板上的屏幕只认像素主控芯片又没有强大的字体引擎真要在LCD或者OLED上显示“你好”两个字最省事的办法就是拿到一套点阵字模一个点一个点地描。HZK12、HZK16、HZK24、HZK32、HZK40、HZK48再配上ASC12、ASC16、ASC24、ASC32、ASC48这批ASCII字库基本就能覆盖从8位单片机到Linux开发板的绝大多数显示需求。这篇文章我就把这些字库文件的结构、寻址方式和读取代码全部拉通讲一遍让中英文混排显示不再靠蒙。1.1 字库家族与命名规律先看名字。HZK是“汉字库”的拼音缩写后面的数字通常代表点阵的高度比如HZK16就是16×16的中文字库HZK48就是48×48的大字库。ASC是ASCII字库后面的数字一般代表点阵高度常见的ASC16实际上是8×16也就是每个英文字符横向8个点、纵向16个点每个字符占16字节。这套命名方式在GUI界面开发、LED广告屏、游戏掌机复刻项目里特别常见。需求也很典型用单片机读取一个中文字符串然后按坐标绘制在屏幕上。没有字库时一个“中”字要自己手工点很久而且换个字号就要重新点一遍。有了这套点阵字库读取代码只需要按公式算出偏移量把固定长度的字节读出来往屏幕缓冲区里一贴字形就出来了。1.2 点阵字模的基本存储思路点阵字模的核心概念是一个字就是一个二维的像素矩阵。比如16×16的字相当于在16行、每行16个格子的纸上有笔画的地方填1空白的地方填0。显示的时候把这一串像素按坐标画到屏幕上。计算机存储时不会真的存“0/1”字符而是把一行的16个点拆成2个字节。每个字节有8位高位对应左边低位对应右边一行2字节一共16行正好32字节。这就是HZK16中每个汉字占32字节的由来。记住“行字节数 × 行数 单字字节数”这个通用公式后面所有HZK规格都能算出来。2. HZK与ASC字库文件结构拆解2.1 HZK字库按区位码排布HZK系列字库使用的是GB2312编码的区位码思路。GB2312把汉字放在94个区、每个区94个位置里也就是最多能放94×948836个字。实际收录的汉字是6763个加上符号标点构成一张区位码表。在HZK16文件中汉字按照“区”和“位”的顺序连续存放。读取时先把要显示的汉字转成两个字节高字节是区码低字节是位码然后利用下面这个公式定位到文件中的偏移位置偏移量 ((区码 - 1) × 94 (位码 - 1)) × 单字字节数以“啊”字为例它的GB2312编码是0xB0 0xA1区码 0xB0 - 0xA0 16位码 0xA1 - 0xA0 1。套公式后它在字库文件中的偏移就是((16 - 1) × 94 0) × 32 45120从文件开头跳过45120字节读32字节就是“啊”的字模。很多老代码里也会写成((qu - 0xA1) * 94 (wei - 0xA1)) * 32其中qu和wei直接用原始字节值。这两种写法本质一样前者先减0xA0得到1~94的区号再减1做数组下标后者直接用字节值减0xA1做下标。我最推荐前面那种逻辑更清楚调试时不容易看错。2.2 不同规格的每个字字节数怎么算HZK24、HZK32、HZK40、HZK48的计算方式完全遵循同一个套路每行字节数等于横向点数除以8再乘以行数。字库规格点阵尺寸每行字节数单字字节数HZK1616×16232HZK2424×24372HZK3232×324128HZK4040×405200HZK4848×486288HZK1212×122按8位向上取整24或32取决于是否按行对齐这里单独把HZK12拎出来说。12不是8的整数倍12 / 8向上取整是2字节也就是说一行12个点要用16个bit位来装最后4位是空着的。如果文件按紧凑方式存12行每行2字节单字就是24字节。如果文件按“行对齐到16位再补满16行”的方式存单字就是32字节。这两种HZK12我都遇到过没有统一标准。所以拿到字库文件后第一件事不是查读取代码而是看文件大小根据文件大小反推单字字节数。还有一个更土的验证办法用文件总字节数除以某个大致汉字数量。如果结果接近32、72、128基本就能确定它的单字大小。比如文件大小是216416除以6763约等于32这通常就是标准的HZK16。如果文件大小是282752除以8836也等于32说明它把94×94个区位全部按顺序占满了读取时用完整区位公式也完全没问题。2.3 ASC字库的排列方式ASC字库的存储逻辑比HZK简单很多。以ASC16为例常见文件里每种字符按ASCII码顺序排每个字符占16字节。如果字库从0x00开始收录那偏移量就是ascii码 × 16如果字库从0x20也就是空格字符开始收录那偏移量就是(ascii码 - 0x20) × 16。ASC24、ASC32、ASC48的字节数同样按照“行字节数 × 行数”去算。拿ASC32来说如果字符宽度是16、高度是32那一行2字节32行每个字符占64字节如果字符宽度是32、高度是32每个字符就是128字节。不同字库作者的设定并不完全一样不要默认ASC32一定是128字节最好先用文件大小反推。判断ASC字库到底收了多少个字符也有窍门。常见文件是96个可打印字符也就是只收0x20到0x7F区间有些会连0x00到0x7F前128个字符一起收还有些是256个全ASCII字符。用文件总字节数除以单个字符的预估字节数如果结果是96、128或者256就说明文件收录范围清楚了。3. 读取代码实现从文件定位到像素数组3.1 先定义字库描述结构体读取代码如果只是临时验证写一个函数就够了。但实际项目里往往要同时用HZK16和ASC16还要在不同字号之间切换我把所有字库信息封成一个结构体代码会干净很多。#include stdio.h #include stdlib.h typedef struct { const char *path; // 字库文件路径 int width; // 字符点阵宽度 int height; // 字符点阵高度 int bytes_per_glyph; // 单个字符的字模字节数 int start_char; // ASCII字库起始字符0x00或0x20 } font_entry_t;这个结构体里bytes_per_glyph是最关键的一项。HZK16填32HZK24填72HZK32填128。换字库的时候只需要修改这个结构体的参数读取函数的其余部分不用动。3.2 中文字模读取函数核心函数就是按区位码计算偏移并读文件。这里我直接给出可以直接落地的版本int read_hzk_glyph(const char *font_path, unsigned char high, unsigned char low, unsigned char *out, int glyph_bytes) { int qu high - 0xA0; int wei low - 0xA0; if (qu 1 || qu 94 || wei 1 || wei 94) { return -1; } FILE *fp fopen(font_path, rb); if (!fp) { return -1; } long offset ((long)(qu - 1) * 94L (wei - 1)) * glyph_bytes; if (fseek(fp, offset, SEEK_SET) ! 0) { fclose(fp); return -1; } size_t got fread(out, 1, glyph_bytes, fp); fclose(fp); return (got (size_t)glyph_bytes) ? 0 : -1; }注意我把偏移量强制转成long类型。点阵字库文件一般不大几MB撑死了按理说不会溢出但嵌入式代码里常有人用16位的“int”去存偏移量遇到HZK48这种单字288字节的文件偏移量轻松突破几万16位一定放不下。这个坑我踩过直接在类型上做防御最省心。调用时也很简单。假设要显示“中”字源字符串是GB2312编码那么unsigned char font_buf[288]; int ret read_hzk_glyph(HZK16, text[0], text[1], font_buf, 32);这里text[0]是高字节text[1]是低字节。如果应用层使用的是UTF-8编码就不能这样直接取字节必须先把字符串转成GB2312或GBK再传入读取函数否则取到的字节根本不是区位码。3.3 英文字模读取函数ASCII字模读取更简单只要算好与起始字符之间的差值int read_asc_glyph(const char *font_path, unsigned char ch, unsigned char *out, int row_bytes, int height, int start_char) { if (ch start_char) { return -1; } int glyph_bytes row_bytes * height; long offset (long)(ch - start_char) * glyph_bytes; FILE *fp fopen(font_path, rb); if (!fp) { return -1; } if (fseek(fp, offset, SEEK_SET) ! 0) { fclose(fp); return -1; } size_t got fread(out, 1, glyph_bytes, fp); fclose(fp); return (got (size_t)glyph_bytes) ? 0 : -1; }行字节数用公式(width 7) / 8计算。比如ASC16宽度8行字节数就是1如果某个ASC32字库字符宽度是16行字节数就是(16 7) / 8 2。这样的写法比在代码里写死数字要稳得多。3.4 把字模数据渲染到屏幕上读出来的字节数组是一串比特位最终要交给绘制函数。绘制逻辑是两层循环void draw_glyph(int x, int y, unsigned char *glyph, int width, int height, int row_bytes, void (*draw_pixel)(int, int, int)) { for (int row 0; row height; row) { for (int col 0; col width; col) { int byte_index row * row_bytes (col / 8); int bit_index 7 - (col % 8); int filled (glyph[byte_index] bit_index) 0x01; draw_pixel(x col, y row, filled ? 1 : 0); } } }这里默认的位序是高位在左边也就是一个字节的最高位对应最左边的像素。如果显示出来的字模整体镜像了就把7 - (col % 8)改成col % 8让它变成低位对应左边。这个细节不同字库厂商的引擎实现习惯不同我在一个OLED项目里就遇到过镜像问题最后排查到是位序反了。4. 常见问题与排查技巧实录4.1 偏移算错导致显示乱字最典型的症状是读出来的字形不对但也不是完全随机看着像是某个“不认识的字”。多半是区位码偏移公式用错了尤其是把区码 - 1和位码 - 1漏写或者把94误写成96。排查时先取一个已知汉字比如“啊”手工按公式算一次偏移再对比文件里那个位置的十六进制字节看看是否和字模目标一致。我自己的排查经验是先在PC上用Python或十六进制编辑器做单步验证别直接烧到板子上调试。PC端看文件偏移和字节内容快得多而且能直接打印点阵图案一眼就能看出是对还是错。4.2 文件大小与预期不符经常有人拿着读HZK16的代码去读一个HZK12文件结果什么也对不上。罪魁祸首就是文件里每个字实际占的字节数和代码里写死的不一致。这里分享一个反推方法。先用ls -l看文件大小假设是282752如果预期是HZK16拿它除以32等于8836也就是94×94个完整区位说明这个字库是按完整区位表排列的。如果文件是216416除以32等于6763说明只存了6763个实际汉字。两种文件的偏移公式其实都可以用“区号位号”思维计算但后者不能直接用94×94完整槽位的公式去算否则会跳到空白区位上。稳妥做法是打开文件找几个常用汉字的位置测试一下偏移折腾5分钟比看半天文档有用。4.3 中文乱码的根源其实是编码问题在Linux或者PC上写示例程序时源文件通常保存为UTF-8。UTF-8编码的“中”字是0xE4 0xB8 0xAD三个字节和GB2312的0xD6 0xD0完全不一样。直接取text[0]和text[1]传给HZK读取函数得到的是错误区位码显示出来自然乱码。解决办法是把源字符串转成GBK/GB2312或者用iconv在程序里做运行时转换。单片机项目里如果字符串长度固定建议在工程里直接把中文字符串以GB2312编码的形式保存省去运行时转换的开销。4.4 12点阵这种非8倍数宽度怎么移位HZK12、HZK24这类点阵宽度不是8的整数倍时处理位运算要小心。12个点放在2个字节里前12位有效后4位是填充位绘制时必须用col width而不是col row_bytes * 8来限制循环否则会把填充的空白也算进去导致字形宽度拉长。24点阵更要注意。24 / 8 3看起来刚好整除但有些字库按32位对齐一行可能占4字节而不是3字节。文件大小反推法在这里同样适用24×24的字如果每行4字节单字就是96字节而不是72字节。5. 实操过程中的一些心得5.1 先用脚本把字模“看”出来再上板我建议任何字库读取代码在接入显示驱动之前都先用PC端脚本做一次可视化验证。下面这段Python代码可以快速打印一个汉字的点阵width, height 16, 16 qu, wei 16, 1 # “啊”字的区号、位号 glyph_bytes 32 with open(HZK16, rb) as f: offset ((qu - 1) * 94 (wei - 1)) * glyph_bytes f.seek(offset) data f.read(glyph_bytes) row_bytes (width 7) // 8 for row in range(height): line for col in range(width): filled data[row * row_bytes col // 8] (0x80 (col % 8)) line ## if filled else print(line)跑出来的图案如果和字帖一致再把它搬到嵌入式工程里。这样能提前暴露百分之八十的偏移和位序问题省下的调试时间非常可观。5.2 这套代码后续还能怎么扩展这套读取代码搞定后可以往两个方向扩展。一个是做多字号缓存把HZK16到HZK48都加载到内存中用结构体数组管理显示时先查缓存避免频繁读文件。另一个方向是做一个字模提取工具把font_entry_t结构体扩展成支持自定义宽度、高度和起始字符这样不管拿到什么非标字库都能通过参数适配不用反复改代码。还有一个实用的小技巧把字库文件放到SD卡或文件系统里时尽量把HZK和ASC文件放在同一个目录读取程序里用相对路径拼接。这样后续换字库版本只需要换文件不用重新编译固件。我在几次项目里都是这么干的测试新字号时省了非常多事。最后留个个人体会点阵字库看起来是一个很古老的技术没有抗锯齿没有矢量轮廓但它仍然是嵌入式显示里最可控、最不依赖外部渲染引擎的方案。每次换新屏幕我第一件事不是调驱动而是把这套字库读取函数跑通因为只要字能上屏后面的界面扩展就完全放得开了。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号