基于Arduino平台的lvgl文件系统的移植与使用(自定义图片显示、中文字体显示、二维码显示)_arduino lvgl-程序员宅基地

技术标签: 嵌入式硬件  单片机  

前言:

本文着重讲解怎么移植与怎么使用,对于原理可自行百度或者B站检索相关资料,推荐百问网

阅读本文需要你会基本的Arduino 、lvgl及硬件知识。

1 介绍

LVGL(Light and Versatile Graphics Library)是一个免费的开源图形库,提供创建具有易于使用的图形元素、美丽的视觉效果和低内存占用的嵌入式GUI所需的一切。更多详情请点击这里

2 实验目的

由于一般的单片机内存很小,可能不够用于显示图片等需要较大内存的资源。那么我们可以使用SD卡来存储这些文件,当MCU需要时,读取使用即可。本实验分为两步,一是lvgl文件的移植二是文件系统的使用,所有操作基于Arduino平台。

3 软件与硬件

平台:Arduino 2.11  +lvgl 8.3;

硬件:Esp32 4M + SD卡2G + 240x240TFT屏幕;

4 硬件连接

Esp32与SD卡的链接引脚

​​​​​​​

                                                    注:有些引脚可自定。

5 文件系统移植

1 、打开lv_conf.h文件打开#IF_USE_FS_FATFS 。

“S”为盘符,就像我们的电脑一样,有很多硬盘,C盘,F盘等,当我们访问文件时就可以拿着盘符去访问,例如:“S:/test.png”,意为访问S盘下test.png文件。

#define LV_USE_FS_FATFS 1
#if LV_USE_FS_FATFS
    #define LV_FS_FATFS_LETTER 'S'     /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
    #define LV_FS_FATFS_CACHE_SIZE 1024*20   /*>0 to cache this number of bytes in lv_fs_read()*/
#endif

2、初始化SD卡。


void initSDCard() {
  if(!SD_MMC.begin()){
        Serial.println("Card Mount Failed");
        return;
    }
    uint8_t cardType = SD_MMC.cardType();

    if(cardType == CARD_NONE){
        Serial.println("No SD_MMC card attached");
        return;
    }

    Serial.print("SD_MMC Card Type: ");
    if(cardType == CARD_MMC){
        Serial.println("MMC");
    } else if(cardType == CARD_SD){
        Serial.println("SDSC");
    } else if(cardType == CARD_SDHC){
        Serial.println("SDHC");
    } else {
        Serial.println("UNKNOWN");
    }

}

注意,初始化SD卡需要在lv_init();方法之前,因为执行lv_init()时已经将FatFs文件系统与lvgl对接。故我们需要在此之前初始化SD卡,注意:初始化SD卡不能在lv_fs_port.c中的lv_fs_init()中,会报错,可能是由于C与C++混编引起的。

上面的操作完成后,编译运行即可。

6 lvgl文件系统的使用

6.1 显示图片

1、在lv_conf.h中打开#IF_USE_PNG 或#IF_USE_SJPG 或#IF_USE_GIF,打开什么取决于你用什么格式的图片,这些是lvgl自带的第三方解码库,解码库在lvgl 8.0版本以上才有,可以显示png、

bmp、jpg/sjpg(sjpg是官方专门为lvgl制作的一种图片格式,其特点为解码迅速、体积小)、GIF、QROCODE(二维码),当我们打开其中任一个宏时,lvgl会自动为我们的项目加入相关的解码器,不需要我们再去移植。

#define LV_USE_PNG 1

/*BMP decoder library*/
#define LV_USE_BMP 0

/* JPG + split JPG decoder library.
 * Split JPG is a custom format optimized for embedded systems. */
#define LV_USE_SJPG 1

/*GIF decoder library*/
#define LV_USE_GIF 0

/*QR code library*/
#define LV_USE_QRCODE 1

2、制作相关文件

我们先制作一张72x72的图片,这里我们用官方的图片。

在Arduino 中写下方代码,值得注意的是“S:/test1.png”,这里是绝对路径,可以设置为你自己的SD卡中的文件,注意图片不可以太大,否则屏幕上会有“No data”的提示,此问题可以自行百度解决。一般是RAM不足所致。


  lv_obj_t *img_bg = lv_img_create(lv_scr_act());
  lv_img_set_src(img_bg, "S:/test1.png");
  lv_obj_center(img_bg);

如若设置的都正确,那么编译烧录后屏幕将显示如下。

 6.2 自定义字体的显示

