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
复杂度
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