1. SD卡与SDIO基础概念解析
在嵌入式系统开发中,SD卡因其体积小、容量大、价格低廉等优势,成为最常用的外部存储设备之一。而SDIO(Secure Digital Input Output)则是用于访问SD卡的标准接口协议。理解SDIO驱动开发需要从硬件和协议两个层面入手。
1.1 SD卡家族演进史
SD卡技术自1999年诞生以来,已经发展出多个版本和规格:
- 标准SD卡:最早的版本,最大支持2GB容量
- SDHC卡(High Capacity):容量范围2GB-32GB,采用FAT32文件系统
- SDXC卡(eXtended Capacity):容量32GB-2TB,使用exFAT文件系统
- MicroSD/TF卡:尺寸更小的版本,广泛应用于移动设备
- eMMC:嵌入式MMC,直接焊接在PCB上的存储解决方案
1.2 SDIO接口特点
SDIO接口在SD卡标准基础上发展而来,具有以下关键特性:
- 多引脚配置:支持1-bit、4-bit和8-bit数据总线宽度
- 高速传输:理论速度可达50MHz(SD模式)或208MHz(UHS模式)
- 低功耗设计:支持多种省电模式
- 协议兼容性:向下兼容MMC设备
2. SD卡物理结构与寄存器详解
2.1 硬件组成架构
一个标准的SD卡包含五个主要功能单元:
- 存储单元:NAND Flash阵列,实际数据存储区域
- 存储接口:负责Flash的读写控制
- 电源管理:电压检测和功耗控制
- 卡控制器:处理主机命令和状态管理
- 接口驱动:物理层信号收发

2.2 关键寄存器解析
SD卡通过8个专用寄存器与主机交互:
| 寄存器 | 位宽 | 描述 | 访问命令 |
|---|---|---|---|
| CID | 128-bit | 卡唯一标识符 | CMD2, CMD10 |
| RCA | 16-bit | 相对卡地址 | CMD3 |
| CSD | 128-bit | 卡特性数据 | CMD9 |
| SCR | 64-bit | SD配置寄存器 | ACMD51 |
| OCR | 32-bit | 操作条件寄存器 | ACMD41 |
重点说明CSD寄存器:包含卡的关键参数,如:
- 最大数据传输速率
- 读写块长度
- 卡容量计算方法
- 擦除特性等
3. SDIO总线协议深度剖析
3.1 总线拓扑与信号定义
SDIO总线采用主从架构,包含以下信号线:
- CLK:主机提供的同步时钟
- CMD:双向命令/响应通道
- DAT0-DAT3:双向数据线(4-bit模式)
- VDD/VSS:电源和地线

3.2 数据传输模式
SDIO支持两种基本传输模式:
- 单线模式:仅使用DAT0,兼容性最好
- 4线模式:使用DAT0-DAT3,带宽提升4倍
数据传输以块(Block)为单位,标准块大小为512字节。每个数据块后跟随16位CRC校验码。
3.3 时钟域管理
SDIO总线有两个关键时钟频率:
- 识别模式:≤400kHz
- 数据传输模式:最高25MHz(标准)或50MHz(高速)
时钟配置公式:
code复制SDIO_CK = SDIOCLK / (2 * (CLKDIV + 1))
其中SDIOCLK通常等于AHB总线时钟(如72MHz)。
4. SDIO命令系统详解
4.1 命令格式规范
SDIO命令为固定48位格式:
| 字段 | 位宽 | 说明 |
|---|---|---|
| 起始位 | 1 | 固定为0 |
| 传输位 | 1 | 0-响应,1-命令 |
| 命令号 | 6 | CMD0~CMD63 |
| 参数 | 32 | 命令相关参数 |
| CRC7 | 7 | 校验码 |
| 结束位 | 1 | 固定为1 |
4.2 关键命令分类
4.2.1 基本命令(Class 0)
| 命令 | 编号 | 功能 |
|---|---|---|
| CMD0 | 0x00 | 复位卡到空闲状态 |
| CMD2 | 0x02 | 获取CID寄存器 |
| CMD3 | 0x03 | 发布相对地址(RCA) |
| CMD7 | 0x07 | 选择/取消选择卡 |
4.2.2 读写命令(Class 2/4)
| 命令 | 编号 | 功能 |
|---|---|---|
| CMD16 | 0x10 | 设置块长度 |
| CMD17 | 0x11 | 读取单块 |
| CMD18 | 0x12 | 读取多块 |
| CMD24 | 0x18 | 写入单块 |
| CMD25 | 0x19 | 写入多块 |
4.2.3 应用特定命令(ACMD)
ACMD命令需要先发送CMD55前缀:
| 命令 | 编号 | 功能 |
|---|---|---|
| ACMD6 | 0x06 | 设置总线宽度 |
| ACMD41 | 0x29 | 发送操作条件 |
| ACMD51 | 0x33 | 读取SCR寄存器 |
4.3 响应类型分析
SD卡支持7种响应格式,常见的有:
- R1(48位):正常响应,包含卡状态
- R2(136位):CID或CSD寄存器内容
- R3(48位):OCR寄存器内容
- R6(48位):发布的RCA地址
- R7(48位):接口条件响应
5. STM32 SDIO外设架构
5.1 功能模块组成
STM32的SDIO控制器包含:
- 适配器寄存器模块:配置和控制寄存器
- 控制单元:时钟和电源管理
- 命令通道:命令发送和响应接收
- 数据通道:数据传输管理
- 数据FIFO:32x32位缓冲器

