SDIO接口协议
·
SDIO接口协议
1. 核心概念
1.1 SDIO协议概述
SDIO(Secure Digital Input Output,安全数字输入输出)是SD卡协会(SDA)制定的扩展接口标准,在SD卡接口基础上增加了I/O设备通信功能。
核心特点:
- 高速传输:最高208MB/s(UHS-II)
- 多功能:存储 + I/O设备通信
- 即插即用:自动识别设备类型
- 低功耗:支持多种电源模式
- 向下兼容:兼容SD/MMC卡
1.2 SDIO设备类型
常见SDIO设备:
设备类型 应用示例
┌──────────────────┬────────────────────┐
│ SD Memory Card │ SD/microSD存储卡 │
│ SDIO WiFi │ ESP32-SDIO, CYW43455│
│ SDIO Bluetooth │ 蓝牙模块 │
│ SDIO GPS │ GPS接收器 │
│ SDIO Camera │ 摄像头模块 │
│ SDIO NFC │ NFC读写器 │
└──────────────────┴────────────────────┘
1.3 SDIO接口引脚
标准4位SDIO接口:
┌─────────┐ ┌─────────┐
│ Host │ │ Card │
│ │──CLK────────►│ │ 时钟
│ │◄─CMD────────►│ │ 命令/响应
│ │◄─DAT0───────►│ │ 数据0
│ │◄─DAT1───────►│ │ 数据1
│ │◄─DAT2───────►│ │ 数据2
│ │◄─DAT3───────►│ │ 数据3
└─────────┘ └─────────┘
引脚功能:
- CLK:时钟信号(最高208MHz)
- CMD:命令/响应(双向)
- DAT0-DAT3:数据线(双向)
- DAT1用作中断线(SDIO设备)
总线模式:
1位模式:CLK + CMD + DAT0
4位模式:CLK + CMD + DAT0~DAT3
8位模式:CLK + CMD + DAT0~DAT7(eMMC)
2. 技术原理
2.1 SDIO命令系统
命令格式:
48位命令帧结构
┌─────┬──────┬────┬──────────────┬─────┬────┐
│起始 │传输 │命令│ 参数 │CRC7 │结束│
│ 01 │方向01│6位 │ 32位 │7位 │ 1 │
└─────┴──────┴────┴──────────────┴─────┴────┘
示例:CMD0(GO_IDLE_STATE)
01 | 01 | 000000 | 00000000 00000000 00000000 00000000 | CRC | 1
响应类型:
R1(48位):标准响应
┌─────┬──────┬──────────────────────┬─────┬────┐
│起始 │传输 │卡状态(32位) │CRC7 │结束│
│ 00 │方向00│ │7位 │ 1 │
└─────┴──────┴──────────────────────┴─────┴────┘
R2(136位):CID/CSD寄存器
R3(48位):OCR寄存器
R4(48位):Fast I/O响应
R5(48位):I/O操作响应
R6(48位):发布RCA响应
R7(48位):卡接口条件
常用命令:
| 命令 | 名称 | 参数 | 响应 | 功能 |
|---|---|---|---|---|
| CMD0 | GO_IDLE_STATE | - | - | 复位卡 |
| CMD2 | ALL_SEND_CID | - | R2 | 读取CID |
| CMD3 | SEND_RELATIVE_ADDR | - | R6 | 获取RCA |
| CMD5 | IO_SEND_OP_COND | OCR | R4 | I/O初始化 |
| CMD7 | SELECT_CARD | RCA | R1b | 选择卡 |
| CMD52 | IO_RW_DIRECT | 地址+数据 | R5 | 单字节读写 |
| CMD53 | IO_RW_EXTENDED | 地址+长度 | R5 | 块读写 |
2.2 SDIO初始化流程
初始化序列:
1. 主机上电,等待稳定(1ms)
2. 发送CMD0(GO_IDLE_STATE)
3. 发送CMD5(IO_SEND_OP_COND)
- 检测SDIO设备
- 协商电压范围
4. 发送CMD3(SEND_RELATIVE_ADDR)
- 获取相对地址(RCA)
5. 发送CMD7(SELECT_CARD)
- 选择卡片进入传输状态
6. 发送CMD52配置寄存器
- 设置总线宽度(1/4位)
- 设置时钟频率
- 使能中断
7. 初始化完成,进入数据传输
2.3 SDIO寄存器
CCCR(Card Common Control Registers):
地址 寄存器名称
0x00 CCCR/SDIO Revision
0x01 SD Specification Revision
0x02 I/O Enable
0x03 I/O Ready
0x04 Int Enable
0x05 Int Pending
0x06 I/O Abort
0x07 Bus Interface Control
0x08 Card Capability
FBR(Function Basic Registers):
每个Function(0-7)有独立的FBR
Function 0:通用功能(必选)
Function 1-7:扩展功能
Function 1示例(WiFi):
地址 寄存器
0x100 Function 1 Basic Info
0x109 Function 1 Block Size (LSB)
0x10A Function 1 Block Size (MSB)
3. 代码实现
3.1 Linux SDIO驱动(Python + ioctl)
class SDIODevice:
"""SDIO设备驱动"""
# SDIO命令
CMD52 = 52 # 单字节读写
CMD53 = 53 # 块读写
def __init__(self, device_path: str = '/dev/mmc0'):
"""
初始化SDIO设备
Args:
device_path: 设备路径
"""
self.device_path = device_path
self.fd = None
def open(self):
"""打开设备"""
self.fd = os.open(self.device_path, os.O_RDWR)
def close(self):
"""关闭设备"""
if self.fd:
os.close(self.fd)
def cmd52_read(self, function: int, address: int) -> int:
"""
CMD52单字节读取
Args:
function: 功能编号(0-7)
address: 寄存器地址
Returns:
读取的字节
"""
# CMD52参数构建
# [31]:R/W(0=读)
# [30:28]:Function Number
# [27]:RAW Flag
# [25:9]:Register Address
# [7:0]:Data(写入时有效)
arg = (0 << 31) | (function << 28) | (address << 9)
# 发送命令(简化示例,实际需使用ioctl)
# 这里假设已有底层驱动支持
response = self._send_cmd(self.CMD52, arg)
return response & 0xFF
def cmd52_write(self, function: int, address: int, data: int):
"""
CMD52单字节写入
Args:
function: 功能编号
address: 寄存器地址
data: 写入数据
"""
# CMD52参数构建(写模式)
arg = (1 << 31) | (function << 28) | (address << 9) | data
self._send_cmd(self.CMD52, arg)
def cmd53_read(self, function: int, address: int,
count: int, block_mode: bool = False) -> bytes:
"""
CMD53块读取
Args:
function: 功能编号
address: 起始地址
count: 读取字节数/块数
block_mode: 块模式(True)或字节模式(False)
Returns:
读取的数据
"""
# CMD53参数
# [31]:R/W(0=读)
# [30:28]:Function Number
# [27]:Block Mode
# [26]:OP Code(0=固定地址,1=递增地址)
# [25:9]:Register Address
# [8:0]:Byte/Block Count
arg = (0 << 31) | (function << 28) | \
(block_mode << 27) | (1 << 26) | \
(address << 9) | count
# 发送命令并接收数据
data = self._send_cmd_with_data(self.CMD53, arg, count)
return data
def cmd53_write(self, function: int, address: int,
data: bytes, block_mode: bool = False):
"""
CMD53块写入
Args:
function: 功能编号
address: 起始地址
data: 写入数据
block_mode: 块模式
"""
count = len(data)
# CMD53参数(写模式)
arg = (1 << 31) | (function << 28) | \
(block_mode << 27) | (1 << 26) | \
(address << 9) | count
self._send_cmd_with_data(self.CMD53, arg, count, data)
def _send_cmd(self, cmd: int, arg: int) -> int:
"""
发送SDIO命令
Args:
cmd: 命令号
arg: 参数
Returns:
响应值
"""
# 实际实现需要使用ioctl与内核驱动交互
# 这里仅作示例
pass
def _send_cmd_with_data(self, cmd: int, arg: int,
length: int, data: bytes = None):
"""
发送SDIO命令(带数据传输)
Args:
cmd: 命令号
arg: 参数
length: 数据长度
data: 写入数据(None表示读取)
Returns:
读取的数据或None
"""
# 实际实现需要使用ioctl与内核驱动交互
pass
class ESP32_SDIO_WiFi:
"""ESP32 SDIO WiFi驱动示例"""
def __init__(self, sdio: SDIODevice):
self.sdio = sdio
self.function = 1 # WiFi功能
def init(self):
"""初始化WiFi模块"""
# 使能Function 1
io_enable = self.sdio.cmd52_read(0, 0x02)
io_enable |= (1 << self.function)
self.sdio.cmd52_write(0, 0x02, io_enable)
# 等待Function Ready
while True:
io_ready = self.sdio.cmd52_read(0, 0x03)
if io_ready & (1 << self.function):
break
print("ESP32 WiFi初始化成功")
def send_packet(self, packet: bytes):
"""发送WiFi数据包"""
# 写入数据到Function 1
self.sdio.cmd53_write(self.function, 0x0000, packet, block_mode=True)
def receive_packet(self, length: int) -> bytes:
"""接收WiFi数据包"""
# 从Function 1读取数据
return self.sdio.cmd53_read(self.function, 0x0000, length, block_mode=True)
# 使用示例
if __name__ == '__main__':
sdio = SDIODevice('/dev/mmc0')
sdio.open()
# 初始化ESP32 WiFi模块
wifi = ESP32_SDIO_WiFi(sdio)
wifi.init()
# 发送WiFi数据包
packet = b'\x00\x01\x02\x03...'
wifi.send_packet(packet)
# 接收WiFi数据包
data = wifi.receive_packet(1024)
print(f"接收: {data.hex()}")
sdio.close()
3.2 STM32 SDIO实现(HAL库)
// stm32_sdio.c - STM32 SDIO驱动
#include "stm32f4xx_hal.h"
// SDIO句柄(外部定义)
extern SD_HandleTypeDef hsd;
/**
* 初始化SDIO
*/
void SDIO_Init(void) {
hsd.Instance = SDIO;
hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
hsd.Init.BusWide = SDIO_BUS_WIDE_1B; // 初始1位模式
hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
hsd.Init.ClockDiv = 0;
HAL_SD_Init(&hsd);
// 切换到4位模式
HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B);
}
/**
* CMD52单字节读取
*/
uint8_t SDIO_CMD52_Read(uint8_t function, uint32_t address) {
uint32_t arg = (0 << 31) | // R/W=0(读)
(function << 28) | // Function Number
(address << 9); // Register Address
// 发送CMD52
SDIO_CmdInitTypeDef cmd;
cmd.Argument = arg;
cmd.CmdIndex = 52;
cmd.Response = SDIO_RESPONSE_SHORT;
cmd.WaitForInterrupt = SDIO_WAIT_NO;
cmd.CPSM = SDIO_CPSM_ENABLE;
SDIO_SendCommand(SDIO, &cmd);
// 等待响应
while (SDIO_GetFlagStatus(SDIO, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND |
SDIO_FLAG_CTIMEOUT) == RESET);
if (SDIO_GetFlagStatus(SDIO, SDIO_FLAG_CTIMEOUT) != RESET) {
return 0xFF; // 超时
}
// 读取响应
uint32_t response = SDIO_GetResponse(SDIO, SDIO_RESP1);
return (uint8_t)(response & 0xFF);
}
/**
* CMD52单字节写入
*/
void SDIO_CMD52_Write(uint8_t function, uint32_t address, uint8_t data) {
uint32_t arg = (1 << 31) | // R/W=1(写)
(function << 28) |
(address << 9) |
data;
// 发送CMD52
SDIO_CmdInitTypeDef cmd;
cmd.Argument = arg;
cmd.CmdIndex = 52;
cmd.Response = SDIO_RESPONSE_SHORT;
cmd.WaitForInterrupt = SDIO_WAIT_NO;
cmd.CPSM = SDIO_CPSM_ENABLE;
SDIO_SendCommand(SDIO, &cmd);
// 等待完成
while (SDIO_GetFlagStatus(SDIO, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND |
SDIO_FLAG_CTIMEOUT) == RESET);
}
/**
* CMD53块读取
*/
HAL_StatusTypeDef SDIO_CMD53_Read(uint8_t function, uint32_t address,
uint8_t* buffer, uint16_t count) {
uint32_t arg = (0 << 31) | // R/W=0(读)
(function << 28) |
(1 << 27) | // Block Mode
(1 << 26) | // Increment Address
(address << 9) |
count;
// 配置数据路径
SDIO_DataInitTypeDef data_config;
data_config.DataTimeOut = 0xFFFFFFFF;
data_config.DataLength = count * 512; // 块大小512字节
data_config.DataBlockSize = SDIO_DATABLOCK_SIZE_512B;
data_config.TransferDir = SDIO_TRANSFER_DIR_TO_SDIO;
data_config.TransferMode = SDIO_TRANSFER_MODE_BLOCK;
data_config.DPSM = SDIO_DPSM_ENABLE;
SDIO_ConfigData(SDIO, &data_config);
// 发送CMD53
SDIO_CmdInitTypeDef cmd;
cmd.Argument = arg;
cmd.CmdIndex = 53;
cmd.Response = SDIO_RESPONSE_SHORT;
cmd.WaitForInterrupt = SDIO_WAIT_NO;
cmd.CPSM = SDIO_CPSM_ENABLE;
SDIO_SendCommand(SDIO, &cmd);
// 等待数据传输完成
uint32_t* dest = (uint32_t*)buffer;
uint32_t count_word = count * 512 / 4;
while (count_word > 0) {
if (SDIO_GetFlagStatus(SDIO, SDIO_FLAG_RXFIFOHF) != RESET) {
for (uint8_t i = 0; i < 8; i++) {
*dest = SDIO_ReadFIFO(SDIO);
dest++;
count_word--;
if (count_word == 0) break;
}
}
}
return HAL_OK;
}
/**
* 主程序示例
*/
int main(void) {
HAL_Init();
SystemClock_Config();
// 初始化SDIO
SDIO_Init();
// 读取CCCR寄存器
uint8_t cccr_version = SDIO_CMD52_Read(0, 0x00);
printf("CCCR Version: 0x%02X\r\n", cccr_version);
// 使能Function 1
uint8_t io_enable = SDIO_CMD52_Read(0, 0x02);
io_enable |= 0x02; // Enable Function 1
SDIO_CMD52_Write(0, 0x02, io_enable);
// 主循环
while (1) {
HAL_Delay(1000);
}
}
4. 行业案例
案例1:Raspberry Pi WiFi模块
项目背景:
Raspberry Pi 3/4内置Broadcom WiFi芯片,通过SDIO连接。
技术方案:
- 芯片:BCM43455(WiFi 5 + Bluetooth 5.0)
- 接口:SDIO 3.0(4位,50MHz)
- 驱动:brcmfmac(Linux内核驱动)
性能指标:
- 传输速率:最高150Mbps
- 延迟:<10ms
- 功耗:<200mW(活动状态)
案例2:ESP32-SDIO模块
项目背景:
ESP32可配置为SDIO Slave模式,与主机通信。
技术方案:
- 接口:SDIO 2.0(4位,40MHz)
- 传输速率:最高20MB/s
- 应用:WiFi/BLE协处理器
5. 协议对比
SDIO vs SPI vs UART
| 特性 | SDIO | SPI | UART |
|---|---|---|---|
| 速率 | 最高208MB/s | 最高50Mbps | 最高12Mbps |
| 数据线 | 1/4/8位 | 1位 | 1位 |
| 应用 | 存储/WiFi | 传感器/Flash | 调试/GPS |
| 复杂度 | 高 | 中 | 低 |
更多推荐

所有评论(0)