作为中间层,利用SPI提供的函数,加上协议完成SD操作

static void SPI4_SetPrescaler(uint16_t prescaler);//设置SPI速度

static uint8_t SPI4_RW(uint8_t dat);//SPI读写

static void SPI4_RxMulti(uint8_t *buff, uint32_t len);//MCU读SPI外设,内部调用SPI4_RW

static void SPI4_TxMulti(const uint8_t *buff, uint32_t len);//MCU写SPI外设,内部调用SPI4_RW

SD是否就绪

不断发送0xFF并读取MISO:

​ 读到0xFF:卡已经就绪 ​ 其他值:卡仍然忙

/* 等待卡就绪: 返回 0=就绪 1=超时 */
static int SD_WaitReady(uint32_t wt)
{
    uint8_t d;
    do {
        d = SPI4_RW(0xFF);
    } while (d != 0xFF && --wt);
    return (d == 0xFF) ? 0 : 1;
}

选中SD卡

/* 选卡并等待就绪, 返回 1=成功 0=失败
 * 关键: 先拉高 CS 再拉低, 保证每个命令事务都有 CS 下降沿。
 * SD SPI 规范要求每个命令以 CS 下降沿开始; 若 CS 持续为低
 * (背靠背命令), 部分 v2 卡的命令状态机不重置, CMD8/ACMD41 无响应。 */
static int SD_Select(void)
{
    SD_CS_HIGH();
    SPI4_RW(0xFF);      /* CS 高期间 8 时钟, 让卡释放 MISO */
    SD_CS_LOW();
    SPI4_RW(0xFF);      /* CS 低后 8 时钟, 卡开始驱动 MISO */
    if (SD_WaitReady(SD_READ_TIMEOUT)) {
        SD_Deselect();
        return 0;
    }
    return 1;
}

取消选中SD卡

static void SD_Deselect(void)
{
    SD_CS_HIGH(); // 拉高CS
    SPI4_RW(0xFF);   /* 额外 8 时钟释放 DO */
}

发送SD命令

SD卡SPI命令固定为6字节:

字节0:01 + 命令号
字节1:参数[31:24]
字节2:参数[23:16]
字节3:参数[15:8]
字节4:参数[7:0]
字节5:CRC7 + 结束位1
/* 发送命令, 返回 R1 */
static uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg)
{
    uint8_t buf[6];
    uint8_t r1;
    uint32_t n;

    if (SD_Select() == 0) return 0xFF;

    buf[0] = (uint8_t)(0x40 | cmd);
    buf[1] = (uint8_t)(arg >> 24);
    buf[2] = (uint8_t)(arg >> 16);
    buf[3] = (uint8_t)(arg >> 8);
    buf[4] = (uint8_t)(arg);
    buf[5] = 0x01;
    if (cmd == CMD0)      buf[5] = 0x95;   /* CMD0 CRC */
    else if (cmd == CMD8) buf[5] = 0x87;   /* CMD8 CRC */

    SPI4_TxMulti(buf, 6);

    if (cmd == CMD12) SPI4_RW(0xFF);       /* CMD12 后补 1 字节 */

    n = 1000;//超时时间1000ms
    do {
        r1 = SPI4_RW(0xFF);
    } while ((r1 & 0x80) && --n);

    return r1;
}