深入解析MMC/SD/SDIO主机控制器:数据格式、中断与DMA机制

发布时间:2026/7/22 16:48:57
深入解析MMC/SD/SDIO主机控制器:数据格式、中断与DMA机制 1. 项目概述从数据完整性到高效传输的控制器核心在嵌入式系统开发尤其是涉及存储或外设扩展的场景里MMC、SD、SDIO这些接口标准几乎无处不在。无论是手机里的eMMC芯片、相机里的SD卡还是通过Wi-Fi或蓝牙模块实现的SDIO设备其底层通信的可靠性与效率都直接依赖于主机控制器Host Controller的设计与驱动程序的实现。很多开发者在使用现成的SDIO驱动库时可能只关心初始化、读写函数是否工作但对于通信为何能成功、数据为何会出错、性能瓶颈在哪里往往知其然而不知其所以然。这次我们就以德州仪器TI某款处理器中的MMC/SD/SDIO主机控制器MMCHS为蓝本深入其内部机制。这个控制器模块堪称一个“协议翻译官”和“交通警察”它一端通过OCP总线与CPU或DMA控制器对话另一端则严格按照MMC/SD/SDIO的物理层和链路层协议与卡片通信。其核心价值在于通过硬件自动处理了繁琐的协议细节如令牌组帧、CRC校验、超时监控等让软件可以更专注于业务逻辑。我们将聚焦三个最影响稳定性和性能的基石数据格式协议的语言、中断系统的耳朵和DMA数据的搬运工。理解它们你就能从“调用API”进阶到“驾驭硬件”在调试链路错误、优化传输效率时思路会清晰得多。2. 通信基石命令、响应与数据令牌格式解析所有与MMC/SD/SDIO卡的交互都建立在一种基于“令牌Token”的串行通信协议之上。你可以把它想象成一套严谨的电报系统每一次通信都由一个格式固定的电报令牌发起或回应。主机控制器硬件的重要职责之一就是按照标准自动生成这些电报并解析来自卡片的回电。2.1 命令令牌主机发起的指令当CPU需要让卡片执行某个操作如复位、读取块数据、设置总线宽度时它会配置控制器的命令寄存器MMCHS_CMD然后由控制器硬件自动组装并发送命令令牌。一个标准的命令令牌总是48位长其格式是硬性规定的起始位固定为0标志一个令牌的开始。传输位固定为1表明这个令牌是从主机发送到卡片的。命令索引6位对应着MMC/SD/SDIO标准中定义的CMD0, CMD1, CMD2...等具体命令。例如CMD0是复位命令CMD17是读取单个块。命令参数32位用于传递命令所需的附加信息。最常见的参数是目标数据的起始扇区地址LBA。对于不需要参数的命令如CMD0这32位通常填充为0。CRC7校验码7位用于保护命令索引和参数部分共38位在传输过程中的完整性。结束位固定为1标志令牌的结束。注意CRC7的计算和附加是由控制器硬件自动完成的驱动程序通常无需关心。但你需要确保在发送命令前已经正确设置了总线时钟频率和模式否则卡片可能无法正确采样到起始位导致整个命令失效。2.2 响应令牌卡片的反馈卡片在接收到命令后必须给予响应。响应令牌的格式因命令类型而异主要分为48位短响应和136位长响应。所有响应令牌都以起始位0和传输位0表明来自卡片开始以结束位1结束并包含CRC7校验。R1, R1b, R3, R6, R7 (48位)这是最常见的响应类型。以R1为例其内容包含32位的卡片状态寄存器Card Status Register其中每一位都指示了卡片当前的一种状态如是否准备好、是否发生错误等。驱动程序必须解析这些状态位来判断命令执行结果。R2 (CID, CSD寄存器响应136位)当主机发送CMD2或CMD10请求卡片标识CID或卡特定数据CSD时卡片会以136位的R2响应。CID包含了卡片的唯一ID、制造商等信息CSD则包含了卡片的容量、读写性能、擦除块大小等关键参数。这是驱动初始化阶段获取卡片信息的关键步骤。R3 (OCR寄存器响应48位)在发送CMD1发送主机容量支持信息后卡片回复R3其中包含操作条件寄存器OCR主机可以从中判断卡片支持的电压范围、上电完成状态等。关键配置在发送命令前驱动程序必须在MMCHS_CMD寄存器的RSP_TYPE字段明确告知控制器期望收到哪种类型的响应。例如对于CMD2应配置为01b期望R2响应对于CMD17应配置为10b期望R1响应。如果配置错误控制器可能会错误地报告CRC或索引错误CIE/CCRC即使物理传输本身是正确的。2.3 数据令牌与块传输格式数据读写是存储接口的核心。数据以“块”为单位进行传输每个数据块前后都有特定的令牌包裹。数据起始令牌在块读操作中卡片在发送实际数据前会先发送一个数据起始令牌Start Block Token通常是一个特定的字节如0xFE主机用它来同步数据流的开始。数据块紧跟着起始令牌的是实际的数据内容。块长度BLOCK_LEN由主机在传输前通过CMD16命令设置常见为512字节但也支持其他尺寸。CRC16校验每个数据块结束后会跟随16位的CRC校验码用于校验整个数据块在传输过程中的完整性。数据结束令牌在块写操作中卡片在接收完主机发送的数据和CRC后会反馈一个数据响应令牌Data Response Token其中几位编码了接收状态如010表示数据被接受101表示CRC错误。控制器支持1位、4位、8位三种数据总线宽度。在4位或8位模式下数据位被并行传输大大提升了吞吐量。例如在4位模式下一个字节的数据被拆分成两个半字节Nibble同时在DAT0-DAT3四条数据线上传输。控制器硬件负责处理这种并/串转换和位序排列对软件透明。实操心得在调试初期如果数据读写不稳定一个非常有效的排查方法是启用并监控所有的数据错误中断特别是数据CRC错误DCRC和数据结束位错误DEB。DCRC错误直接指向物理链路质量问题如布线不佳、时钟抖动、电源噪声。而DEB错误期望结束位是1但收到0往往意味着卡片在传输中途因为某种原因如供电不足导致内部操作超时提前终止了传输。这时你需要检查电源的负载能力和去耦电容是否足够。3. 中断机制如何高效处理异步事件在轮询Polling模式下CPU需要不断查询控制器的状态寄存器效率极低且占用大量CPU资源。中断驱动Interrupt-Driven是实际应用中的标准模式它允许CPU在控制器完成操作或发生错误时才被通知去处理。3.1 中断源与状态寄存器TI的MMCHS控制器提供了丰富的中断源涵盖了命令、数据、DMA、卡片状态等各个方面。每个中断源在MMCHS_STAT寄存器中都有一个对应的状态位Flag。这些中断大致可以分为几类命令完成类CC命令完成。任何命令无论有无响应执行完毕都会触发。CTO命令超时。命令发出后64个时钟周期内未收到响应。CCRC/CIE/CEB命令响应的CRC错误、索引错误、结束位错误。数据传输类TC传输完成。一次多块读写操作全部完成时触发。BGE块间隔事件。当设置了“在块间隔处停止”功能且传输在块之间暂停时触发。BRR缓冲区读就绪。读操作时一个完整的数据块已从卡片存入控制器缓冲区CPU或DMA可以来读取了。BWR缓冲区写就绪。写操作时控制器缓冲区已准备好接收一个完整数据块的数据。数据错误类DTO数据超时。包括写操作后卡片“忙”超时、读数据超时等。DCRC数据CRC错误。DEB数据结束位错误。高级功能与卡片中断CIRQ卡片中断。仅用于SDIO卡和CE-ATA设备允许卡片主动向主机发起中断请求这是实现SDIO Wi-Fi模块“有数据到来”通知的关键机制。DMADMA中断。在ADMA高级DMA模式下由DMA描述符中的指令触发。ERRI错误中断总标志。当MMCHS_STAT[24:15]中任何一个错误位被置位时此位也会被置位方便进行统一的错误检查。3.2 三层使能机制与中断处理流程控制器的中断管理分为三层理解这三层是编写稳健中断服务程序ISR的关键事件发生层硬件事件发生如卡片响应到达无论是否产生中断信号对应的MMCHS_STAT状态位都会被硬件自动置1。状态更新使能层MMCHS_IE寄存器这个寄存器的每一位控制着对应的事件是否能够去更新MMCHS_STAT寄存器中的状态位。如果MMCHS_IE[x] 0那么即使事件发生MMCHS_STAT[x]也不会被置1。这一层主要用于屏蔽你完全不关心的事件减少不必要的状态寄存器读写。中断信号使能层MMCHS_ISE寄存器这一层控制着哪些状态位被置1时会最终触发MMC_IRQ中断信号输出到CPU的中断控制器。只有MMCHS_IE[x] 1且MMCHS_ISE[x] 1时事件x才会引发硬件中断。一个典型的中断驱动操作流程如下初始化时在MMCHS_IE和MMCHS_ISE中使能你需要的中断源例如使能CC,TC,DTO,DCRC。发送命令如CMD17读块。CPU转而处理其他任务。当命令完成或发生错误时控制器拉高MMC_IRQ引脚。CPU跳转到ISR。ISR第一步读取MMCHS_STAT寄存器获取中断状态字。ISR第二步根据状态字判断事件类型。如果是CC说明命令已响应接着检查MMCHS_STAT[24:15]的错误位如果没有错误则可以准备启动数据阶段。ISR第三步关键向MMCHS_STAT寄存器中已置位的位写入1以清除该状态标志。这与许多“写0清除”或“读清除”的寄存器不同需要特别注意。ISR第四步如果是数据传输完成TC则进行后续处理如通知上层任务如果是错误则进行错误处理和恢复如重试、降速、报告错误。严重注意事项有两个中断状态位的清除方式非常特殊CIRQ卡片中断此位不能通过写1清除。正确的处理方式是在ISR中检测到CIRQ后首先在MMCHS_IE寄存器中禁用CIRQ_ENABLE位写0然后去查询SDIO卡内部的CCCR通用控制命令寄存器中的中断状态位并清除卡端的中断源最后再重新使能MMCHS_IE中的CIRQ_ENABLE。ERRI错误中断总标志此位是只读的它会自动在所有具体的错误状态位如CTO,DCRC等都被清除后自动清零。所以处理错误中断时必须逐一检查并清除所有具体的错误位。3.3 轮询模式的应用场景虽然中断模式效率高但轮询模式在特定场景下仍有价值例如在极简的裸机系统中或者在进行非常精细的时序调试时。在轮询模式下你只需禁用MMCHS_ISE中的中断信号使能然后定期或在一个紧密循环中读取MMCHS_STAT寄存器检查感兴趣的状态位即可。清除状态位的方法同样是写1。4. DMA传输模式解放CPU的数据搬运工当进行大量数据块例如读写一个文件传输时如果每个数据块都通过CPU来搬运会消耗大量CPU周期。DMA直接内存访问模式就是为了将CPU从繁重的数据拷贝工作中解放出来。4.1 控制器作为DMA从设备TI的这款MMCHS控制器工作在DMA从模式。这意味着控制器本身不包含DMA引擎它只是向系统级的DMA控制器发出请求。控制器提供两个独立的请求信号SDMARREQNDMA读请求。当控制器需要DMA控制器从它的数据缓冲区读取数据即卡片到内存的传输时发出。SDMAWREQNDMA写请求。当控制器需要DMA控制器向它的数据缓冲区写入数据即内存到卡片的传输时发出。4.2 DMA接收模式详解以从SD卡读取数据到系统内存为例DMA接收模式主机发送读命令如CMD18多块读。卡片开始通过数据线向控制器的内部缓冲区发送数据。当一整个数据块大小由MMCHS_BLK.BLEN定义例如512字节被完整地写入控制器缓冲区后控制器的BRR状态位被置1同时SDMARREQN信号被置为有效激活。系统DMA控制器检测到SDMARREQN有效开始发起一次或多次总线读取事务从控制器的MMCHS_DATA寄存器即缓冲区访问端口读取数据并直接写入系统内存的目标地址。关键点SDMARREQN信号在DMA控制器执行了第一次读取操作后就会被控制器置为无效。无论这个块剩下的数据是DMA控制器通过单次突发传输还是多次访问读完的在整个块传输期间控制器只发出这一次请求。DMA控制器需要自己根据BLEN的值来计算需要执行多少次32位读取BLEN/4向上取整。如果DMA控制器速度太慢在下一个数据块已经从卡片传送到缓冲区时上一个块的数据还未被DMA读完控制器的缓冲区会满。此时一个精妙的设计是控制器会暂时停止提供给卡片的时钟MMC_CLK直到缓冲区有空间为止。这实现了硬件流控防止数据丢失。4.3 DMA发送模式详解以从系统内存写数据到SD卡为例DMA发送模式主机发送写命令如CMD25多块写。控制器发现其内部缓冲区有足够空间容纳一个数据块时BWR状态位被置1同时SDMAWREQN信号被置为有效。系统DMA控制器检测到SDMAWREQN有效开始从系统内存源地址读取数据并通过总线写入控制器的MMCHS_DATA寄存器。同样SDMAWREQN在DMA执行第一次写入操作后失效。当控制器缓冲区积累够一个完整块的数据后它自动开始向卡片发送数据。卡片接收完数据并校验CRC后会反馈一个状态。如果卡片内部正在进行闪存编程写操作较慢它会将数据线拉低Busy控制器会检测到这个忙状态并等待直到卡片释放总线。4.4 配置与注意事项要启用DMA传输必须在发送数据传输命令之前设置MMCHS_CMD寄存器的DE位为1。这个位是触发DMA请求的初始开关。缓冲区管理与双缓冲控制器的内部数据缓冲区大小是有限的例如可能为1KB。当设置的块大小BLEN小于或等于缓冲区总大小的一半时控制器会启用**双缓冲Ping-Pong Buffer**机。此时缓冲区被分为A、B两部分。当DMA正在从A部分读取数据时卡片可以同时向B部分写入下一个数据块实现了传输的流水线化极大提高了连续读写的吞吐量。如果BLEN大于缓冲区一半则只能使用单缓冲传输效率会下降。DMA传输完成中断除了BRR/BWR这类块就绪中断在配置了ADMA使用描述符链表的高级DMA时还可以使用DMA中断。这个中断在DMA描述符中指定的传输全部完成后触发比等待TC传输完成中断更适合管理复杂的多段DMA传输。避坑指南在调试DMA传输时一个常见的问题是数据传输不完整或错位。请务必检查以下几点内存对齐确保DMA源地址和目标地址符合DMA控制器的对齐要求通常是4字节或更高。数据大小端CPU、DMA控制器、MMC控制器三者之间的字节序Endianness必须一致。通常嵌入式系统都是小端模式但需要确认。BLEN设置DMA传输的字节数必须与控制器配置的BLEN块长度匹配。例如BLEN512那么每次DMA请求对应的事务总长度就应该是512字节。缓存一致性如果DMA操作的内存区域被CPU缓存Cache覆盖必须在DMA启动前执行缓存写回Write-Back在DMA完成后执行缓存无效Invalidate否则会读到脏数据或旧数据。5. 电源、时钟与复位管理一个稳健的驱动必须妥善处理控制器的上电、下电和时钟管理这直接关系到系统的功耗和稳定性。5.1 复位机制控制器提供了硬件复位和软件复位两种方式。硬件复位通常在上电或全局复位时由芯片引脚触发。它会重置控制器所有寄存器和状态机。软件可以通过查询MMCHS_SYSSTATUS[0] RESETDONE位来等待复位完成。软件复位通过写MMCHS_SYSCONFIG[1] SOFTRESET位实现其效果与硬件复位几乎相同。这在驱动初始化或需要彻底恢复控制器状态时非常有用。部分复位MMCHS_SYSCTL[26] SRD和[25] SRC位可以分别复位数据通路和命令通路的状态机。这在遇到总线冲突或协议错误需要重新开始传输而无需完全复位整个模块时非常高效。警告文档中特别指出如果MMC/SD/SDIO外设的时钟输入不存在软件复位将无法完成。这意味着在尝试复位控制器前必须确保其功能时钟和接口时钟已经由电源与时钟管理模块PRCM正确使能。5.2 时钟与电源模式控制器涉及三个主要时钟域理解它们对低功耗设计至关重要接口时钟即OCP总线时钟用于寄存器访问。功能时钟即内部核心时钟用于数据移位、协议处理等核心操作。32K时钟用于引脚去抖Debounce例如检测SD卡插入/弹出。控制器支持多种省电模式自动时钟门控当AUTOIDLE位使能且总线上无活动时硬件会自动关断内部时钟以省电。空闲模式这是与系统电源管理模块PRCM协作的深度省电模式。分为几种子模式强制空闲无条件响应PRCM的休眠请求立即进入低功耗状态。风险如果在命令或数据传输中途进入此模式会导致不可预知的后果。智能空闲控制器根据自身状态如是否有传输在进行、是否有中断 pending来决定是否响应休眠请求。这是最安全、最常用的模式。智能空闲唤醒在智能空闲基础上允许模块在休眠时仍能监测特定事件如SDIO卡中断并唤醒系统。CLOCKACTIVITY配置这个字段决定了在空闲模式下哪些时钟可以被关闭。例如为了在休眠时仍能检测SD卡插拔或SDIO卡中断可能需要保持32K时钟或功能时钟开启。软件必须确保这里的配置与PRCM中对相应时钟域的配置保持一致否则会导致系统行为异常。6. 常见问题排查与调试技巧实录在实际驱动开发中你会遇到各种各样的问题。下面是我从多次调试中总结出的一些典型场景和排查思路。6.1 命令阶段失败现象发送初始化命令如CMD0, CMD8后长时间超时或收到CRC错误。排查步骤检查物理连接这是第一步也是最常被忽略的一步。用万用表测量卡座的电源引脚VDD、地线GND是否接触良好电压是否在卡片支持的范围内如3.3V±5%。测量时钟CLK和数据线CMD, DAT0-3是否连通。检查时钟在发送任何命令前必须确保给控制器和卡片提供了时钟。使用示波器测量MMC_CLK引脚确认其频率是否正确初始化阶段通常为400kHz或更低波形是否干净无过冲。过高的频率在初始化阶段会导致卡片无法识别。检查上电时序有些卡片对VDD上电和CLK启动之间的时序有要求。确保先稳定供电再提供时钟最后才释放复位如果控制器有复位引脚控制卡座。确认命令索引和参数对照协议手册检查你写入MMCHS_CMD寄存器的命令索引和参数是否正确。一个常见的错误是在发送CMD8发送接口条件时参数中的电压信息不匹配。检查响应类型配置确认MMCHS_CMD寄存器中的RSP_TYPE字段设置是否正确。期望R1却配置为R2必定会报错。6.2 数据阶段失败现象命令成功但读写数据时失败频繁触发DCRC或DTO错误。排查步骤降低速率首先尝试大幅降低总线时钟频率。如果低速下工作正常高速下出错问题很可能出在信号完整性上。检查信号完整性使用示波器最好是带MIPI或SD协议解码功能的同时捕获CLK、CMD和所有DAT线。观察过冲和振铃数据线在跳变时是否有明显的过冲这会导致逻辑电平误判。通常需要在数据线上串联一个小电阻如22Ω-33Ω来改善。时序裕量测量数据相对于时钟的建立时间Setup Time和保持时间Hold Time是否满足卡片规格书的要求通常SD高速模式要求很严格。交叉干扰当多条DAT线同时切换时是否有一条线严重干扰了另一条线检查PCB布线确保数据线等长、等间距并远离噪声源。检查电源噪声在卡片VDD引脚处测量电源纹波。大电流瞬间切换如NAND闪存编程会导致电压跌落可能使卡片内部逻辑复位或出错。确保电源路径上有足够且靠近卡座的去耦电容如10uF钽电容 0.1uF陶瓷电容。检查DMA配置如果使用DMA请参考第4.4节的避坑指南仔细检查内存对齐、缓存和传输长度。6.3 中断不触发或无法清除现象程序卡死在等待中断的状态或者进入中断服务程序后无法清除中断标志导致反复进入。排查步骤确认中断使能逐级检查首先确认MMCHS_IE中对应事件的状态更新已使能然后确认MMCHS_ISE中对应事件的中断信号输出已使能。确认CPU中断配置检查SoC的系统中断控制器如GIC是否已正确配置并开启了对应MMC_IRQ的中断通道。检查清除方式牢记MMCHS_STAT寄存器是写1清除。你的ISR中必须对检测到的所有置位位执行写1操作。对于CIRQ必须按3.2节所述的特殊流程处理。检查共享中断如果MMC_IRQ是多个控制器的共享中断线需要在ISR中遍历所有可能的中断源并清除所有产生中断的设备的状态位。6.4 低功耗模式下唤醒失败现象系统进休眠后无法通过SD卡插拔或SDIO卡中断唤醒。排查步骤确认唤醒源使能对于SD卡插拔检测通常通过mmc_dat1引脚需要配置相应的GPIO为中断模式并正确使能。对于SDIO卡中断需要确保MMCHS_HCTL寄存器中对应的唤醒使能位已设置并且MMCHS_SYSCONFIG中的ENWAKEUP位已使能。检查时钟配置在智能空闲唤醒模式下为了检测异步唤醒事件必须保证功能时钟或32K时钟在休眠期间是活动的。检查CLOCKACTIVITY的配置。检查电源域确认MMC/SD/SDIO控制器所在的电源域在休眠期间没有被完全关闭。有些SoC设计允许在保持极低功耗的同时维持某个电源域的部分功能以用于唤醒。我个人在调试一个SDIO Wi-Fi模块的唤醒功能时曾花费大量时间。最终发现是CLOCKACTIVITY配置为00关闭所有时钟导致模块在休眠时根本无法采样SDIO卡的中断信号线。将其改为10仅保持功能时钟开启后问题迎刃而解。这个教训告诉我对于低功耗设计必须逐比特地理解每个电源和时钟控制位的含义。