ESP32在ESP-IDF框架下为LVGL(v8.3)配置SD卡文件系统

news2024/9/29 15:32:19

踩坑记录

1、如果SD卡曾经做过系统盘(比如说:作为树莓派的系统盘),那么要把系统盘的分区合并成一个(这个网上有很多教程),并重新格式化,否则实验会失败。
2、并不是买回来的新的SD卡就一定能用(并不是说用读卡器插在电脑上能够正常读写,就表示一定能够实验成功),买过几张卡,确实有不能用的,导致改bug搞了一下午没有成功。
3、可以先运行一下IDF提供的SD卡示例,判断是否能够挂载文件系统成功(如果能够正常读取卡信息就表示成功了)。
说明:实验没有采用lv_fs_if作为一个组件的移植方式,可能与网上的资料稍有不同。

移植过程

说明:本实验是在该实验点击这里的基础上进行的。
1、打开小齿轮(SDK Configureation editor)找到3rd Party Libraries,选中File sysytem on top of FatFS,修改下面为83(ASCII中的‘S’),下面是读取使用的缓冲区大小。
在这里插入图片描述
2、创建新的小组件(ctrl+shift+p ESP-IDF:创建新的ESP-IDF组件命名为sd_card(可自行更改)),在include文件夹中创建sd_card.h文件,include文件夹之外创建sd_card.cCMakeLists.txt文件。三个文件的内容如下。(sd_card组件代码内容是由sd卡示例文件sdspi示例项目修改而来,可直接使用下面文件,也可以自己去更改。)

//*************************************sd_card.h文件内容******************************************
#ifndef _SD_CARD_H
#define _SD_CARD_H

#ifdef __cplusplus
extern "C" {
#endif

#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "esp_err.h"
static const char *TAG = "TFCARD";

#define MOUNT_POINT "/sdcard"
#define SPI_DMA_CHAN    1
// Pin assignments can be set in menuconfig, see "SD SPI Example Configuration" menu.
// You can also change the pin assignments here by changing the following 4 lines.
#define PIN_NUM_MISO  19
#define PIN_NUM_MOSI  23
#define PIN_NUM_CLK   18
#define PIN_NUM_CS    5

esp_err_t sd_init();



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

#endif

//************************************sd_card.c文件内容*******************************************
#include "sd_card.h"
sdmmc_card_t *card;
esp_err_t sd_init()
{
    esp_err_t ret;

    // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = false,
        .max_files = 5,
        .allocation_unit_size = 16 * 1024
    };
    
    const char mount_point[] = MOUNT_POINT;
    ESP_LOGI(TAG, "Initializing SD card");

    // Use settings defined above to initialize SD card and mount FAT filesystem.
    // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
    // Please check its source code and implement error recovery when developing
    // production applications.
    ESP_LOGI(TAG, "Using SPI peripheral");

    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    host.slot = VSPI_HOST;
    spi_bus_config_t bus_cfg = {
        .mosi_io_num = PIN_NUM_MOSI,
        .miso_io_num = PIN_NUM_MISO,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4*1024*sizeof(uint8_t),
    };
    ret = spi_bus_initialize(host.slot, &bus_cfg, host.slot);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to initialize bus.");
        return;
    }

    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = PIN_NUM_CS;
    slot_config.host_id = host.slot;

    ESP_LOGI(TAG, "Mounting filesystem");
    ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem. "
                     "If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        } else {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        return ESP_FAIL;
    }
    ESP_LOGI(TAG, "Filesystem mounted");

    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);
    return ESP_OK;

}
//******************************CMakeLists.txt文件内容*******************************************
idf_component_register(
        SRCS	"sd_card.c"
        INCLUDE_DIRS "include"
        REQUIRES  fatfs)

3、SD卡接线方式,如果想节省一个引脚,可将CS引脚直接接地。接线方式与安信可esp-cam模组相同,原理图参考
在这里插入图片描述

ESP32引脚SD卡引脚
19MISO
23MOSI
18CLK
5CS

