一、简介
SD 卡(Secure Digital Memory Card) 在我们生活中已经非常普遍了,控制器对 SD 卡进行
读写通信操作一般有两种通信接口可选,一种是 SPI 接口,另外一种就是 SDIO 接口。
SDIO 全称是安全数字输入/输出接口,多媒体卡(MMC)、SD 卡、SD I/O 卡都有 SDIO 接口。
MMC 卡可以说是 SD 卡的前身,现阶段已经用得很少。
二、API说明
以下 SDMMC 接口位于 driver/include/driver/sdmmc_host.h。
2.1 SDMMC_HOST_DEFAULT
SDMMC_HOST_DEFAULT()
SDMMC 外设的默认sdmmc_host_t结构初始值设定项。
使用 SDMMC 外设,启用 4 位模式,最大频率设置为 20MHz
2.2 SDMMC_SLOT_CONFIG_DEFAULT
SDMMC_SLOT_CONFIG_DEFAULT()
定义 SDMMC 主机插槽默认配置的宏
以下 FAT 文件系统接口位于 fatfs/vfs/esp_vfs_fat.h。
2.3 esp_vfs_fat_sdmmc_mount
2.4 esp_vfs_fat_sdcard_unmount
以下 SDMMC 接口位于 sdmmc/include/sdmmc_cmd.h。
2.5 sdmmc_card_print_info
三、编程流程
- 使用“一体式”
esp_vfs_fat_sdmmc_mount()
函数进行初始化:- 初始化SDMMC外设;
- 检测并初始化连接到SD/MMC插槽1的卡(HS2_CMD、HS2_CLK、HS2_D0、HS2_D1、HS2_D2、HS2_D3线);
- 使用FATFS库安装FAT文件系统(如果无法安装文件系统,则使用格式化卡);
- 在VFS中注册FAT文件系统,以使用C标准库和POSIX函数。
- 打印有关SD卡的信息,例如名称、类型、容量和支持的最大频率。
- 使用
fopen()
创建一个文件,并使用fprintf()
写入该文件。 - 重命名该文件。重命名之前,请使用
stat()
函数检查目标文件是否已存在,并使用unlink()
函数将其删除。 - 打开重命名的文件进行读取,读回该行,并将其打印到终端。
四、硬件连接
我使用的是 ESP32-LyraT V4.3
开发板
以下功能可通过
功能 DIP 开关
进行选择:
- 在 1-wire 模式下启用 MicroSD 卡
拨码开关 | 位置 |
---|---|
1 | OFF |
2 | OFF |
3 | OFF |
4 | OFF |
5 | OFF |
6 | OFF |
7 | OFF① |
8 | N/A |
①:通过拨动 DIP SW 7 ON可以启用AUX 输入检测。请注意,系统上电时不应插入AUX 输入信号引脚。否则 ESP32 可能无法正常启动。
在这种模式下:
- JTAG功能不可用
- Vol- touch 按钮可用于 API
-
在 4-wire 模式下启用 MicroSD 卡
(例程默认使用的模式)
拨码开关 | 位置 |
---|---|
1 | ON |
2 | ON |
3 | OFF |
4 | OFF |
5 | OFF |
6 | OFF |
7 | OFF |
8 | N/A |
在这种模式下:
- JTAG功能不可用
- Vol- touch 按钮可用于 API
- 来自 API 的AUX 输入检测不可用
五、示例代码
根据 examples\storage\sd_card 中的例程修改
默认是4-wire 模式,1-wire 模式修改slot_config.width = 1;
/* SD card and FAT filesystem example.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include "esp_vfs_fat.h"
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "sdmmc_cmd.h"
#include "sdkconfig.h"
#include "driver/sdmmc_host.h"
static const char *TAG = "example";
#define MOUNT_POINT "/sdcard"
// This example can use SDMMC and SPI peripherals to communicate with SD card.
// By default, SDMMC peripheral is used.
void app_main(void)
{
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 = true,
.max_files = 5,
.allocation_unit_size = 16 * 1024
};
sdmmc_card_t* card;
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 SDMMC peripheral");
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
// 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.
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
// To use 1-line SD mode, uncomment the following line:
// slot_config.width = 1;
// GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
// Internal pull-ups are not sufficient. However, enabling internal pull-ups
// does make a difference some boards, so we do that here.
gpio_set_pull_mode(15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes
gpio_set_pull_mode(2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes
gpio_set_pull_mode(4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only
gpio_set_pull_mode(12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only
gpio_set_pull_mode(13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes
// 初始化 SD卡
ret = esp_vfs_fat_sdmmc_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 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;
}
// 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.
ESP_LOGI(TAG, "Opening file");
FILE* f = fopen(MOUNT_POINT"/hello.txt", "w");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for writing");
return;
}
fprintf(f, "Hello %s!\n", card->cid.name);
fclose(f);
ESP_LOGI(TAG, "File written");
// Check if destination file exists before renaming
struct stat st;
if (stat(MOUNT_POINT"/foo.txt", &st) == 0) {
// Delete it if it exists
unlink(MOUNT_POINT"/foo.txt");
}
// Rename original file
ESP_LOGI(TAG, "Renaming file");
if (rename(MOUNT_POINT"/hello.txt", MOUNT_POINT"/foo.txt") != 0) {
ESP_LOGE(TAG, "Rename failed");
return;
}
// Open renamed file for reading
ESP_LOGI(TAG, "Reading file");
f = fopen(MOUNT_POINT"/foo.txt", "r");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for reading");
return;
}
char line[64];
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);
// All done, unmount partition and disable SDMMC or SPI peripheral
esp_vfs_fat_sdcard_unmount(mount_point, card);
ESP_LOGI(TAG, "Card unmounted");
}
查看打印:
• 由 Leung 写于 2021 年 8 月 13 日
• 参考:ESP32笔记(4) SD卡的读写