ESP32系列之LVGL(五):LVGL(v8.3)移植文件系统

系列文章目录

ESP32系列之LVGL(一):ESP32S3+ST7789点屏

ESP32系列之LVGL(二):ESP32S3移植LVGL(v8.3)

ESP32系列之LVGL(三):Gui-Guider的使用

ESP32系列之LVGL(四):输入设备(外部按键)对接



前言

由于lvgl的交互特性,从外部存储器读写数据是非常常见的操作,比如从sd卡中读取图片数据给ui做显示,又或者是将一些图像数据保存到sd卡等。本文是基于lvgl v8.3版本将文件系统移植到ESP32S3的一些踩坑记录。

另外,由于手上的开发板没有外部sd卡硬件,本文便先使用ESP32S3内部flash的文件系统进行操作,后续有机会的话也会补上sd卡相关部分。


一、准备工作

在移植之前,笔者也在网上找过一些经验帖,很多都说需要引入lv_fs_if库,但是后来发现8.3版本的lvgl已经合并了这个库,并不需要再单独引入,只需要按照之前外部按键输入的移植方式操作即可。

芯片平台:ESP32S3

LVGL版本:V8.3

ESP-IDF版本:v4.4

1.已完成屏幕显示功能

2.已完成lvgl库移植到esp32s3


二、移植文件系统

1.创建文件系统组件

在工程的components/lvgl/examples/porting/文件夹下找到 lv_port_fs_template.c 和lv_port_fs_template.h 两个文件,将其复制拷贝到新的 lv_port_fs 组件文件夹下并改名为 lv_port_fs.c 和 lv_port_fs.h,并创建CMakeLists.txt 文件。结构如下:

2.底层代码的实现

1.修改lv_port_fs.h

lv_port_fs.h 需要修改的不多,只需要把#if 0 改为 #if 1,同时自定义一个文件路径 LV_FS_PATH 即可。

/**
 * @file lv_port_fs_templ.h
 *
 */

/*Copy this file as "lv_port_fs.h" and set this value to "1" to enable content*/
#if 1

#ifndef LV_PORT_FS_H
#define LV_PORT_FS_H

#ifdef __cplusplus
extern "C" {
#endif

/*********************
 *      INCLUDES
 *********************/
#include "lvgl/lvgl.h"

/*********************
 *      DEFINES
 *********************/
#define LV_FS_PATH "/storage" /*Projet root*/
/**********************
 *      TYPEDEFS
 **********************/

/**********************
 * GLOBAL PROTOTYPES
 **********************/
void lv_port_fs_init(void);

/**********************
 *      MACROS
 **********************/

#ifdef __cplusplus
} /*extern "C"*/
#endif

#endif /*LV_PORT_FS_TEMPL_H*/

#endif /*Disable/Enable content*/

2. 修改lv_port_fs.c

lv_port_fs.c 文件中最主要的是实现fs_init()函数,这是底层硬件驱动的接口,这里参考了ESP-IDF中example/storage下的示例。

其次则是读写接口函数,这里实现了fs_open、fs_close、fs_read、fs_write、fs_seek、fs_tell等几个函数。这些函数的实现可以直接参考 lvgl/src/extra/libs/fsdrv/lv_fs_stdio.c 的实现。

驱动器接口关联字符这里我们使用的是内部flash,则将其与字符‘F’相关联,如果使用的是SD卡则与字符‘S’相关联。

/**
 * @file lv_port_fs_templ.c
 *
 */

/*Copy this file as "lv_port_fs.c" and set this value to "1" to enable content*/
#if 1

/*********************
 *      INCLUDES
 *********************/
#include "lv_port_fs.h"
#include "lvgl.h"

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include "esp_system.h"
#include "esp_log.h"
#include "esp_spiffs.h"
#include "esp_err.h"

/*********************
 *      DEFINES
 *********************/
#define TAG "lv_fs" // log输出的标签

/**********************
 *      TYPEDEFS
 **********************/
