图37.3.1.1 SD卡实验程序流程图
37.3.2 SD卡函数解析
ESP-IDF提供了一套API来配置SD卡。要使用此功能,需要导入必要的头文件:
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h"
#include "spi.h"
接下来,作者将介绍一些常用的ESP32-S3中的SD卡函数,这些函数的描述及其作用如下:
1,挂载SD卡
该函数用给定的配置,挂载SD卡,该函数原型如下所示:
esp_err_t esp_vfs_fat_sdspi_mount(const char* base_path,
const sdmmc_host_t* host_config_input,
const sdspi_device_config_t* slot_config,
const esp_vfs_fat_mount_config_t*mount_config,
sdmmc_card_t** out_card);
该函数的形参描述如下表所示:
参数 | 描述 |
| 应该注册分区的路径(例如“/sdcard”) |
| 指向描述SDMMC主机的结构的指针。此结构可以使用SDSPI_HOST_DEFAULT宏初始化。 |
| 指向具有插槽配置的结构的指针,对于SPI外设,将指针传递到使用sdspi_device_config_DEFAULT初始化的sdspi_device_config_t结构。 |
| 指向具有用于安装FATFS的额外参数的结构的指针 |
| 如果不是NULL,指向卡片信息结构的指针将通过此参数返回。 |
表37.3.2.1 esp_vfs_fat_sdspi_mount()函数形参描述
该函数的返回值描述,如下表所示:
| |
| |
| 如果已经调用了esp_vfs_fat_sdmmc_mount |
| 如果无法分配内存 |
| 如果分区无法安装,则来自SDMMC或SPI驱动程序、SDMMC协议或FATFS驱动程序的其他错误代码 |
表37.3.2.2 函数esp_vfs_fat_sdspi_mount ()返回值描述
该函数使用sdmmc_host_t类型的结构体变量传入,该结构体的定义如下所示:
| 成员变量 | |
| | 定义主机属性的标志:SPI协议且可调用deinit函数,有如下值可以配置: SDMMC_HOST_FLAG_1BIT SDMMC_HOST_FLAG_4BIT SDMMC_HOST_FLAG_8BIT SDMMC_HOST_FLAG_SPI SDMMC_HOST_FLAG_DDR SDMMC_HOST_FLAG_DEINIT_ARG |
| 使用SPI2端口 ,有如下值可以配置: SPI1_HOST SPI2_HOST |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
表37.3.2.3 sdmmc_host_t结构体参数值描述
完成上述结构体参数配置之后,可以将结构传递给 esp_vfs_fat_sdspi_mount () 函数,用以实例化SD卡,挂载文件系统。
2,取消挂载SD卡
该函数用于取消挂载SD卡,该函数原型如下所示:
esp_err_t esp_vfs_fat_sdcard_unmount(const char* base_path, sdmmc_card_t *card);
该函数的形参描述如下表所示:
参数 | 描述 |
| 应该注册分区的路径(例如“/sdcard”) |
| SD / MMC卡结构 |
表37.3.2.4 esp_vfs_fat_sdcard_unmount ()函数形参描述
该函数的返回值描述,如下表所示:
| |
ESP_OK | |
ESP_ERR_INVALID_ARG | 如果card参数未注册 |
ESP_ERR_INVALID_STATE | 如果尚未调用esp_vfs_fat_sdmmc_mount |
表37.3.2.5 函数esp_vfs_fat_sdcard_unmount ()返回值描述
37.3.3 SD卡驱动解析
在IDF版的26_sd例程中,作者在26_sd \components\BSP路径下新增了一个SDIO文件夹,用于存放spi_sdcard.c、spi_sdcard.h这两个文件。其中,spi_sdcard.h文件负责声明SDIO相关的函数和变量,而spi_sdcard.c文件则实现了SDIO的驱动代码。下面,我们将详细解析这两个文件的实现内容。
1,spi_sdcard.h文件
/* 引脚定义 */
#define SD_NUM_CS GPIO_NUM_2
#define MOUNT_POINT "/0:"
1, spi_sdcard.c文件
sd_pi_init的设计就比较简单了,我们只需要填充SPI结构体的控制句柄,然后添加SPI总线设备并配置文件系统挂载即可,根据外设的情况,我们可以设置SPI的使用端口为SPI2端口。
spi_device_handle_t MY_SD_Handle;
/**
* @retval esp_err_t
*/
esp_err_t sd_spi_init(void)
{
esp_err_t ret = ESP_OK;
/* SD / MMC卡结构 */
sdmmc_card_t *card;
/* 挂载点/根目录 */
const char mount_point[] = MOUNT_POINT;
/* SPI驱动接口配置,SPISD卡时钟是20-25MHz */
spi_device_interface_config_t devcfg = {
/* SPI时钟 */
.clock_speed_hz = 20 * 1000 * 1000,
/* SPI模式0 */
.mode = 0,
/* 片选引脚 */
.spics_io_num = SD_NUM_CS,
/* 事务队列尺寸 7个 */
.queue_size = 7,
};
/* 添加SPI总线设备 */
ret = spi_bus_add_device(SPI2_HOST, &devcfg, &MY_SD_Handle);
/* 文件系统挂载配置 */
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
/* 如果挂载失败:true会重新分区和格式化/false不会重新分区和格式化 */
.format_if_mount_failed = false,
/* 打开文件最大数量 */
.max_files = 5,
/* 硬盘分区簇的大小 */
.allocation_unit_size = 16 * 1024,
};
/* SD卡参数配置 */
sdmmc_host_t host = {0};
/* 定义主机属性的标志:SPI协议且可调用deinit函数 */
host.flags = SDMMC_HOST_FLAG_SPI | SDMMC_HOST_FLAG_DEINIT_ARG;
/* 使用SPI2端口 */
host.slot = SPI2_HOST;
/* 主机支持的最大频率:20000 */
host.max_freq_khz = SDMMC_FREQ_DEFAULT;
/* 控制器使用的I/O电压 */
host.io_voltage = 3.3f;
/* 用于初始化驱动程序的主机函数 */
host.init = &sdspi_host_init;
/* 设置总线宽度的主机功能 */
host.set_bus_width = NULL;
/* 取总线宽度的主机函数 */
host.get_bus_width = NULL;
/* 设置DDR模式的主机功能 */
host.set_bus_ddr_mode = NULL;
/* 设置板卡时钟频率的主机函数 */
host.set_card_clk = &sdspi_host_set_card_clk;
/* 执行事务的主机函数 */
host.do_transaction = &sdspi_host_do_transaction;
/* 用于取消初始化驱动程序的主机函数 */
host.deinit_p = &sdspi_host_remove_device;
/* 启用SDIO中断线的主机功能 */
host.io_int_enable = &sdspi_host_io_int_enable;
/* 等待SDIO中断线路激活的主机功能 */
host.io_int_wait = &sdspi_host_io_int_wait;
/* 超时,默认为0*/
host.command_timeout_ms = 0;
/* SD卡引脚配置 */
sdspi_device_config_t slot_config = {0};
slot_config.host_id = host.slot;
slot_config.gpio_cs = SD_NUM_CS;
slot_config.gpio_cd = GPIO_NUM_NC;
slot_config.gpio_wp = GPIO_NUM_NC;
slot_config.gpio_int = GPIO_NUM_NC;
/* 挂载文件系统 */
ret = esp_vfs_fat_sdspi_mount(mount_point,
&host,
&slot_config,
&mount_config,
&card);
if (ret != ESP_OK)
{
spi_bus_remove_device(MY_SD_Handle); /* 移除SPI上的SD卡设备 */
return ret;
}
return ret;
}
接下来这个函数用于获取SD卡相关信息,比如容量大小,以及剩余容量,如下所示:
/**
* @brief 获取SD卡相关信息
* @param out_total_bytes:总大小
* @param out_free_bytes:剩余大小
* @retval 无
*/
void sd_get_fatfs_usage(size_t *out_total_bytes, size_t *out_free_bytes)
{
FATFS *fs;
size_t free_clusters;
int res = f_getfree("0:", (size_t *)&free_clusters, &fs);
assert(res == FR_OK);
size_t total_sectors = (fs->n_fatent - 2) * fs->csize;
size_t free_sectors = free_clusters * fs->csize;
size_t sd_total = total_sectors / 1024;
size_t sd_total_KB = sd_total * fs->ssize;
size_t sd_free = free_sectors / 1024;
size_t sd_free_KB = sd_free*fs->ssize;
/* 假设总大小小于4GiB,对于SPI Flash应该为true */
if (out_total_bytes != NULL)
{
*out_total_bytes = sd_total_KB;
}
if (out_free_bytes != NULL)
{
*out_free_bytes = sd_free_KB;
}
}
37.3.4 CMakeLists.txt文件
打开本实验BSP下的CMakeLists.txt文件,其内容如下所示:
set(src_dirs
IIC
LCD
LED
SDIO
SPI
XL9555)
set(include_dirs
IIC
LCD
LED
SDIO
SPI
XL9555)
set(requires
driver
fatfs)
idf_component_register(SRC_DIRS ${src_dirs}
INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})
component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
上述的红色SDIO驱动以及fatfs依赖库需要由开发者自行添加,以确保SDIO驱动能够顺利集成到构建系统中。这一步骤是必不可少的,它确保了SDIO驱动的正确性和可用性,为后续的开发工作提供了坚实的基础。
37.3.5 实验应用代码
打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。
i2c_obj_t i2c0_master;
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
esp_err_t ret;
size_t bytes_total, bytes_free; /* SD卡的总空间与剩余空间 */
ret = nvs_flash_init(); /* 初始化NVS */
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();
}
led_init(); /* 初始化LED */
i2c0_master = iic_init(I2C_NUM_0); /* 初始化IIC0 */
spi2_init(); /* 初始化SPI */
xl9555_init(i2c0_master); /* 初始化IO扩展芯片 */
lcd_init(); /* 初始化LCD */
lcd_show_string(30, 50, 200, 16, 16, "ESP32-S3", RED);
lcd_show_string(30, 70, 200, 16, 16, "FATFS TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
while (sd_spi_init()) /* 检测不到SD卡 */
{
lcd_show_string(30, 110, 200, 16, 16, "SD Card Error!", RED);
vTaskDelay(500);
lcd_show_string(30, 130, 200, 16, 16, "Please Check! ", RED);
vTaskDelay(500);
}
lcd_show_string(30, 110, 200, 16, 16, "SD Card OK!", RED);
lcd_show_string(30, 130, 200, 16, 16, "Total: MB", RED);
lcd_show_string(30, 150, 200, 16, 16, "Free : MB", RED);
sd_get_fatfs_usage(&bytes_total, &bytes_free);
lcd_show_num(80, 130,(int)bytes_total / 1024,5,16,BLUE);
lcd_show_num(80, 150,(int)bytes_free / 1024,5,16,BLUE);
while (1)
{
LED_TOGGLE();
vTaskDelay(500);
}
}
可以看到,本实验的应用代码中,通过初始化SD卡判断与SD卡的连接是否有误,SD卡初始化成功后便通过函数sd_get_fatfs_usage()获取容量等信息,同时也在LCD上显示了SD的容量信息。
37.4 下载验证
程序下载到开发板后,可以看到SPILCD显示SD卡目录下的test.txt文件的内容,如下图所示。如图37.4.1: