5.18 SDIO/MMC设备
2026/7/20大约 7 分钟设备驱动驱动SDIOMMCSD卡存储
5.18 SDIO/MMC设备
📚 本节导读
学习时长: 约 50 分钟
难度级别: ⭐⭐⭐⭐⭐
前置知识: C 语言基础、SD/MMC 协议基础、5.1 设备驱动框架
🎯 学习目标
- 理解 OneOS SDIO/MMC 协议栈的分层架构
- 掌握 Host 控制器的抽象与注册
- 理解 SD/MMC/SDIO 卡的检测与初始化流程
- 掌握 Block 设备接口的读写操作
- 了解热插拔检测机制
一、概述
OneOS SDIO/MMC 驱动位于 drivers/sdio/ 目录下,提供完整的 SD/MMC/SDIO 协议栈支持。该驱动实现了从 Host 控制器到卡协议层的完整抽象,支持 SD 卡、MMC 卡、SDIO 设备(如 WiFi 模块)的识别与操作。
协议栈分层架构:
┌──────────────────────────────────────┐
│ Block Device Interface │ ← 块设备读写接口
├──────────────────────────────────────┤
│ SD Protocol │ MMC Protocol │ ← 卡协议层(sd.c / mmc.c)
│ │ │
├──────────────────────────────────────┤
│ mmcsd_core.c │ ← 核心框架层
│ (命令发送、响应解析、数据传输) │
├──────────────────────────────────────┤
│ mmcsd_host │ ← Host 控制器抽象
│ (Host ops 函数表) │
├──────────────────────────────────────┤
│ HAL 层 Host 驱动实现 │ ← 具体硬件实现
│ (STM32/ESP32/... SDIO 外设) │
└──────────────────────────────────────┘SDIO 驱动文件组成:
| 模块 | 文件 | 说明 |
|---|---|---|
| 核心框架 | mmcsd_core.h / mmcsd_core.c | 命令发送/接收、数据传输、卡检测线程 |
| Host 抽象 | mmcsd_host.h | Host 控制器结构体和操作函数表 |
| 命令定义 | mmcsd_cmd.h | SD/MMC 命令宏定义 |
| 卡信息 | mmcsd_card.h | 卡信息结构体(CID/CSD/SCR 等) |
| SD 协议 | sd.h / sd.c | SD 卡初始化、识别、配置 |
| MMC 协议 | mmc.h / mmc.c | MMC/eMMC 卡初始化、识别、配置 |
| SDIO 协议 | sdio.h / sdio.c | SDIO 设备(WiFi/BT 等)初始化与读写 |
| Block 设备 | block_dev.c | 将 SD/MMC 卡封装为 Block 设备 |
二、核心数据结构
2.1 Host 控制器结构体 struct os_mmcsd_host
struct os_mmcsd_host
{
struct os_mmcsd_host_ops *ops; /* Host 操作函数表 */
uint32_t freq_min; /* 最小时钟频率 */
uint32_t freq_max; /* 最大时钟频率 */
uint32_t valid_ocr; /* 有效工作电压范围 */
uint32_t flags; /* Host 特性标志 */
uint32_t max_seg_size; /* 最大段大小 */
uint32_t max_dma_segs; /* DMA 最大段数 */
uint32_t max_blk_size; /* 最大块大小 */
uint32_t max_blk_count;/* 最大块数 */
/* 私有数据 */
void *private;
};Host 特性标志:
| 宏定义 | 说明 |
|---|---|
MMCSD_BUSWIDTH_1 | 1-bit 总线宽度 |
MMCSD_BUSWIDTH_4 | 4-bit 总线宽度 |
MMCSD_BUSWIDTH_8 | 8-bit 总线宽度(eMMC) |
MMCSD_SUPP_HIGHSPEED | 支持高速模式 |
MMCSD_SUPP_SDIO_IRQ | 支持 SDIO 中断 |
2.2 Host 操作函数表 struct os_mmcsd_host_ops
struct os_mmcsd_host_ops
{
/* 发送命令,获取响应 */
void (*request)(struct os_mmcsd_host *host, struct os_mmcsd_req *req);
/* 设置 Host IO 电压 */
void (*set_iocfg)(struct os_mmcsd_host *host, uint32_t iocfg);
/* 获取卡检测状态 */
int32_t (*get_card_status)(struct os_mmcsd_host *host);
/* 启用/禁用 SDIO 中断 */
void (*enable_sdio_irq)(struct os_mmcsd_host *host, int32_t en);
/* 配置 DMA 传输 */
void (*config_dma)(struct os_mmcsd_host *host, void *buf, uint32_t size);
};2.3 请求/命令/数据结构
/* MMC/SD 请求 */
struct os_mmcsd_req
{
struct os_mmcsd_cmd *cmd; /* 待发送的命令 */
struct os_mmcsd_data *data; /* 关联的数据传输 */
};
/* MMC/SD 命令 */
struct os_mmcsd_cmd
{
uint32_t cmd_code; /* 命令码 */
uint32_t arg; /* 命令参数 */
uint32_t resp[4]; /* 响应寄存器(最多 128bit) */
uint32_t flags; /* 响应类型标志 */
/* ... */
};
/* MMC/SD 数据传输 */
struct os_mmcsd_data
{
uint32_t blksize; /* 块大小 */
uint32_t blks; /* 块数量 */
uint8_t *buf; /* 数据缓冲区 */
int32_t err; /* 错误码 */
uint32_t flags; /* 方向标志:DATA_DIR_WRITE / DATA_DIR_READ */
uint32_t bytes_xfered; /* 已传输字节数 */
struct os_mmcsd_cmd *stop; /* 停止传输命令(CMD12) */
uint32_t timeout_ns; /* 超时(纳秒) */
uint32_t timeout_clks; /* 超时(时钟周期) */
};响应类型:
| 宏 | 说明 | 响应长度 |
|---|---|---|
RESP_NONE | 无响应 | 0 |
RESP_R1 | 常规响应 | 48 bit |
RESP_R1B | 带 Busy 的 R1 | 48 bit |
RESP_R2 | CID/CSD 响应 | 136 bit |
RESP_R3 | OCR 响应 | 48 bit |
RESP_R6 | 发布 RCA 响应 | 48 bit |
RESP_R7 | 接口条件响应 | 48 bit |
2.4 卡信息结构体 struct os_mmcsd_card
struct os_mmcsd_card
{
struct os_mmcsd_host *host; /* 所属 Host */
uint32_t rca; /* 卡相对地址 */
uint32_t resp_cid[4]; /* 卡识别号 */
uint32_t resp_csd[4]; /* 卡特定数据 */
uint32_t resp_scr[2]; /* SD 配置寄存器 */
uint32_t card_capacity; /* 卡容量(扇区数) */
uint32_t card_blksize; /* 块大小 */
uint32_t card_type; /* 卡类型 */
/* ... */
};卡类型:
| 宏 | 说明 |
|---|---|
CARD_TYPE_SD | SD 卡 |
CARD_TYPE_SDHC | 高容量 SD 卡(SDHC) |
CARD_TYPE_SDXC | 扩展容量 SD 卡(SDXC) |
CARD_TYPE_MMC | MMC 卡 |
CARD_TYPE_SDIO | SDIO 设备 |
三、关键 API
3.1 Host 控制器注册
/* 注册 MMC/SD Host 控制器 */
int32_t mmcsd_host_register(struct os_mmcsd_host *host);HAL 层需要填充 struct os_mmcsd_host 和 struct os_mmcsd_host_ops,然后调用 mmcsd_host_register() 注册。
3.2 核心框架 API
/* Host 总线锁(互斥量) */
void mmcsd_host_lock(struct os_mmcsd_host *host);
void mmcsd_host_unlock(struct os_mmcsd_host *host);
/* 发送命令 */
void mmcsd_send_request(struct os_mmcsd_host *host, struct os_mmcsd_req *req);
/* 设置 Host 时钟和总线宽度 */
void mmcsd_set_clock(struct os_mmcsd_host *host, uint32_t clock);
void mmcsd_set_bus_width(struct os_mmcsd_host *host, uint32_t width);
/* 卡检测 */
void mmcsd_card_detect(struct os_mmcsd_host *host);
/* 热插拔事件 */
void mmcsd_host_hotplug(struct os_mmcsd_host *host);3.3 卡状态检查
/* 检查卡是否插入 */
int32_t mmcsd_card_status(struct os_mmcsd_host *host);
/* 等待卡就绪 */
int32_t mmcsd_wait_card_ready(struct os_mmcsd_host *host);四、使用示例
4.1 HAL 层 Host 注册
#include <sdio/mmcsd_host.h>
#include <sdio/mmcsd_core.h>
/* HAL 层实现 Host 操作函数 */
static void stm32_mmcsd_request(struct os_mmcsd_host *host,
struct os_mmcsd_req *req)
{
/* 发送命令,等待响应,处理数据传输 */
/* 发送完成后调用 req->cmd->err 和 req->data->err 设置结果 */
}
static void stm32_mmcsd_set_iocfg(struct os_mmcsd_host *host,
uint32_t iocfg)
{
/* 配置 IO 电压 */
}
static int32_t stm32_mmcsd_get_card_status(struct os_mmcsd_host *host)
{
/* 读取卡检测引脚状态 */
return (GPIO_ReadPin(SD_DETECT_GPIO) == 0) ? 1 : 0;
}
static struct os_mmcsd_host_ops stm32_sdio_ops =
{
.request = stm32_mmcsd_request,
.set_iocfg = stm32_mmcsd_set_iocfg,
.get_card_status = stm32_mmcsd_get_card_status,
.enable_sdio_irq = OS_NULL,
.config_dma = OS_NULL,
};
/* 注册 Host */
void sdio_host_init(void)
{
static struct os_mmcsd_host host;
host.ops = &stm32_sdio_ops;
host.freq_min = 400000; /* 400KHz 识别模式 */
host.freq_max = 24000000; /* 24MHz 高速模式 */
host.valid_ocr = 0x00FF8000;/* 2.7V-3.6V */
host.flags = MMCSD_BUSWIDTH_4 | MMCSD_SUPP_HIGHSPEED;
host.max_seg_size = 65535;
host.max_blk_size = 512;
host.private = OS_NULL;
mmcsd_host_register(&host);
}4.2 卡检测处理
/* 卡检测任务(SDIO 框架内部创建) */
void mmcsd_detect_task(void *arg)
{
while (1)
{
/* 等待卡插入事件(通过邮箱) */
os_mb_recv(mmcsd_detect_mb, &event, OS_WAIT_FOREVER);
/* 处理卡检测 */
mmcsd_card_detect(host);
}
}4.3 通过 Block 设备读写
SD/MMC 卡初始化后会被封装为 Block 设备,可通过标准 Block 设备接口访问:
#include <device.h>
#include <block/block_device.h>
void sd_card_read_write_example(void)
{
os_device_t *blk_dev;
uint8_t buf[512];
int ret;
/* 打开 Block 设备(SD 卡通常命名为 sd0) */
blk_dev = os_device_open("sd0");
if (blk_dev == OS_NULL)
{
os_kprintf("SD card not found!\r\n");
return;
}
/* 读取第 0 扇区(MBR) */
ret = os_device_read_block(blk_dev, 0, buf, 1);
if (ret == 1)
{
os_kprintf("Read sector 0 success, first byte: 0x%02X\r\n", buf[0]);
}
/* 写入第 100 扇区 */
memset(buf, 0xAB, 512);
ret = os_device_write_block(blk_dev, 100, buf, 1);
if (ret == 1)
{
os_kprintf("Write sector 100 success\r\n");
}
}五、卡初始化流程
SD/MMC 卡初始化是一个复杂的协议握手过程,由 mmcsd_core.c 中的 mmcsd_card_detect() 统一管理:
mmcsd_card_detect(host)
│
├── 1. 复位 Host 控制器
│
├── 2. 发送 CMD0(GO_IDLE_STATE)→ 复位所有卡
│
├── 3. 发送 CMD8(SEND_IF_COND)→ 检查接口条件
│ ├── 有响应 → SD 2.0+ 卡
│ └── 无响应 → SD 1.x 或 MMC 卡
│
├── 4. 发送 ACMD41(SD_SEND_OP_COND)→ SD 卡初始化
│ ├── 有响应 → SD 卡 → sd_init()
│ │ ├── CMD2(ALL_SEND_CID)→ 获取 CID
│ │ ├── CMD3(SEND_RELATIVE_ADDR)→ 获取 RCA
│ │ ├── CMD9(SEND_CSD)→ 获取 CSD
│ │ ├── CMD7(SELECT_CARD)→ 选中卡
│ │ ├── ACMD51(SEND_SCR)→ 获取 SCR
│ │ ├── ACMD6(SET_BUS_WIDTH)→ 设置 4-bit 总线
│ │ └── CMD6(SWITCH_FUNC)→ 切换到高速模式
│ │
│ └── 无响应 → 尝试 MMC 初始化 → mmc_init()
│ ├── CMD1(SEND_OP_COND)→ MMC 初始化
│ ├── CMD2(ALL_SEND_CID)→ 获取 CID
│ ├── CMD3(SET_RELATIVE_ADDR)→ 设置 RCA
│ ├── CMD9(SEND_CSD)→ 获取 CSD
│ ├── CMD7(SELECT_CARD)→ 选中卡
│ └── CMD8(SEND_EXT_CSD)→ 获取扩展 CSD(eMMC)
│
├── 5. 尝试 SDIO 初始化 → sdio_init()
│ └── CMD5(IO_SEND_OP_COND)→ SDIO 设备初始化
│
├── 6. 计算卡容量
│ ├── SDSC: capacity ≤ 2GB
│ ├── SDHC: 2GB < capacity ≤ 32GB
│ └── SDXC: capacity > 32GB
│
└── 7. 注册 Block 设备
└── mmcsd_block_device_init(card) → 创建 sd0 设备六、热插拔检测
SDIO 框架支持两种卡检测方式:
6.1 GPIO 中断检测
/* GPIO 中断回调中调用 */
void sd_cd_gpio_isr(void *arg)
{
struct os_mmcsd_host *host = (struct os_mmcsd_host *)arg;
/* 通知热插拔事件 */
mmcsd_host_hotplug(host);
}6.2 轮询检测
框架内部定期检查卡状态,检测到变化后通过邮箱通知 mmcsd_detect_thread 处理。
七、注意事项
- 识别频率: 卡初始化阶段使用低速时钟(≤400KHz),初始化完成后切换到高速时钟
- 总线宽度: 默认为 1-bit 模式,初始化后通过 ACMD6 切换到 4-bit 或 8-bit 模式
- 电压范围: Host 的
valid_ocr需与卡支持的电压范围匹配 - DMA 传输: 大数据量传输应使用 DMA 模式,通过
config_dma回调配置 - SDIO 设备: WiFi/BT 等 SDIO 设备初始化后不创建 Block 设备,而是通过
sdio.c中的 IO_RW 命令直接通信 - 热插拔: 不要在卡读写过程中拔卡,应先
umount文件系统再操作
八、驱动文件位置
| 文件 | 路径 | 说明 |
|---|---|---|
mmcsd_core.h / mmcsd_core.c | drivers/sdio/ | 核心框架:命令执行、卡检测、热插拔 |
mmcsd_host.h | drivers/sdio/ | Host 控制器抽象接口 |
mmcsd_cmd.h | drivers/sdio/ | SD/MMC 命令码定义 |
mmcsd_card.h | drivers/sdio/ | 卡信息结构体定义 |
sd.h / sd.c | drivers/sdio/ | SD 卡协议实现 |
mmc.h / mmc.c | drivers/sdio/ | MMC/eMMC 协议实现 |
sdio.h / sdio.c | drivers/sdio/ | SDIO 设备协议实现 |
block_dev.c | drivers/sdio/ | Block 设备封装 |
Kconfig | drivers/sdio/ | 编译配置选项 |
📖 延伸阅读
- 5.1 设备驱动框架 — 理解设备驱动框架基础
- 5.17 FAL Flash抽象层 — Flash 存储抽象层
- SD 卡简化规范 — SD 卡物理层规范