ESP32上SD卡的读写操作

环境

1、ubuntu虚拟机 搭建ESP32_IDF

2、VSCODE远程连接

3、C语言开发

参考链接:

第十五讲 外设教程——读写MicroSD卡_哔哩哔哩_bilibili

自我学习留用

① 概述:

本章学习使用ESP32的sdmmc组件读写MicroSD卡

sdmmc组件就是ESP-IDF中封装的一组函数,包含了对存储卡进行的各种操作。

② 硬件原理图

硬件分析:除去VDD,VSS,GND我们发现还剩下DATA0、DATA1、DATA2、DATA3四个数据引脚。CLK是时钟引脚。

CMD是命令引脚, 用于主机(如单片机)向MicroSD卡发送控制命令,例如初始化、读写请求、状态查询等。

这个卡既可以使用SPI模式(一位数据传输),也可以使用SDIO进行4位数据的传输。老师的工程是SDIO的模式。

注意:大家可以看到,这个卡的四个数据线都使用了上拉电阻。但是ESP32的GPIO12有个规定,当它上电是高电平时,ESP32会给FLASH提供1.8v供电,上电是低电平时,ESP32才会给FLASH提供3.3v供电。因此如果你需要FLASH工作在3.3V域,而由恰好因为外界因素比如这里的上拉。则采样乐鑫给出的解决方案,烧录ESP32内部熔断丝选择器efuse,强制固化启动方式为3.3V.

烧录步骤:

1、切换到ESP-IDF目录

2、执行命令

components/esptool_py/esptool/espfuse.py set_flash_voltage 3.3V

核对下老师的图片,我没试过奥

3、确认

BURN

注意:IO2如果被拉高,ESP32进不了boot模式,则ESP32无法正常下载程序。

乐鑫:将GPIO2和GPIO0短接到一起,就可以

下载工具会在下载前拉低GPIO0 ,GPIO2会被一同拉低

③ 源码分析

例程目的:对MICROSD卡进行读写操作。有些名词看不懂的往下看④名词解释

#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h"
​
#define EXAMPLE_MAX_CHAR_SIZE    64
​
static const char *TAG = "example";
​
#define MOUNT_POINT "/sdcard"   //挂载点名称
​
​
static esp_err_t s_example_write_file(const char *path, char *data)
{
    ESP_LOGI(TAG, "Opening file %s", path);
    FILE *f = fopen(path, "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return ESP_FAIL;
    }
    fprintf(f, data);
    fclose(f);
    ESP_LOGI(TAG, "File written");
​
    return ESP_OK;
}
​
static esp_err_t s_example_read_file(const char *path)
{
    ESP_LOGI(TAG, "Reading file %s", path);
    FILE *f = fopen(path, "r");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for reading");
        return ESP_FAIL;
    }
    char line[EXAMPLE_MAX_CHAR_SIZE];
    fgets(line, sizeof(line), f);
    fclose(f);
​
    // strip newline
    char *pos = strchr(line, '\n');
    if (pos) {
        *pos = '\0';
    }
    ESP_LOGI(TAG, "Read from file: '%s'", line);
​
    return ESP_OK;
}
​
void app_main(void)
{
    esp_err_t ret;//返回esp错误码
    
    /*我的理解:
    1、这个执行格式化的意思其实就理解成挂载失败是否重新挂载。
    2、最大可打开MICROSD中的文件数,这边老师的示例就一个文件简单读取,所以设置的5够用。
    3、4*1024就是4KB的意思,这个设置小了读写速度会变慢。比如大小只有1kb,结果写入一个2Kb的数据,就要读写两次。但是越大不一定越好,如果现在我设置了4KB,但是每次只读取1KB,则会造成3KB的浪费*/
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = true,     //挂载失败是否执行格式化
        .max_files = 5,                     //最大可打开文件数
        .allocation_unit_size = 4 * 1024   //执行格式化时的分配单元大小(分配单元越大,读写越快)
    };
    
    sdmmc_card_t *card;
    const char mount_point[] = MOUNT_POINT;
    ESP_LOGI(TAG, "Initializing SD card");
​
    ESP_LOGI(TAG, "Using SDMMC peripheral");
    
    
    //默认配置,速度20MHz,使用卡槽1
    //我的理解:老师说卡槽0和FLASH有硬件冲突 ,所以选用卡槽1
    sdmmc_host_t host = SDMMC_HOST_DEFAULT();
​
    //默认的IO管脚配置。
    /*
      我的理解:老师的这个硬件设计好了,按照ESP32的默认模式设置的引脚的连接,因此不更改,可以看到在
      下面, slot_config.clk这些设置都被包含在了if 0里。
    */
    sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
​
    //4位数据
    //我的理解:老师说使用SDIO模式,一次传输4位数据
    slot_config.width = 4;
​
    //不适用通过IO矩阵进行映射的管脚,只使用默认支持的SDMMC管脚,可以获得最大性能
    #if 0
    slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
    slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
    slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
    slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
    slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
    slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;
    #endif
    // Enable internal pullups on enabled pins. The internal pullups
    // are insufficient however, please make sure 10k external pullups are
    // connected on the bus. This is for debug / example purpose only.
    //管脚启用内部上拉
    slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
