Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
LVGL使用字库IC芯片显示中文
发布于 2024-12-02 09:58:42 浏览:15
订阅该版
[tocm] # LVGL使用字库IC芯片显示中文 ### LVGL使用字库IC - 基于STM32F407 LVGL使用的是字库IC。相比于大多数使用的数组字库方式,使用字库IC编译后占用更小的存储空间,可以解码并显示更多的汉字,能够支持更多的字体大小等。 ### 字库IC读取字形数据 根据自己所使用的IC,实现字形数据的读取。我用的晶联讯屏幕带字库的,给了字库读取的例程。做了些修改,以获取到LVGL显示字体所需要的数据。 ***已知:*** - LVGL库会传入汉字或字符的utf-8编码,字库IC使用的是GB2312编码。 - 获取字形数据需要知道字体大小。 - 需要数据区存储获取的字形数据。 - 需要知道得到的字形数据大小。 - 需要知道字体的长*宽。 * 因此,需要做的工作: 编码转换(放到后面)。 修改字库读取的例程,以满足上述2-4的内容。 函数原型如下: ``` void jlx350_read_font(struct jlx350_object* obj, uint32_t gb_code, enum jlx350_font font, uint8_t* buff, uint16_t* size, uint16_t* w, uint16_t* h); // obj: 自己定义的结构体,不重要,可有可无 // gb_code: GB2312的字符编码 // jlx350_font: 字库IC支持的字体大小,这里使用了枚举 // buff: 用于存储字形数据的数组 // size: 字形数据大小 // w: 字形的宽度 // h: 字形的高度 可以看出,字符编码和字体大小是传入的,而后面的四个参数是读出的。具体内容看函数实现: void jlx350_read_font(struct jlx350_object* obj, uint32_t gb_code, enum jlx350_font font, uint8_t* buff, uint16_t* size, uint16_t* w, uint16_t* h) { obj->ops.pin_cs_zk_set(obj, 0); // 片选 switch(font){ // 跳转不同的字体大小 case JLX_FONT_12: __read_font_12(obj, gb_code, buff, size, w, h); break; case JLX_FONT_16: __read_font_16(obj, gb_code, buff, size, w, h); break; case JLX_FONT_24: __read_font_24(obj, gb_code, buff, size, w, h); break; case JLX_FONT_32: __read_font_32(obj, gb_code, buff, size, w, h); break; } obj->ops.pin_cs_zk_set(obj, 1); } // 以下使用其中一个字体大小举例 font_12 void __read_font_12(struct jlx350_object* obj, uint32_t unicode, uint8_t* buff, uint16_t* size, uint16_t* w, uint16_t* h) { uint64_t font_addr = 0; uint8_t code_H = (unicode >> 8) & 0xFF; // GB2312编码处理,需要根据编码获取到对应的IC存储地址 uint8_t code_L = unicode & 0xFF; // 这部分是根据例程来的,就是计算font_addr if((code_H >= 0xB0) && (code_H <= 0xF7) && (code_L >= 0xA1)){ font_addr = (code_H - 0xB0) * 94; font_addr += (code_L - 0xA1) + 846; font_addr = (uint64_t)(font_addr * 24); font_addr = (uint64_t)(font_addr + 0x00); *size = 24; // 字形数据大小,12*12的汉字,需要12*2=24 bytes *w = 12; // 字形的宽度 *h = 12; // 字形的高度 } else if((code_H >= 0xA1) && (code_H <= 0xA9) && (code_L >= 0xA1)){ font_addr = (code_H - 0xA1) * 94; font_addr += (code_L - 0xA1); font_addr = (uint64_t)(font_addr * 24); font_addr = (uint64_t)(font_addr + 0x00); *size = 24; *w = 12; *h = 12; } else if((code_L >= 0x20) && (code_L <= 0x7E)){ font_addr += (code_L - 0x20); font_addr = (uint64_t)(font_addr * 12); font_addr = (uint64_t)(font_addr + 0x1DBE00); *size = 12; // 这里是英文字符/数字/半角符号 所以大小是12就可以了,IC内的字形是6*12 *w = 6; *h = 12; } // 以上根据IC的手册,确定读取字形的数据长度,字形宽/高,并赋值 // 下面的是读取数据了,存入到buff中 __zk_read_font(obj, font_addr, buff, *size); } // SPI读取字形数据 void __zk_read_font(struct jlx350_object* obj, uint32_t glyph_addr, uint8_t* data, uint16_t size) { __zk_write_byte(obj, 0x03); // 指令 __zk_write_byte(obj, (glyph_addr >> 16) & 0xFF); // 字形地址,24 bits __zk_write_byte(obj, (glyph_addr >> 8) & 0xFF); __zk_write_byte(obj, glyph_addr & 0xFF); for(int i=0; i
= 8 static const lv_font_fmt_txt_dsc_t font_dsc = { #else static lv_font_fmt_txt_dsc_t font_dsc = { #endif .glyph_bitmap = glyph_bitmap, .glyph_dsc = NULL, }; /*----------------- * PUBLIC FONT *----------------*/ /*Initialize a public general font descriptor*/ #if LVGL_VERSION_MAJOR >= 8 const lv_font_t jlx_font_24 = { #else lv_font_t lv_font_montserrat_24 = { #endif .get_glyph_dsc = jlx_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/ .get_glyph_bitmap = jlx_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/ .line_height = 26, /*The maximum line height required by the font*/ .base_line = 5, /*Baseline measured from the bottom of the line*/ #if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0) .subpx = LV_FONT_SUBPX_NONE, #endif #if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8 .underline_position = -2, .underline_thickness = 1, #endif .dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */ }; ``` 以上的结构体中,指明了两个函数jlx_font_get_glyph_dsc_fmt_txt和jlx_font_get_bitmap_fmt_txt。 jlx_font_get_glyph_dsc_fmt_txt获取字库IC的数据。jlx_font_get_bitmap_fmt_txt将字库数据转换为像素数据。 dsc指向的结构体内的glyph_bitmap成员表示像素内容。这里字体最大是32*32,占用32*4个像素,因此glyph_bitmap是一个128大小的uint8数组。 其余成员的作用没有深究。 ``` bool jlx_font_get_glyph_dsc_fmt_txt(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next) { /*It fixes a strange compiler optimization issue: https://github.com/lvgl/lvgl/issues/4370*/ bool is_tab = unicode_letter == '\t'; if(is_tab) { unicode_letter = ' '; } enum jlx350_font f; if(font == &jlx_font_12){ f = JLX_FONT_12; }else if(font == &jlx_font_16){ f = JLX_FONT_16; }else if(font == &jlx_font_24){ f = JLX_FONT_24; }else if(font == &jlx_font_32){ f = JLX_FONT_32; } uint16_t size = 0, w = 0, h = 0; // size好像没啥用 memset(glyph_bitmap, 0, 128); jlx350_read_font(&obj_jlx350, unicode_letter, f, glyph_bitmap, &size, &w, &h); dsc_out->adv_w = w; // 可以调节字符的间距 dsc_out->box_h = h; dsc_out->box_w = w; dsc_out->ofs_x = 0; dsc_out->ofs_y = 0; dsc_out->format = 1; dsc_out->is_placeholder = false; dsc_out->gid.index = 0; // 返回给lvgl字形数据在数组内的索引,因为使用IC,每次都只读一个字形的数据,这里就直接使用0,和使用大数组是不一样的。 if(is_tab) dsc_out->box_w = dsc_out->box_w * 2; return true; } const void * jlx_font_get_bitmap_fmt_txt(lv_font_glyph_dsc_t * g_dsc, lv_draw_buf_t * draw_buf) { const lv_font_t * font = g_dsc->resolved_font; uint8_t * bitmap_out = draw_buf->data; lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)font->dsc; int32_t gsize = (int32_t) g_dsc->box_w * g_dsc->box_h; if(gsize == 0) return NULL; if(fdsc->bitmap_format == LV_FONT_FMT_TXT_PLAIN) { const uint8_t * bitmap_in = &fdsc->glyph_bitmap[0]; uint8_t * bitmap_out_tmp = bitmap_out; int32_t i = 0; int32_t x, y; uint32_t stride = lv_draw_buf_width_to_stride(g_dsc->box_w, LV_COLOR_FORMAT_A8); if(1) { // 这里的循环,根据字形数据转换成pixel数据。根据自己实际修改 for(y = 0; y < g_dsc->box_h; y ++) { for(x = 0; x < g_dsc->box_w; x++, i++) { i = i & 0x7; if(i == 0) bitmap_out_tmp[x] = (*bitmap_in) & 0x80 ? 0xff : 0x00; else if(i == 1) bitmap_out_tmp[x] = (*bitmap_in) & 0x40 ? 0xff : 0x00; else if(i == 2) bitmap_out_tmp[x] = (*bitmap_in) & 0x20 ? 0xff : 0x00; else if(i == 3) bitmap_out_tmp[x] = (*bitmap_in) & 0x10 ? 0xff : 0x00; else if(i == 4) bitmap_out_tmp[x] = (*bitmap_in) & 0x08 ? 0xff : 0x00; else if(i == 5) bitmap_out_tmp[x] = (*bitmap_in) & 0x04 ? 0xff : 0x00; else if(i == 6) bitmap_out_tmp[x] = (*bitmap_in) & 0x02 ? 0xff : 0x00; else if(i == 7) { bitmap_out_tmp[x] = (*bitmap_in) & 0x01 ? 0xff : 0x00; } if((i == 7) || (x == g_dsc->box_w-1)){ bitmap_in++; if(x == g_dsc->box_w-1) i = 7; } } bitmap_out_tmp += g_dsc->box_w; } } return draw_buf; } /*If not returned earlier then the letter is not found in this font*/ return NULL; } ``` 以上两个函数是重要的lvgl字形数据获取和转换的。都是根据原有的函数修改而来。字符的“间隔”和“行高”都可以在字体的结构体内相关参数修改。 ***注:glyph_bitmap数组需要是全局变量。*** 最后,再添加字体的声明。我是在lv_font.h中加的。 ``` LV_FONT_DECLARE(jlx_font_32) LV_FONT_DECLARE(jlx_font_12) LV_FONT_DECLARE(jlx_font_16) LV_FONT_DECLARE(jlx_font_24) ``` 字符编码 以上完成之后,就可以按照字符的编码,查找IC内的字形数据,最后显示到显示屏。 但是,前提是能够正确解码。在lv_text.c内修改解码函数。 ``` // 添加gb2312的解码函数声明和定义 static uint32_t lv_text_gb2312_next(const char * txt, uint32_t * i); static uint32_t lv_text_gb2312_next(const char * txt, uint32_t * i) { /** * Unicode to UTF-8 * 00000000 00000000 00000000 0xxxxxxx -> 0xxxxxxx * 00000000 00000000 00000yyy yyxxxxxx -> 110yyyyy 10xxxxxx * 00000000 00000000 zzzzyyyy yyxxxxxx -> 1110zzzz 10yyyyyy 10xxxxxx * 00000000 000wwwzz zzzzyyyy yyxxxxxx -> 11110www 10zzzzzz 10yyyyyy 10xxxxxx */ uint32_t result = 0; /*Dummy 'i' pointer is required*/ uint32_t i_tmp = 0; if(i == NULL) i = &i_tmp; /*Normal ASCII*/ if(LV_IS_ASCII(txt[*i])) { result = txt[*i]; (*i)++; } /*Real UTF-8 decode*/ else { result = (txt[*i] << 8) | txt[(*i)+1]; (*i) += 2; } return result; } // 修改lv_text_encoded_next函数指针的值 uint32_t (*const lv_text_encoded_next)(const char *, uint32_t *) = lv_text_gb2312_next; ``` 至此,大功告成。要注意有汉字需要显示的文件使用gb2312编码方式保存。
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
用RTT跑流水灯
这家伙很懒,什么也没写!
文章
11
回答
58
被采纳
2
关注TA
发私信
相关文章
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
SFUD
msh
rt_mq_消息队列_msg_queue
keil_MDK
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1443
个答案
289
次被采纳
张世争
805
个答案
174
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
4
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
1
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部