5.2 时钟系统设计
SDIO涉及三个时钟域:
- AHB时钟(HCLK):CPU接口时钟
- SDIO适配器时钟(SDIOCLK):通常等于HCLK
- 卡时钟(SDIO_CK):输出到SD卡的时钟
时钟配置示例(HCLK=72MHz):
c复制// 初始化阶段(400kHz)
SDIO_InitStructure.SDIO_ClockDiv = 89; // 72MHz/(2*(89+1)) = 400kHz
// 数据传输阶段(24MHz)
SDIO_InitStructure.SDIO_ClockDiv = 1; // 72MHz/(2*(1+1)) = 18MHz
5.3 数据流控制机制
STM32使用两个状态机管理数据传输:
- CPSM(Command Path State Machine):命令通道状态机
- DPSM(Data Path State Machine):数据通道状态机
状态转换由硬件自动完成,开发者只需配置相关寄存器。
6. SD卡初始化流程详解
6.1 完整初始化序列
- 上电复位(CMD0)
- 检查接口条件(CMD8)
- 发送应用条件(ACMD41)
- 获取CID(CMD2)
- 设置RCA(CMD3)
- 获取CSD(CMD9)
- 选择卡(CMD7)
- 设置总线宽度(ACMD6)
6.2 关键代码实现
c复制SD_Error SD_Init(void)
{
// 硬件层初始化
SD_LowLevel_Init();
// 1. 上电序列
errorstatus = SD_PowerON();
if(errorstatus != SD_OK) return errorstatus;
// 2. 卡识别
errorstatus = SD_InitializeCards();
if(errorstatus != SD_OK) return errorstatus;
// 3. 获取卡信息
errorstatus = SD_GetCardInfo(&SDCardInfo);
if(errorstatus != SD_OK) return errorstatus;
// 4. 选择卡
errorstatus = SD_SelectDeselect((uint32_t)(SDCardInfo.RCA << 16));
if(errorstatus != SD_OK) return errorstatus;
// 5. 配置4位总线
errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);
return errorstatus;
}
6.3 电压检测流程
- 发送CMD8检查2.7-3.6V电压支持
- 循环发送ACMD41直到卡就绪
- 检查OCR寄存器中的电压范围位
c复制// 发送CMD8检查电压
SDIO_CmdInitStructure.SDIO_Argument = 0x000001AA; // 2.7-3.6V, 检查模式0xAA
SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;
SDIO_SendCommand(&SDIO_CmdInitStructure);
// 处理响应
if(CmdResp7Error() == SD_OK) {
CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0;
}
7. 数据读写操作实现
7.1 单块读操作流程
- 发送CMD17(单块读)
- 配置数据控制寄存器
- 等待数据准备就绪
- 从FIFO读取数据
- 检查CRC状态
c复制SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize)
{
// 1. 发送读命令
SDIO_CmdInitStructure.SDIO_Argument = ReadAddr;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK;
SDIO_SendCommand(&SDIO_CmdInitStructure);
// 2. 配置数据传输
SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_512b;
SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
SDIO_DataConfig(&SDIO_DataInitStructure);
// 3. 读取数据
for(i=0; i<BlockSize/4; i++) {
*(tempbuff+i) = SDIO_ReadData();
}
// 4. 检查状态
return errorstatus;
}
7.2 多块写操作优化
多块写入时需要注意:
- 使用CMD25替代CMD24
- 预擦除目标区域(CMD32+CMD33+CMD38)
- 合理设置DMA传输
- 最后发送CMD12终止传输
c复制// 预擦除设置
SDIO_CmdInitStructure.SDIO_Argument = StartAddr;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE_WR_BLK_START;
SDIO_SendCommand(&SDIO_CmdInitStructure);
SDIO_CmdInitStructure.SDIO_Argument = EndAddr;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE_WR_BLK_END;
SDIO_SendCommand(&SDIO_CmdInitStructure);
// 开始多块写入
SDIO_CmdInitStructure.SDIO_Argument = WriteAddr;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_MULT_BLOCK;
SDIO_SendCommand(&SDIO_CmdInitStructure);
8. 性能优化技巧
8.1 DMA传输配置
启用DMA可以显著提升传输效率:
- 配置DMA通道为外设到内存模式
- 设置数据对齐(32位最优)
- 合理设置DMA缓冲区大小
c复制void SD_DMA_Config(void)
{
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferDST;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = BufferSize/4;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_Init(DMAy_Channelx, &DMA_InitStructure);
}
8.2 中断管理策略
合理的中断处理能提高系统响应速度:
- 命令完成中断
- 数据传输完成中断
- FIFO阈值中断
- 错误中断
c复制void SDIO_IRQHandler(void)
{
// 命令响应中断
if(SDIO_GetITStatus(SDIO_IT_CMDREND) != RESET) {
SDIO_ClearITPendingBit(SDIO_IT_CMDREND);
// 处理命令完成
}
// 数据块传输完成
if(SDIO_GetITStatus(SDIO_IT_DATAEND) != RESET) {
SDIO_ClearITPendingBit(SDIO_IT_DATAEND);
// 处理数据完成
}
}
9. 常见问题排查指南
9.1 初始化失败排查
-
检查硬件连接
- 确认所有信号线连接正确
- 测量电源电压(2.7-3.6V)
- 检查上拉电阻(CMD/DAT线通常需要50kΩ上拉)
-
时钟配置验证
- 确保初始化阶段时钟≤400kHz
- 检查SDIO_CK引脚是否有波形输出
-
命令响应分析
- 使用逻辑分析仪捕获CMD线波形
- 检查每个命令的响应类型是否正确
9.2 数据传输错误处理
-
CRC错误
- 检查总线终端匹配
- 降低时钟频率测试
- 确认数据线数量配置正确
-
超时错误
- 增加SDIO_DataTimeOut值
- 检查卡是否处于忙状态(DAT0线为低)
-
DMA传输错误
- 确认内存缓冲区32位对齐
- 检查DMA通道优先级设置
- 验证缓冲区大小是否为块大小的整数倍
10. 实战开发建议
10.1 硬件设计要点
-
PCB布局建议
- SDIO信号线长度匹配(±5mm)
- 避免与高频信号平行走线
- 电源引脚放置去耦电容(0.1μF+10μF)
-
ESD防护
- 在连接器附近放置TVS二极管
- 考虑使用带ESD保护的SD卡座
10.2 软件优化技巧
-
文件系统集成
- 推荐使用FatFS等轻量级文件系统
- 合理设置簇大小(通常4K-32K)
- 启用长文件名支持需预留足够RAM
-
磨损均衡策略
- 避免频繁写入固定地址
- 实现简单的地址映射算法
- 定期检查坏块
-
功耗管理
- 空闲时关闭SDIO时钟
- 使用CMD7取消选择不活动的卡
- 考虑使用睡眠模式
通过深入理解SDIO协议和STM32硬件特性,开发者可以构建稳定高效的存储解决方案。实际项目中,建议结合具体需求选择合适的配置参数,并通过充分的测试验证系统可靠性。