自定义字体的显示,有两种方式一种是C数组,一种是bin文件,本文中心是文件系统的使用,那么我们将使用bin文件,我们先制作bin文件,将其放入SD卡中,然后利用lvgl文件系统读取显示在屏幕上。

1、bin字体文件的制作。

先按照it网王老师的方法安装lv_font_conv工具,也可以直接用王老师的方法去制作bin字体文件,如果你有Python环境的话,也可以用我用tkinter写的带UI的转换工具(前提是你已经按照王老师的方法安装好lv_font_conv)。

from tkinter import *
from tkinter.filedialog import askopenfilename
import os
root=Tk()
root.geometry("250x300+552+173")
root.title("LVGL字体转换")
var=StringVar()
def file():
    global path,savepath
    path=askopenfilename(title="选择一个字体文件")
    if path:
        savepath="/".join(path.split("/")[:-1])+"/"
    else:
        var.set("文件错误,请重试")
def but():
    global inp1,inp2,tex1
    btn1=Button(root,text="点击选择字体文件夹",fg="blue",command=file)
    btn1.pack(side=TOP,anchor="e")
    btn2=Button(root,text="点击开始转换",fg="red",command=save)
    btn2.pack(side=TOP,anchor="e")
    lab1=Label(root,textvariable=var,fg="Pink")
    lab1.pack(side=TOP,anchor="w")
    lab=Label(root,text="名称:",fg="red")
    lab1=Label(root,text="大小:",fg="red")
    lab.place(relx=0,rely=0.01)
    lab1.place(relx=0,rely=0.07)
    var.set("请在上方输入框内输入字体名称与大小\n且在下方输入想要的汉字然后点击转换")
    inp1=Entry(root,bg="yellow")
    inp1.place(relx=0.15,rely=0.07,relheight=0.08,relwidth=0.3)
    inp2=Entry(root,bg="hotpink")
    inp2.place(relx=0.15,rely=0.01,relheight=0.08,relwidth=0.3)
    tex1=Text(root,bg="pink",width=248,height=200)
    tex1.pack(side=BOTTOM)
def save():          
    o="lv_font_conv --size {0} --format bin --bpp 1 --font  {1}  --symbols  {2} --no-compress -o {3}.bin".format(inp1.get(),path,tex1.get("1.0",END).replace("\n",""),savepath+inp2.get())
    try:
        os.system(o)
        var.set("转换成功,字体保存在\n{0}".format(savepath))
    except:
        var.set("失败")

def run():
    but()
    root.mainloop()
if __name__ == "__main__":
    run()

如设置无错误的话,将得到一个xxx.bin文件。

然后在主程序中写下如下代码

    lv_font_t *font;
    font = lv_font_load("S:ttf1.bin");
    if (font == NULL)
    {
        Serial.println("font load failed");
    }
    static lv_style_t font_style;
    lv_style_init(&font_style);
    lv_style_set_text_font(&font_style, font);

    lv_obj_t *label_zh = lv_label_create(lv_scr_act());
    lv_obj_center(label_zh);
    lv_obj_add_style(label_zh, &font_style, 0);
    lv_label_set_text(label_zh, "安");

编译烧录后,我们的屏幕将显示如下:

 我们的自定义字体显示成功,其实还可以显示图标字体,那么这里不再过多介绍,感兴趣的同学可以自行百度或者查阅相关手册学习。

6.3 二维码的显示

二维码我们不需要文件系统就可以显示,这里作为一个简单介绍。显示二维码的前提是,保证你已经打开#LV_USE_QRCODE宏定义。那么在我们的程序中输入以下代码,编译烧录后即可以在屏幕上显示如图所示的二维码了,

其中data里存放的是百度首页的网址,手机扫二维码后就可以跳转百度网页,同学们可以借助此组件,创造一些有意思的内容出来。

  lv_color_t bg_color = lv_palette_lighten(LV_PALETTE_LIGHT_BLUE, 5);
  lv_color_t fg_color = lv_palette_darken(LV_PALETTE_BLUE, 4);

  const char* data = "https://baidu.com";
  lv_obj_t * qr = lv_qrcode_create(lv_scr_act(),150,fg_color, bg_color);
  lv_qrcode_update(qr,data,strlen(data));
  lv_obj_center(qr);

 7、完整代码

#include <lvgl.h>
#include <TFT_eSPI.h>
#include <SD_MMC.h>

static const uint16_t screenWidth = 240;
static const uint16_t screenHeight = 240;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[screenWidth * 10];

TFT_eSPI tft = TFT_eSPI( screenWidth, screenHeight); 


void initSDCard() {
  if(!SD_MMC.begin()){
        Serial.println("Card Mount Failed");
        return;
    }
    uint8_t cardType = SD_MMC.cardType();

    if(cardType == CARD_NONE){
        Serial.println("No SD_MMC card attached");
        return;
    }

    Serial.print("SD_MMC Card Type: ");
    if(cardType == CARD_MMC){
        Serial.println("MMC");
    } else if(cardType == CARD_SD){
        Serial.println("SDSC");
    } else if(cardType == CARD_SDHC){
        Serial.println("SDHC");
    } else {
        Serial.println("UNKNOWN");
    }

}

void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
  uint32_t w = (area->x2 - area->x1 + 1);
  uint32_t h = (area->y2 - area->y1 + 1);
  tft.startWrite();
  tft.setAddrWindow(area->x1, area->y1, w, h);
  tft.pushColors((uint16_t *)&color_p->full, w * h, true);
  tft.endWrite();
  lv_disp_flush_ready(disp);
}

void setup(){
  Serial.begin(115200);
  initSDCard();
  lv_init();

  tft.begin();        
  tft.setRotation(3); 
  lv_disp_draw_buf_init(&draw_buf, buf, NULL, screenWidth * 10);
  static lv_disp_drv_t disp_drv;
  lv_disp_drv_init(&disp_drv);
  disp_drv.hor_res = screenWidth;
  disp_drv.ver_res = screenHeight;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.draw_buf = &draw_buf;
  lv_disp_drv_register(&disp_drv);

  /*二维码显示*/
  lv_color_t bg_color = lv_palette_lighten(LV_PALETTE_LIGHT_BLUE, 5);
  lv_color_t fg_color = lv_palette_darken(LV_PALETTE_BLUE, 4);
  const char* data = "https://baidu.com";
  lv_obj_t * qr = lv_qrcode_create(lv_scr_act(),150,fg_color, bg_color);
  lv_qrcode_update(qr,data,strlen(data));
  lv_obj_center(qr);

  /* 图片显示*/
  lv_obj_t *img_bg = lv_img_create(lv_scr_act());
  lv_img_set_src(img_bg, "S:/wink.png");
  lv_obj_center(img_bg);


  /* 字体测试*/
    lv_font_t *font;
    font = lv_font_load("S:ttf1.bin");
    if (font == NULL)
    {
        Serial.println("font load failed");
    }
    static lv_style_t font_style;
    lv_style_init(&font_style);
    lv_style_set_text_font(&font_style, font);

    lv_obj_t *label_zh = lv_label_create(lv_scr_act());
    lv_obj_center(label_zh);
    lv_obj_add_style(label_zh, &font_style, 0);
    lv_label_set_text(label_zh, "安");
}

void loop(){
  lv_timer_handler(); 
  vTaskDelay(5);
}

8、结语

基于Arduino框架的lvgl8.3文件系统的移植与使用就展示完毕,感兴趣的可以尝试一下,第一篇文章,有写的不对的地方请指正。

注:原创作品,转载请注明出处,谢谢合作;

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/houzipython/article/details/131702917

智能推荐

攻防世界_难度8_happy_puzzle_攻防世界困难模式攻略图文-程序员宅基地

文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文

达梦数据库的导出(备份)、导入_达梦数据库导入导出-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作  导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释:   cwy_init/init_123..._达梦数据库导入导出

js引入kindeditor富文本编辑器的使用_kindeditor.js-程序员宅基地

文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js

STM32学习过程记录11——基于STM32G431CBU6硬件SPI+DMA的高效WS2812B控制方法-程序员宅基地

文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6

计算机网络-数据链路层_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输

软件测试工程师移民加拿大_无证移民,未受过软件工程师的教育(第1部分)-程序员宅基地

文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...

随便推点

Thinkpad X250 secure boot failed 启动失败问题解决_安装完系统提示secureboot failure-程序员宅基地

文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure

C++如何做字符串分割(5种方法)_c++ 字符串分割-程序员宅基地

文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割

2013第四届蓝桥杯 C/C++本科A组 真题答案解析_2013年第四届c a组蓝桥杯省赛真题解答-程序员宅基地

文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答

基于供需算法优化的核极限学习机(KELM)分类算法-程序员宅基地

文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。

metasploitable2渗透测试_metasploitable2怎么进入-程序员宅基地

文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入

Python学习之路:从入门到精通的指南_python人工智能开发从入门到精通pdf-程序员宅基地

文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf

推荐文章

热门文章

相关标签