typedef FILE *file_t;

typedef DIR *dir_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void fs_init(void);

static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_size(lv_fs_drv_t * drv, void * file_p, uint32_t * size_p);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);

static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * rddir_p, char * fn);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * rddir_p);

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 * GLOBAL PROTOTYPES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void lv_port_fs_init(void)
{
    /*----------------------------------------------------
     * Initialize your storage device and File System
     * -------------------------------------------------*/
    fs_init();

    /*---------------------------------------------------
     * Register the file system interface in LVGL
     *--------------------------------------------------*/

    /*Add a simple drive to open images*/
    static lv_fs_drv_t fs_drv;
    lv_fs_drv_init(&fs_drv);

    /*Set up fields...*/
    fs_drv.letter = 'F';
    fs_drv.open_cb = fs_open;
    fs_drv.close_cb = fs_close;
    fs_drv.read_cb = fs_read;
    fs_drv.write_cb = fs_write;
    fs_drv.seek_cb = fs_seek;
    fs_drv.tell_cb = fs_tell;

    fs_drv.dir_close_cb = fs_dir_close;
    fs_drv.dir_open_cb = fs_dir_open;
    fs_drv.dir_read_cb = fs_dir_read;

    lv_fs_drv_register(&fs_drv);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/*Initialize your Storage device and File system.*/
static void fs_init(void)
{
   esp_vfs_spiffs_conf_t conf = {
        .base_path = LV_FS_PATH,
        .partition_label = NULL,
        .max_files = 5,
        .format_if_mount_failed = true};
    esp_err_t ret = esp_vfs_spiffs_register(&conf);
    if (ret != ESP_OK)
    {
        if (ret == ESP_FAIL)
        {
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        }
        else if (ret == ESP_ERR_NOT_FOUND)
        {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
        }
        else
        {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
        }
    }

    size_t total = 0, used = 0;
    ret = esp_spiffs_info(conf.partition_label, &total, &used);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
    }
    else
    {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    }
}

/**
 * Open a file
 * @param drv       pointer to a driver where this function belongs
 * @param path      path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
 * @param mode      read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
 * @return          a file descriptor or NULL on error
 */
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{

    void * f = NULL;

    if(mode == LV_FS_MODE_WR) 
    {
        f ="wb";         
    }
    else if(mode == LV_FS_MODE_RD) 
    {
        f ="rb";        
    }
    else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) 
    {
        f ="rb+";         
    }
    char filepath[256]={0};
    sprintf(filepath, LV_FS_PATH "/%s",path);
    return fopen(filepath, f);
}

/**
 * Close an opened file
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable. (opened with fs_open)
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
   
    LV_UNUSED(drv);
    fclose(file_p);
    return LV_FS_RES_OK;

}

/**
 * Read data from an opened file
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable.
 * @param buf       pointer to a memory block where to store the read data
 * @param btr       number of Bytes To Read
 * @param br        the real number of read bytes (Byte Read)
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
    
    LV_UNUSED(drv);
    *br = fread(buf, 1, btr, file_p);
    return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}

/**
 * Write into a file
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable
 * @param buf       pointer to a buffer with the bytes to write
 * @param btw       Bytes To Write
 * @param bw        the number of real written bytes (Bytes Written). NULL if unused.
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
    lv_fs_res_t res = LV_FS_RES_OK;

    /*Add your code here*/
    LV_UNUSED(drv);
    *bw = fwrite(buf, 1, btw, file_p);
    return (int32_t)(*bw) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}

