作为中间层,利用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;
}