首页 > 其他分享 >STM32F407使用LVGL之字库IC

STM32F407使用LVGL之字库IC

时间:2024-11-16 17:57:20浏览次数:1  
标签:LVGL bitmap code STM32F407 IC font glyph dsc out

LVGL使用字库IC - 基于STM32F407

在上一篇笔记中,记录了所以用STM32F407移植LVGL。其中提到了中文显示,使用的是字库IC。相比于大多数使用的数组字库方式,使用字库IC编译后占用更小的存储空间,可以解码并显示更多的汉字,能够支持更多的字体大小等。

字库IC读取字形数据

根据自己所使用的IC,实现字形数据的读取。我用的晶联讯屏幕带字库的,给了字库读取的例程。做了些修改,以获取到LVGL显示字体所需要的数据。

已知:

  1. LVGL库会传入汉字或字符的utf-8编码,字库IC使用的是GB2312编码。
  2. 获取字形数据需要知道字体大小。
  3. 需要数据区存储获取的字形数据。
  4. 需要知道得到的字形数据大小。
  5. 需要知道字体的长*宽。

因此,需要做的工作:

  1. 编码转换(放到后面)。
  2. 修改字库读取的例程,以满足上述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<size; i++){                          // 读取数据,按size大小
        __zk_read_byte(obj, &data[i]);
    }
}

完成了根据字体和编码获取数据。至于SPI的内容,就不赘述了。

LVGL中添加字体

看了LVG了关于字体的代码,加入新字体需要定义结构体。如:

#if LVGL_VERSION_MAJOR >= 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_txtjlx_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编码方式保存。

标签:LVGL,bitmap,code,STM32F407,IC,font,glyph,dsc,out
From: https://www.cnblogs.com/Johnny-m/p/18548782

相关文章

  • Meissonic 文生图模型:小参数,超轻量,本地部署推理教程
    最近,阿里巴巴集团、SkyworkAI携手香港科技大学及其广州校区、浙江大学、加州大学伯克利分校,联合推出一款超厉害的文生图多模态模型——Meissonic!它仅有1B参数量,却能在普通电脑上轻松运行推理,生成高质量图像,未来甚至有望在无线端实现文本到图像的生成,简直是文生图领域的“小......
  • 杂散电感对二极管反向恢复期间影响_LTSPICE
    前言二极管两端不加电压的时候本身就存在空间电荷区.当二极管接正向电压,空间电荷区被削弱,二极管正向导通.当二极管接反向电压,空间电荷区顺势增强,极大的阻碍了载流子移动,此时二极管截止.那么当二极管由正向导通转变为反向导通,空间电荷区从被削弱状态到空间电荷区变得更......
  • ssm125四六级报名与成绩查询系统+jsp(论文+源码)_kaic
     毕业设计(论文)题目:四六级报名与成绩查询系统的设计与实现      摘 要互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对四六级报名信息管理混......
  • 记录---nextTick用过吗?讲一讲实现思路吧
    ......
  • CSS(7):定位position:相对定位(relative)、绝对定位(absolute)、固定定位(fixed)和静态定位(st
    一.定位:将盒子定在某一个位置,其规则为:定位=定位模式+边偏移 。二:定位模式1.static静态定位:元素无设置的时候就是static        “position:static;”2.relative相对定位:相对于当前位置进行移动,通过设置偏移属性(top、right、bottom、left)来使其在水平和垂直......
  • 洛谷 P6874 [COCI2013-2014#6] KOCKICE
    动笔算算样例可得一个性质,只要确定中间位置的数是多少,其他位置就可以直接求出。如果我们暴枚中间的数,必然超时。于是我们需要用二分。如果中间位置上的数是答案,那么无论什么数,操作次数一定多于他。所以我们只要判断关系就能判断往哪边找。代码:#include<bits/stdc++.h>using......
  • C. Penchick and BBQ Buns (python解)-codeforces
    C.PenchickandBBQBuns(python解)-codeforces原题链接:点击传送问题分析:我们需要为给定数量的BBQ包子分配填料,满足以下条件:每种填料必须至少使用两次,或者不使用。任何两个相同填料的包子之间的距离必须是一个完全平方数。思路:为了满足条件,我们可以利用完全平方数的......
  • c++的static和extern
    首先这是一个static和extern的例子:#include<bits/stdc++.h>usingnamespacestd;inta(intb,intc){intd;d=rand()%3+1;externintd;return0;}intmain(){a(1,2);staticintd;cout<<d;return0;}实际上extern就是隐藏变......
  • (nice!!!)(LeetCode) 3240. 最少翻转次数使二进制矩阵回文 II (分类讨论、数组)
    题目:3240.最少翻转次数使二进制矩阵回文II思路:分类讨论,需要对行和列的个数进行讨论,时间复杂度为0(nm),细节看注释。C++版本:classSolution{public:intminFlips(vector<vector<int>>&grid){intans=0;intn=grid.size(),m=grid[0].size();......
  • 嵌入式系统应用-LVGL的应用-音乐播放器
    嵌入式系统应用-LVGL的应用-音乐播放器1播放器介绍2LVGL绘制2.1背景绘制2.2按键绘制2.2.1图标下载2.2.2由于图片尺寸默认200*200,利用图片工具转化成为50*502.2.3利用lvgl在线转化工具,进行转化c文件2.2.4代码显示2.3滑动条绘制2.4文本绘制2.5绘制效果3STM......