​
    ESP_LOGI(TAG, "Mounting filesystem");
    
    //返回SD卡的句柄  挂载SD卡 相当于是插入卡槽
    /*我的理解:刚刚关于引脚,卡槽的配置设置成功了 现在就是把硬件连接到文件系统上,我们直接通过mount_point这个路径/sdcard就可以找到。*/
    ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
    /*
      mount_point:挂载点路径(类似 Windows 的盘符,例如 /sdcard)。
      host:SDMMC 硬件控制器配置(默认用 SDMMC_HOST_DEFAULT())。
      slot_config:SD 卡引脚配置(比如 CLK、CMD、D0-D3 引脚)。
      mount_config:前面提到的挂载配置。
      card:保存 SD 卡信息的指针
    */
    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem. ");
        } 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_LOGI(TAG, "Filesystem mounted");
​
    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);
​
    // Use POSIX and C standard library functions to work with files:
​
    // First create a file.
    const char *file_hello = MOUNT_POINT"/hello.txt";
    char data[EXAMPLE_MAX_CHAR_SIZE];
    snprintf(data, EXAMPLE_MAX_CHAR_SIZE, "%s %s!\n", "Hello", card->cid.name);
    ret = s_example_write_file(file_hello, data);
    if (ret != ESP_OK) {
        return;
    }
​
    const char *file_foo = MOUNT_POINT"/foo.txt";
    // Check if destination file exists before renaming
    struct stat st;
    if (stat(file_foo, &st) == 0) {
        // Delete it if it exists
        unlink(file_foo);
    }
​
    // Rename original file
    ESP_LOGI(TAG, "Renaming file %s to %s", file_hello, file_foo);
    if (rename(file_hello, file_foo) != 0) {
        ESP_LOGE(TAG, "Rename failed");
        return;
    }
​
    ret = s_example_read_file(file_foo);
    if (ret != ESP_OK) {
        return;
    }
​
    // Format FATFS
    #if 0
    ret = esp_vfs_fat_sdcard_format(mount_point, card);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to format FATFS (%s)", esp_err_to_name(ret));
        return;
    }
​
    if (stat(file_foo, &st) == 0) {
        ESP_LOGI(TAG, "file still exists");
        return;
    } else {
        ESP_LOGI(TAG, "file doesnt exist, format done");
    }
    #endif
    const char *file_nihao = MOUNT_POINT"/nihao.txt";
    memset(data, 0, EXAMPLE_MAX_CHAR_SIZE);
    snprintf(data, EXAMPLE_MAX_CHAR_SIZE, "%s %s!\n", "Nihao", card->cid.name);
    ret = s_example_write_file(file_nihao, data);
    if (ret != ESP_OK) {
        return;
    }
​
    //Open file for reading
    ret = s_example_read_file(file_nihao);
    if (ret != ESP_OK) {
        return;
    }
​
    // All done, unmount partition and disable SDMMC peripheral
    esp_vfs_fat_sdcard_unmount(mount_point, card);
    ESP_LOGI(TAG, "Card unmounted");
}
​

④ 名词解释

挂载: ESP32的SD卡驱动中,挂载(mount)指的是将SD卡的存储系统(如FAT文件系统)与设备的虚拟文件系统(VFS)进行关联,使得应用程序可以通过标准文件操作接口(如fopenfwrite等)访问SD卡中的文件12。具体到esp_vfs_fat_sdmmc_mount_config_t配置中的参数。

卡槽:在ESP32中,卡槽0(Slot 0)和卡槽1(Slot 1)是硬件上两个独立的SD/MMC控制器接口。

⑤ 效果

### ESP32 SD文件夹读写操作 对于ESP32上的SD文件夹读写操作,可以采用两种主要方法来实现:通过SPI接口或者SDMMC接口。通常情况下,SDMMC接口的速度更快一些[^2]。 下面是一个基于Arduino环境下的示例代码,用于展示如何利用SPI接口完成基本的文件创建、打开、关闭以及向其中写入数据的操作: ```cpp #include "FS.h" #include "SD_MMC.h" void setup() { Serial.begin(115200); if (!SD_MMC.begin()) { // 初始化SD Serial.println("Card Mount Failed"); return; } File file = SD_MMC.open("/test.txt", FILE_WRITE); // 打开或新建一个名为 test.txt 的文件 if (file) { file.print("Hello, world!"); // 向文件中写入字符串 file.close(); // 关闭文件 Serial.println("File written successfully."); } else { Serial.println("Error opening or creating file."); } } void loop() {} ``` 此段代码展示了最基础的文字记录功能,在实际应用当中可能还需要考虑更多细节,比如错误处理机制等。如果想要进一步管理目录结构,则可以通过`mkdir()`函数建立新目录;使用`openDir()`遍历现有目录内的项目;调用`remove()`删除指定路径下的文件或空目录;而重命名则可通过`rename()`达成目的。 为了更好地理解这些API的具体用途及其参数设置,请参阅Arduino官方文档关于SD库的部分说明。 #### 创建子目录并保存文件至该位置的例子如下所示: ```cpp if(SD_MMC.mkdir("/myfolder")){ // 尝试创建一个新的子目录叫做 myfolder Serial.println("Directory created!"); } else { Serial.println("Failed to create directory!"); } // 接下来我们将尝试在一个特定的位置下存储我们的文件 String path = "/myfolder/test_in_folder.txt"; File file = SD_MMC.open(path.c_str(), FILE_WRITE); if(file){ file.print("This is a text inside folder"); file.close(); Serial.println("File saved into specific location."); }else{ Serial.println("Could not save file into specified location."); } ``` 上述例子演示了怎样在根目录之外的地方放置文件,并且介绍了几个重要的命令以便于更灵活地控制文件系统的布局。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值