嵌入式显示界面的中文字库编程:实现GB2312字库的高效存储与调用
嵌入式显示中文最忌“每个界面写死字模数组”——字一多Flash瞬间爆。标准做法是挂一套GB2312点阵字库,按区位码算偏移直接取模,内部Flash可指针映射,SPI Flash可读偏移,调用层只关心“编码→偏移→画点”。
一、GB2312汉字怎么定位
GB2312汉字双字节,高字节是区码、低字节是位码,合法范围0xA1~0xFE。
#define HZ_BYTES(w,h) ((w/8)*(h)) // 单字字节数
static const uint8_t* font_base; // 字库起始地址
const uint8_t* hz_dot_addr(const uint8_t*g, int w,int h){
uint8_t q = g[0] - 0xA1; // 区
uint8_t ww= g[1] - 0xA1; // 位
uint32_t off = (q*94 + ww) * HZ_BYTES(w,h);
return font_base + off;
}
16×16单字32字节,24×24是72字节,32×32是128字节。
ASCII不走这套,单字节从0x20起:
off = (ch - 0x20) * asc_bytes;
二、字库放哪
内部Flash:字库小(菜单项子集几十KB)直接const数组或烧在固定扇区,base地址宏定义
SPI Flash(W25Q64):完整HZK16约256KB,放外置Flash,按offset发读命令
只读不加载:别把全字库拷进RAM,单次取32字节临时缓冲即可
SPI Flash读字模:
void hz_read(const uint8_t*g, uint8_t*buf,int sz){
uint32_t off = hz_offset(g);
w25q_read(FONT_BASE+off, buf, sz);
}
三、取模方向必须和屏一致
这是乱码头号原因:字库按“纵向取模”存,代码按“横向”扫,字就歪。
约定一种就行,工程里写死:
横向取模、行优先、高位在前(TFT常用)
或纵向取模、列优先(OLED页寻址常用)
16×16横向取模:16行,每行2字节,bit7对应最左像素。
void lcd_draw_hz(int x,int y,const uint8_t*g,uint16_t color){
uint8_t dot[32];
hz_read(g,dot,32);
for(int row=0;row<16;row++){
uint8_t b0=dot[row*2], b1=dot[row*2+1];
for(int col=0;col<8;col++){
if(b0&(0x80>>col)) px(x+col, y+row,color);
if(b1&(0x80>>col)) px(x+col+8, y+row,color);
}
}
}
纵向取模把行列循环反过来:col*2 + (row/8)取字节,row%8定位bit。
四、中英文混排
字符串扫描看首字节:
void ui_text(int x,int y,const char*s,uint16_t c){
while(*s){
if((uint8_t)s[0] >= 0xA1 && s[1]){ // GB2312汉字
lcd_draw_hz(x,y,(const uint8_t*)s,c);
x += 16; s += 2;
}else{ // ASCII
lcd_draw_asc(x,y,*s,c);
x += 8; s += 1;
}
}
}
汉字占两格宽、ASCII占一格,换行/对齐都按这个算,不然排版错位。
五、存储压缩三招
完整GB2312六千多字大多用不上,量产别全塞:
子集字库:菜单/报警词提取成自定义bin,偏移自己建索引
SPI Flash分区:16×16放0x800000,24×24放0x830000
常用字缓存:SRAM开32字环形缓冲,重复词不回读Flash
RLE压缩:点阵空白多,连续0跑长编码,解压进临时buf
500个常用字16×16约16KB,内部Flash都扛得住。
六、落坑清单
取模方向跟屏扫描不一致→满屏乱码
偏移用(区-0xA0)不统一→差一行错位
完整HZK16塞F103内部Flash→128KB直接爆
每次画字都全字库memcpy→RAM没了
中英混排只s++→汉字第二字节被当ASCII
SPI Flash读途中拉高CS→字节断帧
字体版权随便用商业TTF→量产踩雷
OLED页寻址按行扫→上下两截错位
七、落地顺序
定编码GB2312,弃UTF-8(除非上LVGL矢量)
选字号16×16起步,工业屏24×24
取模工具定死“横向/纵向+高位在前”
字库烧内部Flash或W25Q
封装hz_offset()按区位码算地址
画点函数匹配取模方向
中英文混排按首字节分流
常用子集+缓存降Flash读取
中文显示稳不稳,不在“字模数组写得对不对”,在区位码寻址和取模约定对齐。GB2312本质是张94×94表,汉字就是(区*94+位)*单字字节的偏移;驱动层只做“取32字节+按位画”,Flash占用、调用速度、乱码问题一起解决。写死全量数组那是Demo,按编码查表才是产品。





