环境
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)进行关联,使得应用程序可以通过标准文件操作接口(如fopen
、fwrite
等)访问SD卡中的文件12。具体到esp_vfs_fat_sdmmc_mount_config_t
配置中的参数。
卡槽:在ESP32中,卡槽0(Slot 0)和卡槽1(Slot 1)是硬件上两个独立的SD/MMC控制器接口。