/**
 * Set the read write pointer. Also expand the file size if necessary.
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable. (opened with fs_open )
 * @param pos       the new position of read write pointer
 * @param whence    tells from where to interpret the `pos`. See @lv_fs_whence_t
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
    lv_fs_res_t res = LV_FS_RES_OK;

    /*Add your code here*/
    switch (whence)
    {
    case LV_FS_SEEK_SET:
    {
        LV_UNUSED(drv);
        fseek(file_p, pos, SEEK_SET);
    }
    default:
        break;
    }

    return res;
}
/**
 * Give the position of the read write pointer
 * @param drv       pointer to a driver where this function belongs
 * @param file_p    pointer to a file_t variable.
 * @param pos_p     pointer to to store the result
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
    LV_UNUSED(drv);
    *pos_p = ftell(file_p);
    return LV_FS_RES_OK;
}

/**
 * Initialize a 'lv_fs_dir_t' variable for directory reading
 * @param drv       pointer to a driver where this function belongs
 * @param path      path to a directory
 * @return          pointer to the directory read descriptor or NULL on error
 */
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    /*Add your code here*/

    return res;
}

/**
 * Read the next filename form a directory.
 * The name of the directories will begin with '/'
 * @param drv       pointer to a driver where this function belongs
 * @param rddir_p   pointer to an initialized 'lv_fs_dir_t' variable
 * @param fn        pointer to a buffer to store the filename
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * rddir_p, char * fn)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    /*Add your code here*/

    return res;
}

/**
 * Close the directory reading
 * @param drv       pointer to a driver where this function belongs
 * @param rddir_p   pointer to an initialized 'lv_fs_dir_t' variable
 * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
 */
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * rddir_p)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    /*Add your code here*/

    return res;
}

#else /*Enable this file at the top*/

/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif

3.CMakeLists.txt 的实现

CMakeLists.txt 这里引用lvgl 以及spiffs组件。

file(GLOB_RECURSE srcs *.c)

idf_component_register(
    SRCS ${srcs}
    INCLUDE_DIRS .
    REQUIRES lvgl spiffs
)

 到了这一步我们的lvgl 文件系统差不多就可以在esp32s3上跑起来了,下面我们试下效果。


三、演示

这里我们通过文件系统从esp32s3内部flash读取一张图片并通过img控件显示到屏幕上。

1.图片准备

准备一张jpg或png图片并将其调整到合适大小(不超过屏幕),使用lvgl官网提供的图片取模工具取模为二进制文件。

 这里我们颜色格式选择CF_TRUE_COLOR,输出格式选择Binary RGB565,如果后面发现图片输出的颜色不对,也可以尝试使用Binary RGB565 Swap 。

2.工程修改

因为这里我们使用的是esp32s3内部的flash来挂载文件系统,所以需要对我们的工程做一定的修改。

2.1 使用自定义分区表

从其它工程中拷贝一份partitions.csv 文件到我们工程的main 文件夹下,并进行如下修改。

增加storage分区,分区大小依据esp32s3的flash大小进行修改;同时在menuconfig里使用自定义分区表。

# Name,   Type, SubType, Offset,  Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs,      data, nvs,     0x9000,  0x4000,
otadata,  data, ota,     ,        0x2000,
phy_init, data, phy,     ,        0x1000,
factory,  app,  factory, 0x10000, 2M,
storage,  data, spiffs,  ,        2M,

 

2.2 将图片文件加入工程

在工程的main文件夹下新建文件夹storage,并将图片取模后的bin文件放入该文件夹。

2.3 修改CMakeLists.txt

修改main文件夹下的CMakeLists.txt文件以创建相应的系统分区。

spiffs_create_partition_image(storage ../storage FLASH_IN_PROJECT)

 2.4 修改 mian.c

在 lv_init()之后初始化文件系统;设置img控件时根据图片实际大小来设置控件大小,图片源设置路径为:F:lv.bin。

#include <stdio.h>
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "lvgl.h"

#include "lv_port_fs.h"
#include "lvgl_helpers.h"

#include "demos/lv_demos.h"

#define LVGL_TICK_MS 1


#define TAG "main"



void lv_tick_task(void *arg)
{
    lv_tick_inc(LVGL_TICK_MS);
}


