ESP32驱动SD卡实战

1、SD卡介绍

 SD卡有SD、MiniSD和microSD三种类型。可以访问如下链接查看更多规定。https://www.sdcard.org icon-default.png?t=N7T8https://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;
    }
}

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青涩天空

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值