4、因为LVGL要使用文件系统和初始化SD卡,所以LVGL要依赖fatfs和sd_card组件。打开lvgl–>env_support–>cmake–>esp.cmake文件,在第45行位置添加依赖(REQUIRES后面的内容),添加后的依赖如下:

  idf_component_register(SRCS ${SOURCES} ${EXAMPLE_SOURCES} ${DEMO_SOURCES}
      INCLUDE_DIRS ${LVGL_ROOT_DIR} ${LVGL_ROOT_DIR}/src ${LVGL_ROOT_DIR}/../
                   ${LVGL_ROOT_DIR}/examples ${LVGL_ROOT_DIR}/demos
      REQUIRES esp_timer
      fatfs
      sd_card)

5、打开lv_fs_fatfs.c文件(路径:lvgl/src/extra/libs/fsdrv/),添加sd_card.h头文件(在第10行位置:#include "sd-card.h")。将第230行的两个DIR修改为FF_DIR。在第92行处fs_init(void)函数中调用sd_init()函数来初始化sd卡。
6、注:这种方式移植文件系统与使用lv_fs_if组件的方式不同,当调用lv_init()函数的时候,就已经初始化SD卡并且挂载了文件系统。所以对于上一次实验,不需要更改main.c的任何内容,就实现了初始化SD卡和文件系统。

测试

1、将上次实验的main.c直接拿过来。在lv_init()之后使用vTaskDelay(pdMS_TO_TICKS(5));延时5ms,然后在初始化屏幕驱动(lvgl_driver_init();),不然可能出现花屏
2、在阿里巴巴矢量图标库中随便下载一个200像素的小图片,并将图片在LVGL官网提供的图片转化工具Online Image Converter中生成二进制.bin文件。并将改文件存储到SD卡中pictures文件夹中。
在这里插入图片描述
3、使用SquareLine studio工具1.2.2版本,拖拽一个image控件放到屏幕中。像下面这样。将生成的文件放在my_ui组建中(上一实验最后有讲)。

在这里插入图片描述
4、在main.c的ui_init()函数之后,调用lv_img_set_src(ui_Image2,“S:pictures/xxx.bin”);其中xxx是图片的名字。运行结果如下,表明SD卡文件系统移植成功:

在这里插入图片描述

完整的主文件代码(main.c)如下:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_freertos_hooks.h"
#include "freertos/semphr.h"
#include "esp_system.h"
#include "driver/gpio.h"
#include "lvgl/lvgl.h"
#include "lvgl_helpers.h"
#include <lv_demos.h>
#include <ui.h>


/*********************
 *      DEFINES
 *********************/
#define TAG "demo"
#define LV_TICK_PERIOD_MS 1

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void lv_tick_task(void *arg);
static void guiTask(void *pvParameter);


/**********************
 *   APPLICATION MAIN
 **********************/
void app_main() {
    xTaskCreatePinnedToCore(guiTask, "gui", 4096*2, NULL, 0, NULL, 1);
}

SemaphoreHandle_t xGuiSemaphore;

static void guiTask(void *pvParameter) {

    (void) pvParameter;
    xGuiSemaphore = xSemaphoreCreateMutex();
    lv_init();
    vTaskDelay(pdMS_TO_TICKS(5));
    lvgl_driver_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.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, LV_TICK_PERIOD_MS * 1000));
    /* 在这里更换自己的UI */
    ui_init();
    lv_img_set_src(ui_Image2,"S:pictures/cat.bin");
    while (1) {
        /* Delay 1 tick (assumes FreeRTOS tick is 10ms */
        vTaskDelay(pdMS_TO_TICKS(10));

        /* Try to take the semaphore, call lvgl related function on success */
        if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
            lv_task_handler();
            xSemaphoreGive(xGuiSemaphore);
       }
    }
    /* A task should NEVER return */
    free(buf1);
    vTaskDelete(NULL);
}

