1. MMC/SD/SDIO接口寄存器配置与数据传输深度解析
在嵌入式系统开发中,MMC/SD/SDIO接口作为高速存储设备的主流通信协议,其底层驱动开发一直是工程师面临的挑战。本文将基于TI平台MMCHS控制器,从寄存器级视角剖析协议栈实现原理,提供可直接移植的配置方案。
1.1 协议核心架构与寄存器映射
MMC/SD/SDIO协议栈采用分层设计,物理层通过6线制接口(CLK/CMD/DAT[3:0])实现通信。TI平台的MMCHS控制器通过寄存器组完成协议状态管理,主要寄存器模块包括:
- 控制寄存器组:MMCHS_CON(0x4809C02C)负责总线模式、中断极性等全局配置
- 命令寄存器组:MMCHS_CMD(0x4809C10C)存储命令操作码及响应类型
- 数据寄存器组:MMCHS_DATA(0x4809C120)作为FIFO缓冲区接口
- 状态寄存器组:MMCHS_STAT(0x4809C130)反映传输错误和中断状态
寄存器访问需严格遵守32位对齐原则,16/8位访问会导致数据损坏。典型寄存器位域设计如下表所示:
| 寄存器名 | 关键位域 | 功能描述 | 复位值 |
|---|---|---|---|
| MMCHS_CON | DW8(bit5) | 8位数据总线使能 | 0 |
| MODE(bit4) | 0-功能模式/1-测试模式 | 0 | |
| MMCHS_SYSCTL | CEN(bit2) | 内部时钟使能 | 0 |
| CLKD(bit9:0) | 时钟分频系数 | 0x3C | |
| MMCHS_CMD | CMDINDEX(bit5:0) | 命令索引号(如CMD2=0x02) | - |
| RESP_TYPE(bit19:16) | 响应类型(0-无响应,1-48bit等) | - |
1.2 设备初始化流程精解
1.2.1 电压协商与OCR校验
初始化阶段首先通过CMD1命令完成电压协商,具体步骤如下:
-
总线预配置:设置MMCHS_CON寄存器进入开漏模式(OD=1)
c复制MMCHS1.MMCHS_CON = 0x00000001; // Open-drain mode -
发送初始CMD1:主机发送支持的电压范围(Host OCR)
c复制MMCHS1.MMCHS_CMD = 0x01090000; // CMD1, 48bit响应 MMCHS1.MMCHS_ARG = 0x00FF8000; // 2.7-3.6V电压范围 -
OCR交叉校验:读取RSP10寄存器获取Card OCR,计算交集电压
c复制uint32_t card_ocr = MMCHS1.MMCHS_RSP10; uint32_t valid_voltage = host_ocr & card_ocr; -
发送最终CMD1:使用协商后的电压参数
c复制
MMCHS1.MMCHS_ARG = valid_voltage;
关键点:电压协商失败通常表现为RSP10返回全零,需检查硬件连接和上电时序。实测显示3.3V系统需至少保持100ms稳定供电后再发CMD1。
1.2.2 设备识别流程
完成电压协商后,通过CMD2获取CID寄存器(设备唯一标识):
c复制MMCHS1.MMCHS_CMD = 0x02090000; // CMD2, 136bit响应
while(!(MMCHS1.MMCHS_STAT & CC)); // 等待命令完成
uint32_t cid_part0 = MMCHS1.MMCHS_RSP10; // CID[31:0]
随后用CMD3分配相对地址(RCA),此时总线切换至推挽模式:
c复制MMCHS1.MMCHS_CON = 0x00000000; // Push-pull模式
MMCHS1.MMCHS_CMD = 0x031A0000; // CMD3, RCA=1
MMCHS1.MMCHS_ARG = 0x00010000;
1.3 高速模式配置实战
1.3.1 CSD寄存器解析
通过CMD9获取CSD寄存器后,需重点解析以下字段:
- TRAN_SPEED (CSD[103:96]): 最大传输速率(0x32表示25MHz)
- CCC (CSD[95:84]): 支持的命令类别
- READ_BL_LEN (CSD[83:80]): 最大块长度(通常为512B)
典型解析代码:
c复制uint32_t csd_part0 = MMCHS1.MMCHS_RSP10;
uint8_t max_speed = (csd_part0 >> 24) & 0xFF;
if(max_speed >= 0x32) {
// 支持高速模式
}
1.3.2 8bit总线配置
对于支持MMC4.0+的设备,通过CMD6切换总线位宽:
-
发送切换命令:
c复制MMCHS1.MMCHS_CMD = 0x061B0000; // CMD6 with busy MMCHS1.MMCHS_ARG = 0x03B70200; // EXT_CSD[183]=2(8bit) -
等待状态切换:
c复制while(MMCHS1.MMCHS_STAT & CEB); // 检查busy状态 -
更新控制器配置:
c复制MMCHS1.MMCHS_CON |= 0x00000020; // 使能DW8位
1.3.3 时钟频率优化
根据CSD中的TRAN_SPEED调整时钟分频:
c复制// 计算分频系数 (96MHz主频下)
uint32_t div = 96000000 / (max_speed * 1000000) - 1;
MMCHS1.MMCHS_SYSCTL = (div & 0xFF) | 0x00000100;
实测案例:某eMMC芯片在8bit总线+48MHz时钟下,连续读取速率可达45MB/s,较4bit模式提升80%。
1.4 数据传输模式详解
1.4.1 块传输参数设置
数据传输前需通过CMD16设置块长度(通常512B):
c复制MMCHS1.MMCHS_BLK = 0x00000200; // 512字节块
MMCHS1.MMCHS_CMD = 0x101A0000; // CMD16
多块传输时需用CMD23预定义块数:
c复制MMCHS1.MMCHS_ARG = 0x00000008; // 8个块
MMCHS1.MMCHS_CMD = 0x171A0000; // CMD23
1.4.2 DMA传输配置
启用DMA需配置以下寄存器:
-
DMA触发条件:
c复制MMCHS1.MMCHS_IE = 0x107F0013; // 使能BWR/BRR中断 -
DMA通道参数:
c复制DMA.CCR = 0x000005AA; // 32位宽,地址递增 DMA.CDSAR = (uint32_t)buffer; DMA.CDDAR = 0x4809C120; // MMCHS_DATA地址 -
启动传输:
- 读操作:发送CMD18
c复制MMCHS1.MMCHS_CMD = 0x123A0033; // CMD18 - 写操作:发送CMD25
c复制MMCHS1.MMCHS_CMD = 0x193A0023; // CMD25
- 读操作:发送CMD18
1.4.3 传输状态监控
需轮询STAT寄存器处理异常:
c复制uint32_t status = MMCHS1.MMCHS_STAT;
if(status & DCRC) {
// 数据CRC错误
MMCHS1.MMCHS_STAT = DCRC; // 写1清除
}
if(status & CTO) {
// 命令超时
MMCHS1.MMCHS_STAT = CTO;
}
1.5 低功耗优化策略
1.5.1 时钟门控配置
通过SYSCONFIG寄存器实现动态功耗管理:
c复制MMCHS1.MMCHS_SYSCONFIG = 0x00000121;
// AUTOIDLE=1, CLOCKACTIVITY=01(仅保持接口时钟)
1.5.2 输入缓冲控制
非活动期关闭非必要输入缓冲:
c复制MMCHS1.MMCHS_CON |= 0x00000800; // CTPL=1保持DAT1中断检测
1.6 典型问题排查指南
1.6.1 命令无响应
- 现象:STAT寄存器持续报CTO错误
- 排查步骤:
- 确认CLK线是否有波形(示波器测量)
- 检查MMCHS_CON[OD]是否与总线模式匹配
- 测量卡槽供电电压(需≥2.7V)
1.6.2 数据CRC错误
- 现象:大量DCRC错误中断
- 解决方案:
- 降低时钟频率(增大MMCHS_SYSCTL分频值)
- 检查PCB走线等长(CLK与DATA长度差<50mil)
- 启用内部延迟校准(若控制器支持)
1.6.3 DMA传输断流
- 现象:数据传输不完整
- 修复方案:
c复制// 增加DMA完成中断检查 if(DMA.IRQ & 0x01) { MMCHS1.MMCHS_STAT = TC; // 手动触发传输完成 }
1.7 性能优化实践
通过调整以下参数可获得最佳吞吐量:
- 块大小:建议4KB对齐(8×512B块)
- FIFO阈值:设置为DMA burst长度的整数倍
- 中断合并:每4KB数据产生一次中断
某工业级案例优化前后对比如下:
| 参数项 | 优化前 | 优化后 |
|---|---|---|
| 块大小 | 512B | 4KB |
| 中断频率 | 每512B | 每4KB |
| 实测吞吐量 | 22MB/s | 38MB/s |
| CPU占用率 | 35% | 12% |
在完成所有配置后,建议通过以下命令序列验证稳定性:
c复制// 发送CMD13查询状态
MMCHS1.MMCHS_CMD = 0x0D1A0000;
while(!(MMCHS1.MMCHS_STAT & CC));
if(MMCHS1.MMCHS_RSP10 & 0x0000F000) {
// 存在错误状态位
}
通过本文详实的寄存器级操作指南,开发者可快速构建稳定的MMC/SD底层驱动。实际项目中建议结合芯片勘误手册调整时序参数,特别是在高温环境下需增加CMD-DAT延迟补偿。