static void lv_example_canvas_1(void)
{
    lv_obj_t * img_play = lv_img_create(lv_scr_act());
    lv_obj_set_pos(img_play, 0, 0);
    //根据图片实际大小设置
    lv_obj_set_size(img_play, 163, 220);
    lv_obj_set_style_img_opa(img_play, 255, LV_PART_MAIN|LV_STATE_DEFAULT);

    // lv_img_set_src(img_play,&esp_logo);
    //设置图片源
    lv_img_set_src(img_play, "F:lv_1.bin");
   
            
}



void app_main(void)
{

    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }

    
    /* Initialize SPI or I2C bus used by the drivers */
    lvgl_driver_init();

    lv_init();

    lv_port_fs_init();    //初始化文件系统

    lv_color_t *buf1 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf1 != NULL);
    static lv_color_t *buf2 = NULL;

    static lv_disp_draw_buf_t disp_buf;

    uint32_t size_in_px = DISP_BUF_SIZE;
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, size_in_px);
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = LV_HOR_RES_MAX;
    disp_drv.ver_res = LV_VER_RES_MAX;
    disp_drv.flush_cb = disp_driver_flush;
    disp_drv.draw_buf = &disp_buf;
    lv_disp_drv_register(&disp_drv);

    const esp_timer_create_args_t periodic_timer_args = {
        .callback = &lv_tick_task,
        .name = "periodic_gui"};
    esp_timer_handle_t periodic_timer;
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, 1 * 1000));

    // lvgl demo演示
    // lv_demo_music();
    // lv_demo_stress();

    //ui 显示
    lv_example_canvas_1();
   
    while (1)
    {
        /* Delay 1 tick (assumes FreeRTOS tick is 10ms */
        vTaskDelay(pdMS_TO_TICKS(10));
        lv_task_handler();
    }
}

2.5 实际效果


四、其它

这里吐槽几个点吧,这也是篇文章的主要(不是)顺带的目的。

第一个就是lvgl更新的速度确实是有点快,这导致笔者在网上找到的很多关于lvgl的文章都还是v7版本,有时候需要从这里那里引入各种库,而新版本可能已经把库合并了,之前的经验也可能也行不通了,遇到无法解决的问题的时候需要去验证这样那样的方法确实是非常的浪费时间,等这篇文章发出来后说不定也已经过时了。

第二个就是拿来主义也不是那么好拿的,之前在一个前辈的工程里找到了一个移植好了的组件库,使用的时候才发现好像是v7版本的lvgl,本来想着应该差不多改改就行,结果后来在fs_open()这个函数的实现上栽了跟头。

v7与v8版本的fs_open()实现函数在传入参数上有了区别,v7的fs_open函数不直接返回文件描述符的指针,而v8 要直接返回一个void* 的指针,一开始由于没搞清楚这个void * 到底需要我们返回什么,导致在使用文件系统读取图片时报各种错,搞了几天才想到可能是返回值出错了,最后才在lvgl找到了官方示例。

v7:

static lv_fs_res_t fs_open(lv_fs_drv_t *drv, void *file_p, const char *path, lv_fs_mode_t mode)

v8:

 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)

 第三个就是在找错误的过程中觉得指针不愧是c语言的扛把子,直接被绕晕了。

typedef FILE *file_t;

static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{

    file_t *fp = file_p; 
    
    //*br = fread(buf, 1, btr, *fp);    //(v7抄的出错了)
    *br = fread(buf, 1, btr, fp);
    return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}

总结

