1、SD卡介绍
SD卡有SD、MiniSD和microSD三种类型。可以访问如下链接查看更多规定。https://www.sdcard.org https://www.sdcard.org
最新SD卡规格的参数如下图所示:
其中脚位数对应实卡上的"金手指"数,不同的类型的卡的触点数量不同,访问的速度也不同。
SD卡常见的两种访问接口模式是SDIO模式和SPI模式。
如下图SD卡对于两种模式的引脚功能定义:
microSD卡引脚功能如下图:
注: S: 电源 I: 输入 O: 推挽输出 PP: 推挽
SD卡内部物理结构如下图所示:
SD卡的寄存器信息如下:
SD卡的OCR、CID、CSD、和SCR寄存器保存卡的配置信息。RCA寄存器保存着通信过程中
卡当前暂时分配的地址(只适合SDIO模式,SPI模式下没有)。CSR寄存器表示卡状态(Card Status)
和SSR寄存器SD状态(SD Status)寄存器保存着卡的状态(例如:是否写成功,通信的CRC校验是否
正确等),这两个寄存器的内容与通信模式(SD模式或SPI模式)相关。MMC卡没有SRC和SSR寄存
器。
2、SD卡命令
SD卡所有的命令都是固定长度48bit,6个Byte。格式如下图:
起始位:固定为0。
传输位:表示传输方向(主机 =1,从机=0)
命令号:占6个bit。
参数:占32个bit,4字节。
CRC7:7位CRC校验位。
结束位:占1bit。
SD卡的命令一共被分为了12类,分为Class0~Class11。比较重要的命令如下图:
3、卡模式
1.卡识别模式
SD卡系统定义了如下图几个工作模式。
对于开发来说只有卡识别和数据传输两种有效操作模式。
主机在系统复位后,处于卡识别模式。寻找总线上可用的SD卡。同时SD卡也处于卡识别模式,
直到被主机识别到,即SD卡在卡识别状态下收到CDM3(SEND_RCA)命令后,SD卡进入数据
传输模式,主机需在总线上所有卡被识别后进入数据传输模式。
SD卡识别模式状态转换如下图所示:
2、数据模式
SD卡有两种数据模式,一种是常规8位宽度,即一次按一字节传输。另一种是512字节传输。本次只使用
第一种单字节传输模式。每次传输从最低字节开始,每字节从最高位(MSB)开始发送。数据传输模式下
SD卡状态转换过程如下图所示:
4、SPI模式下SD卡初始化流程
SPI下SD卡的初始化时序如下图所示:
根据初始化时序图,SPI模式下SD卡的初始化主要有以下几个步骤
1、初始化主机与SD卡连接的硬件条件(MCU的SPI配置,IO配置)
2、主机拉低片选(CS)信号,SD卡上电延时(74个CLK)。
3、复位卡(CMD0),进入IDLE状态。
4、发送CMD8命令,检查是否支持2.0协议。
5、根据不同协议检查SD卡(命令包括:CMD55、ACMD41、CMD58和CMD1等)。
6、取消片选,发多8个CLK信号,结束初始化。
以上步骤就可以完成对SD卡的初始化。接下来就可以对SD卡进行读写数据。
SD卡扇区读取数据,具体过程如下:
1、发送CMD17。
2、接收卡响应R1。
3、接收数据起始令牌0xFE。
4、接收数据。
5、接收2个字节的CRC,如果不使用CRC,这两个字节在读取后可以丢掉。
6、禁止片选之后,发多8个CLK。
SD卡写数据的过程如下所示:
1、发送CMD24。
2、接收卡响应R1。
3、发送写数据起始令牌0xFE。
4、发送数据。
5、发送2字节的伪CRC。
6、禁止片选之后,发多8个CLK。
更多资料请查看官方资料 https://www.sdcard.org/
5、ESP32-S3模组的 SD/MMC概述
ESP32-S3模组支持两个外部卡,但不支持同时工作。其内部连接结构如下图所示:
6、ESP32代码编写
1、SPI外设初始化
void spi2_init(void)
{
esp_err_t ret = 0;
spi_bus_config_t spi_bus_conf = {0};
/* SPI总线配置 */
spi_bus_conf.miso_io_num = SPI_MISO_GPIO_PIN; /* SPI_MISO引脚 */
spi_bus_conf.mosi_io_num = SPI_MOSI_GPIO_PIN; /* SPI_MOSI引脚 */
spi_bus_conf.sclk_io_num = SPI_CLK_GPIO_PIN; /* SPI_SCLK引脚 */
spi_bus_conf.quadwp_io_num = -1; /* SPI写保护信号引脚,该引脚未使能 */
spi_bus_conf.quadhd_io_num = -1; /* SPI保持信号引脚,该引脚未使能 */
spi_bus_conf.max_transfer_sz = 320 * 240 * 2; /* 配置最大传输大小,以字节为单位 */
/* 初始化SPI总线 */
ret = spi_bus_initialize(SPI2_HOST, &spi_bus_conf, SPI_DMA_CH_AUTO); /* SPI总线初始化 */
ESP_ERROR_CHECK(ret); /* 校验参数值 */
// /* 添加SPI总线设备 */
}
2、SD卡初始化函数
/*
*@brief SD卡初始化
*@param 无
*retval esp_err_t
*/
esp_err_t sd_spi_init(void)
{
esp_err_t ret = ESP_OK;
if(Sky_SD_Handle != NULL)
{
spi_bus_remove_device(Sky_SD_Handle); /*移除SPI上的SD卡设备*/
if(mount_ret == ESP_OK)
{
esp_vfs_fat_sdcard_unmount(mount_point, card); //取消挂载
}
}
/*SPI驱动接口配置,SPISD卡时钟是20-25MHZ*/
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 20 * 1000 * 1000, //SPI时钟
.mode = 0, //SPI模式0
.spics_io_num = SD_NUM_CS, //片选引脚
.queue_size = 7, //事务队列尺寸7个
};
ret = spi_bus_add_device(SPI2_HOST, &devcfg, &Sky_SD_Handle); //添加SPI总线设备
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false, //若挂载失败:true会重新分区和格式化/false不会重新分区和格式化
.max_files = 5, //打开文件最大数量
.allocation_unit_size = 4 * 1024 * sizeof(uint8_t) //硬盘分区的大小
};
/*SD 卡参数配置*/
sdmmc_host_t host = {0};
host.flags = SDMMC_HOST_FLAG_SPI | SDMMC_HOST_FLAG_DEINIT_ARG; //定义主机属性的标志:SPI协议且可调用deinit函数
host.slot = SPI2_HOST; //使用SPI2端口
host.max_freq_khz = SDMMC_FREQ_DEFAULT; //主机支持的最大频率: 20000
host.io_voltage = 3.3f; //控制器使用的I/O电压
host.init = &sdspi_host_init; //用于初始化驱动程序的主机函数
host.set_bus_width = NULL; //设置总线宽度的主机功能
host.get_bus_width = NULL; //取总线宽度的主机函数
host.set_bus_ddr_mode = NULL; //设置DDR模式的主机功能
host.set_card_clk = &sdspi_host_set_card_clk; //设置板卡时钟频率的主机函数
host.do_transaction = &sdspi_host_do_transaction; //执行事务的主机函数
host.deinit_p = &sdspi_host_remove_device; //用于取消初始化驱动程序的主机函数
host.io_int_enable = &sdspi_host_io_int_enable; //启用SDIO中断线的主机功能
host.io_int_wait = &sdspi_host_io_int_wait; //等待SDIO中断线路激活的主机功能
host.command_timeout_ms = 0; //超时,默认为0
host.get_real_freq = &sdspi_host_get_real_freq;
host.set_cclk_always_on = NULL;
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;
mount_ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card); //挂载文件系统
ret |= mount_ret;
return ret;
}
3、获取SD卡内存大小测试函数
/*
*@brief 获取SD卡相关信息
*@param out_total_bytes:总大小
*@param out_free_bytes: 剩余带下
*@retval 无
*/
void sd_get_fatfs_usage(size_t *total_bytes, size_t *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(total_bytes != NULL)
{
*total_bytes = sd_total_KB;
}
if(free_bytes != NULL)
{
*free_bytes = sd_free_KB;
}
}