static void lv_tick_task(void *arg) {
    (void) arg;
    lv_tick_inc(LV_TICK_PERIOD_MS);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/429473.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Vant组件库二次封装组件(TS+Vue3.x环境)

1. 今天的需求是封装一个 Navigation Bar 导航栏组件&#xff0c;目的是给到App几乎所有的页面复用&#xff1a; 2. 因为之前的项目里使用过Vant组件库&#xff0c;笔者第一时间想到了Vant组件库中的 NavBar 组件&#xff0c;和当前App的需求匹配度很高。Vant组件库的 NavBar 组…

压箱底教程分享,手把手教会你如何注册target账号和下单

喜欢套利的朋友肯定都认识target这个平台吧&#xff0c;它是美国热门的综合性海淘网站之一。东哥近日收到私信有朋友向我请教在注册target账号时遇到的一些问题&#xff0c;所以今天东哥想跟大家分享的就是就是target账号注册教程和下单流程&#xff0c;让也想注册target账号的…

软考第五章 无线通信网

无线通信网 无线通信网包括面向语音通信的移动电话系统以及面向数据传输的无线局域网和无线广域网。 WiFI底层是如何传输数据的呢 1.移动通信 1.1 蜂窝通信系统 1980年中期&#xff0c;欧洲和日本都建立了第一代蜂窝移动电话系统。蜂窝网络把一个地理区域划分成若干个称为…

Vue2-黑马(五)

目录&#xff1a; &#xff08;1&#xff09;vue2-组件重用 &#xff08;2&#xff09;vue2-element ui安装 &#xff08;3&#xff09;vue2-ElementUI-table &#xff08;4&#xff09;Element-ui-分页pagination &#xff08;1&#xff09;vue2-组件重用 页面上有很多的…

PyTorch中的符号索引和函数索引用法

Pytorch中很多函数都采用的是函数式索引的思路&#xff0c;而且使用函数式索引对代码可读性会有很大提升。 张量的符号索引 张量也是有序序列&#xff0c;我们可以根据每个元素在系统内的顺序位置&#xff0c;来找出特定的元素&#xff0c;也就是索引。 一维张量的索引 一维…

离线安装JumpServer

官网操作手册&#xff1a; https://docs.jumpserver.org/zh/v3/installation/setup_linux_standalone/offline_install/ 环境要求&#xff1a;&#xff08;内存最小需要4G&#xff09; 架构图 安装部署 1、下载 JumpServer官网下载&#xff1a; https://community.fit2cloud…

定点数的二进制表示形式

定点数的二进制表示形式 文章目录定点数的二进制表示形式什么是定点数表示格式数值范围与分辨率转换python 转换定点数C 双精度浮点数转换为8位和16位定点数C 将定点数转回浮点数测试什么是定点数 在嵌入式系统中&#xff0c;为了降低运算复杂度&#xff0c;通常还会使用定点数…

有趣的数学之回文数

“回文”是指正读反读都能读通的句子&#xff0c;它是古今中外都有的一种修辞方式和文字游戏&#xff0c;如“我为人人&#xff0c;人人为我”等&#xff0c;最有名的莫过于“上海自来水来自海上&#xff0c;人过大佛寺佛大过人 ”。你们知道吗&#xff0c;在数学中也有这样一类…

30多份软件测试报告模板,如何写一份优秀测试报告模板流程

相信很多做软件测试的小伙伴在软件测试后期&#xff0c;都为软件测试报告总结花费了很多的精力&#xff0c;那么如何做好软件测试报告呢&#xff1f;一份优秀的测试报告又包含哪些内容呢&#xff1f; 测试报告的核心要素 一、测试结论 从测试工程师的专业角度分析&#xff0…

pytorch进阶学习(四):使用不同分类模型进行数据训练(alexnet、resnet、vgg等)

课程资源&#xff1a;5、帮各位写好了十多个分类模型&#xff0c;直接运行即可【小学生都会的Pytorch】_哔哩哔哩_bilibili 目录 一、项目介绍 1. 数据集准备 2. 运行CreateDataset.py 3. 运行TrainModal.py 4. 如何切换显卡型号 二、代码 1. CreateDataset.py 2.Train…

如何基于ChatGPT+Avatar搭建24小时无人直播间

0 前言 最近朋友圈以及身边很多朋友都在研究GPT开发&#xff0c;做了各种各样的小工具小Demo&#xff0c;AI工具用起来是真的香&#xff01;在他们的影响下&#xff0c;我也继续捣鼓GPT Demo&#xff0c;希望更多的开发者加入一起多多交流。 上一篇结合即时通 IM SDK捣鼓了一个…

因为这三个面试题,我与字节offer失之交臂

我一个朋友挑战3个月入职字节&#xff0c;一路过关斩将直到终面&#xff0c;着实把我惊了一把&#xff0c;可惜的是&#xff0c;他倒在了最后三个面试题上。 我很讶异&#xff0c;前面不是打得很好吗&#xff1f;怎么会在最后几题上犯错误呢&#xff1f; 朋友说&#xff1a;别…

电瓶隔离器工作原理与发展简史

电瓶隔离器(Battery Isolators)工作原理与发展简史 电池隔离器(英文&#xff1a;Battery Isolators)&#xff0c;又叫双电池隔离器、双电瓶隔离器、双电瓶保护器&#xff0c;还有叫双电池分离器的。 电瓶隔离器其实并没有真正的隔离&#xff0c;负极是始终连在一起的。房车、…

拓展系统命令

文章目录拓展系统命令使用方式拓展系统命令快速运行方法命令 - ZFASTRUN安全运行方法命令 - ZFASTSAFERUN快速运行Query方法命令 - ZFASTQUERY安全运行Query方法 命令 - ZSAFEQUARY防止调试时误将数据提交命令 - ZTRN在Terminal执行SQL语句命令 - ZSQL安全Global命令 - ZSAFEKI…

动态内存管理【上篇】

文章目录⚙️1.为什么存在动态内存分配⚙️2.动态内存函数的介绍&#x1f4ec;2.1. malloc函数&#x1f4ec;2.2. free函数&#x1f4ec;2.3. calloc函数&#x1f4ec;2.4. realloc函数⚙️3.常见的动态内存错误&#x1f512;3.1.对NULL指针的解引用操作&#x1f512;3.2.对动态…

二叉树(OJ)

单值二叉树&#xff08;力扣&#xff09; ---------------------------------------------------哆啦A梦的任意门------------------------------------------------------- 我们来看一下题目的具体要求&#xff1a; 既然我们都学了二叉树了&#xff0c;我们就应该学会如何去…

笔记:Java关于轻量级锁与重量级锁之间的问答

问题&#xff1a;如果在轻量级锁状态下出现锁竞争&#xff0c;不一定会直接升级为重量级锁&#xff0c;而是会先尝试自旋获取锁&#xff0c;那么有a b两个线程竞争锁&#xff0c;a成功获取锁了&#xff0c;b就一定失败&#xff0c;那么轻量级锁就一定升级为重量级锁&#xff0c…

基于Bazel + SQLFluff实现SQL lint

背景SQL进行版本化控制后&#xff0c;我们希望为SQL加入lint步骤。这样做的好处是我们可以在真正执行SQL前发现问题。本文中&#xff0c;我们通过Bazel执行SQLFluff[1]以实现SQL的lint。SQLFluff是一款使用Python语言使用的&#xff0c;支持SQL多方言的SQL lint工具。它的特点是…

设计模式-创建型模式之单例模式

6.单例模式6.1. 模式动机对于系统中的某些类来说&#xff0c;只有一个实例很重要&#xff0c;例如&#xff0c;一个系统中可以存在多个打印任务&#xff0c;但是只能有一个正在工作的任务&#xff1b;一个系统只能有一个窗口管理器或文件系统&#xff1b;一个系统只能有一个计时…

360安全卫士退出企业安全云模式

360安全卫士退出企业安全云模式前言360企业安全云关闭企业安全云提醒退出企业安全云模式前言 360安全卫士推出了企业安全云&#xff0c;并会给个人版用户进行推送&#xff0c;虽然可以关闭&#xff0c;但有可能会不小心升级为企业安全云&#xff0c;用户可能并不不习惯&#x…