本文主要是一些移植lvgl文件系统到esp32s3的踩坑记录,后续如果用到sd卡的话会做一些补充。

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: ESP32 移植 LVGL 需要使用 ESP-IDF 的支持,它提供了 ESP32 平台基本功能的驱动程序和配置文件。你可以按照 ESP-IDF 的官方文档,使用 ESP-IDF 的平台支持进行 LVGL 移植。 ### 回答2: ESP32是一款强大的微控制器,支持运行各种应用程序。LVGL是一种开源的GUI库,可以轻松实现图形化界面。下面我将介绍如何在ESP32移植LVGL。 首先,我们需要下载LVGL库的源代码。可以从官方网站或者GitHub上下载最新版本的LVGL库。 接下来,我们需要用到Arduino开发环境来编译和烧录代码。确保你已经安装了最新版本的Arduino开发环境,并且已经将ESP32开发板支持库安装。 然后,将LVGL源代码复制到你的Arduino项目文件夹中的一个子文件夹中。然后打开Arduino开发环境,找到你的项目文件夹并打开LVGL源代码文件夹。 在LVGL源代码文件夹中找到`lv_conf_template.h`文件,并将其复制为`lv_conf.h`文件。然后,打开`lv_conf.h`文件并根据你的需求进行配置。在配置文件中,你可以设置屏幕大小、颜色深度、字体等。 然后,在Arduino开发环境中打开ESP32的示例程序。找到一个空闲的示例程序,例如“Blink”示例程序,并将其复制为一个新的文件夹。 将LVGL源代码文件夹中的`lvgl`文件夹复制到ESP32示例程序文件夹中。 打开ESP32示例程序文件夹中的`platformio.ini`文件,并取消注释以下两行代码: ``` lib_ignore = ESP32_BLE_Arduino # lib_deps = ``` 然后,在`lib_deps =`后面添加以下代码: ``` ${env.LIBS_DIR}/lvgl ``` 保存并退出`platformio.ini`文件。 最后,编译和烧录ESP32示例程序。在Arduino开发环境中,选择正确的端口和开发板,并点击“编译并上传”按钮。完成后,你的ESP32就成功移植LVGL,并可以使用LVGL库创建图形化界面了。 总之,移植LVGLESP32可以通过将LVGL源代码复制到你的项目文件夹中,配置LVGL并将其添加到ESP32示例程序中来实现。这样,你就可以在ESP32上轻松创建图形化界面了。 ### 回答3: ESP32是一款功能强大的微控制器,LVGL是一种用于嵌入式系统的开源图形库。要将LVGL移植ESP32上,可以按照以下步骤进行操作: 1. 下载LVGL软件包:在LVGL官方网站上下载最新版本的LVGL软件包,并解压缩到ESP32开发环境的工作目录中。 2. 配置开发环境:打开ESP32开发环境(如ESP-IDF或Arduino IDE),创建新的项目或打开现有的项目。 3. 配置LVGL:在ESP32的项目文件中,找到并打开LVGL的配置文件(如lv_conf.h)。根据项目的需求,设置LVGL的配置选项,如分辨率、颜色深度、绘制驱动等。 4. 添加LVGL的源代码:将LVGL的源代码文件添加到ESP32项目的源文件目录中。可以复制整个LVGL文件夹,或者将相关文件手动复制到项目目录。 5. 添加驱动程序:根据项目的显示设备,选择并添加相关的驱动程序。LVGL支持多种显示设备和操作系统接口,如SPI、I2C、UART等。 6. 初始化LVGL:在ESP32的程序中,添加LVGL的初始化函数。该函数负责初始化LVGL图形库,并配置所需的驱动程序。 7. 编写应用程序:根据项目的需求,在ESP32的程序中编写LVGL应用程序。可以使用LVGL提供的各种图形元素和功能,如按钮、标签、文本框、进度条等。 8. 编译和烧录:将ESP32与计算机连接,并进行编译和烧录操作。确保所有的依赖文件和驱动程序都正确配置,并且LVGL的源代码能够正确编译。 9. 调试和优化:在ESP32上运行LVGL应用程序,并进行调试和优化。可以使用串口输出或者调试工具来查找和修复可能出现的问题。 10. 测试和部署:在完成调试和优化后,进行系统测试以及部署到实际项目中。 以上是将LVGL移植ESP32的一般步骤,具体的细节和操作方式可能会因项目的需求和开发环境而有所不同。希望这些简要的说明可以帮助您成功地将LVGL移植ESP32上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值