
1. 项目概述深入SD/MMC主机接口的底层世界在嵌入式系统开发中SD卡和MMC卡因其高容量、低成本和小体积成为了最主流的非易失性存储解决方案之一。然而对于许多开发者而言驱动一张小小的存储卡其底层细节却像一个“黑盒”——我们调用高级API如FATFS进行文件读写但对硬件如何检测到卡的插入、如何判断卡是否被写保护、以及数据如何通过物理引脚一位一位地搬移往往知之甚少。理解这些底层机制不仅是驱动调试和性能优化的基石更是处理复杂异常、设计高可靠性嵌入式存储系统的关键。本文将以瑞萨电子RA8D2微控制器中的SD/MMC主机接口SDHI模块为蓝本为你彻底揭开这层神秘面纱。我们将不满足于手册的简单翻译而是结合我十多年在嵌入式存储驱动开发中踩过的坑、积累的经验深入解析从物理引脚电平变化到寄存器位操作再到中断与DMA协同工作的完整链条。无论你是正在调试SD卡驱动的新手还是希望优化现有存储性能的资深工程师这篇文章都将提供从原理到实操的详尽指南。我们将重点关注三个核心环节卡的物理检测与状态监控卡检测与写保护、高效的数据搬运机制中断与DMA、以及最核心的单块与多块数据传输操作流程。通过理解这些你将能真正驾驭SDHI而不仅仅是使用它。2. 硬件信号层卡检测与写保护的物理实现在软件读写数据之前硬件必须首先可靠地感知到存储卡的存在及其写保护状态。这是所有后续操作的前提。SDHI模块提供了两种主要的物理检测机制理解其硬件连接和信号时序是避免“卡无法识别”等玄学问题的第一步。2.1 基于专用检测引脚SDnCD的卡检测这是最直观、最常用的卡检测方式。SD卡座通常有一个机械开关与SDnCDCard Detect引脚相连。2.1.1 硬件连接与上拉电阻在主机即我们的MCU侧SDnCD引脚需要通过一个上拉电阻连接到电源通常是3.3V。这个电阻的阻值不是随意的它需要在确保可靠检测的前提下兼顾功耗和信号完整性。典型值在10kΩ到100kΩ之间。手册中提到“由SD/MMC主机设备规格决定”在实际选型时你需要参考MCU的硬件设计指南。过小的电阻会导致插入卡时下拉电流过大可能超出引脚驱动能力过大的电阻则会使信号在噪声环境中变得不稳定。实操心得上拉电阻的选择我遇到过因上拉电阻过大1MΩ导致在电磁环境复杂的工业现场偶尔检测不到卡插入的案例。虽然理论上可行但抗干扰能力差。建议遵循硬件设计手册的推荐值通常47kΩ或100kΩ是兼顾可靠性与功耗的稳妥选择。如果PCB空间允许预留一个0欧姆电阻和备用阻值的位置便于后期调试。2.1.2 插入与移除的检测逻辑与时序当卡座为空时SDnCD引脚被主机上拉至高电平。插入一张卡后卡座内的机械开关闭合将SDnCD引脚短接到地GND使其变为低电平。移除卡后开关断开引脚恢复高电平。然而机械开关的闭合与断开并非理想的瞬时动作会存在一段时间的弹跳Bounce。如果MCU直接采样此引脚的电平变化会误判为多次快速的插入/移除事件。因此SDHI模块内置了去抖逻辑。关键寄存器是SD_OPTION中的MCYCLE位域。它定义了一个去抖时间窗口Mcycle周期。只有当SDnCD引脚的电平状态高或低稳定保持超过这个Mcycle时间后状态变化才会被确认并置位相应的状态标志位。卡插入检测流程卡插入SDnCD被拉低。低电平状态持续超过Mcycle时间。SD_INFO1寄存器中的SDCDIN位被硬件自动置为1。如果SDCDIN对应的中断掩码位SDCDINM为0即未屏蔽则会产生卡检测中断CDETI。软件通过向SDCDIN位写0来清除该标志。卡移除检测流程卡移除SDnCD恢复高电平。高电平状态持续超过Mcycle时间。SD_INFO1寄存器中的SDCDRM位被硬件自动置为1。同样可能产生中断软件写0清除。下图清晰地展示了此时序关系SDnCD引脚: 高电平 ---(卡插入)--- 低电平 ---(卡移除)--- 高电平 | | | |--- Mcycle ---| |--- Mcycle ---| | | | 状态标志: 无变化 SDCDIN1 SDCDRM12.1.3 寄存器操作与软件处理软件的关键任务是配置Mcycle和响应中断。Mcycle的时间需要根据SDHI模块的输入时钟PCLK频率来计算。例如如果PCLK100MHzMcycle设置为100则去抖时间为100 * (1/100MHz) 1微秒。对于机械开关通常需要几毫秒到几十毫秒的去抖时间因此需要根据时钟频率合理设置Mcycle值。在中断服务程序ISR中你需要读取SD_INFO1寄存器判断是SDCDIN还是SDCDRM被置位然后执行相应的卡初始化或卸载流程最后务必记得手动清除对应的状态位否则无法检测到下一次状态变化。2.2 基于数据引脚SDnDAT3的卡检测仅SD卡这是一种节省引脚的设计尤其适用于引脚资源紧张的场景。SD卡规范中DAT3线在卡初始化前内部有一个上拉电阻。主机可以利用这个特性进行检测。2.2.1 工作原理在主机侧SDnDAT3引脚被主动下拉到地。当卡未插入时该引脚被主机强制拉低。当一张SD卡插入后卡内部的DAT3上拉电阻生效与主机的下拉电阻形成分压导致SDnDAT3引脚被拉高。主机检测到这个上升沿即可判断卡已插入。2.2.2 与SDnCD方式的区别与注意事项专用性此方法仅适用于SD卡不适用于MMC卡因为MMC卡的DAT3线没有此上拉特性。硬件设计主机侧的下拉电阻阻值需要根据SD卡的上拉电阻值通常为10kΩ ~ 100kΩ谨慎选择以确保电平能被可靠识别为高或低。检测时机其检测时序与SDnCD类似也依赖于稳定的电平持续时间手册中示例为2个PCLK周期但实际可能由其他配置决定。对应的状态标志位是SD_INFO1中的SDD3IN插入和SDD3RM移除。冲突风险在数据传输阶段DAT3是数据线此时不能再用于卡检测。因此这种检测方式仅在卡初始化前的识别阶段有效。一旦开始初始化流程软件应忽略此引脚的电平变化。避坑指南DAT3检测的局限性我曾在一个为了节省PCB空间而采用DAT3检测的项目中遇到MMC卡无法识别的问题。如果你的产品需要同时兼容SD卡和MMC卡强烈建议使用专用的SDnCD引脚进行卡检测DAT3检测只能作为SD卡的备选或辅助方案。在设计初期就必须明确存储卡类型。2.3 写保护机制的双重保障写保护功能防止了存储卡内容的意外擦写。SDHI同样提供了硬件和软件两种方式。2.3.1 硬件写保护SDnWP引脚SD卡座的另一个机械开关连接SDnWPWrite Protect引脚。卡插入后写保护锁的位置决定了该引脚的电平。解锁可写开关断开SDnWP被主机通过上拉或下拉电阻置于非激活状态具体由上拉/下拉决定。锁定写保护开关闭合SDnWP被拉到激活电平。主机通过采样SDnWP引脚的状态并将其反映到SD_INFO1寄存器的SDWPMON位。软件可以轮询或通过中断监控此位从而在尝试写操作前判断卡的硬件写保护状态。2.3.2 软件写保护命令控制这是更灵活、更常用的写保护方式不依赖于物理开关。SD/MMC卡内部有一套复杂的写保护管理机制可以通过发送特定的命令如CMD28、CMD29等来设置或清除卡内部存储区域的写保护状态。即使物理写保护开关是解锁的如果通过软件命令设置了写保护卡也会拒绝写入操作。2.3.3 实践中的策略在实际驱动开发中应同时检查硬件和软件写保护状态。一个健壮的驱动流程应该是卡初始化后立即读取SDWPMON位获取硬件写保护状态。在每次发起写命令如CMD24、CMD25前可以发送查询命令获取卡的当前写保护状态。任何一次写操作失败都应检查错误状态并确认是否为写保护错误CMDE命令错误的一种情况。3. 核心控制层中断与DMA请求机制解析当卡被成功检测并初始化后高效的数据传输就成为核心。SDHI模块通过中断和DMA请求与CPU协同工作理解其机制是优化性能、实现非阻塞操作的关键。3.1 中断系统事件驱动的核心SDHI的中断源非常丰富覆盖了从命令响应结束到数据传输完成的各个环节。所有中断状态都汇集在三个标志寄存器中SD_INFO1、SD_INFO2和SDIO_INFO1。3.1.1 中断源分类与映射手册中的表格列出了主要的中断源我们可以将其归纳为几大类中断类别中断名称状态标志寄存器关键状态位触发条件卡访问中断卡访问中断 (CACI)SD_INFO1ACEND单块或多块数据传输完成。SD_INFO1RSPEND命令响应接收完成。SD_INFO2BWE写缓冲区SD_BUF就绪可写入数据。SD_INFO2BRE读缓冲区SD_BUF数据就绪可读取数据。SD_INFO2多种错误位 (ILA,RSPTO,DTO等)发生通信错误或超时。卡检测中断卡检测中断 (CDETI)SD_INFO1SDCDIN/SDCDRM卡插入/移除事件被确认。SD_INFO1SDD3IN/SDD3RM(仅SD卡)通过DAT3检测到卡插入/移除。3.1.2 中断使能与处理流程中断的产生需要两个条件同时满足某个状态标志位如BRE被硬件置为1。该状态标志位对应的中断掩码位如BREM为0即未屏蔽。SD_INFO1_MASK、SD_INFO2_MASK等寄存器就是用来控制哪些事件可以触发中断的。默认情况下这些掩码位可能为上电复位值通常为1即屏蔽因此在你希望使用中断时必须先将对应掩码位清零。中断服务程序ISR的标准操作流程如下读取SD_INFO1或SD_INFO2寄存器判断具体是哪个事件触发的中断。执行相应的处理逻辑如从SD_BUF读数据。清除状态标志。这是非常关键且容易出错的一步。清除方法是向该状态标志位写入0同时向其他不需要清除的位写入1。例如要清除BRE位需要向SD_INFO2寄存器写入0xFFFF ~(1 BRE位的位置)。手册中示例代码的0x0000_EAFF等值就是根据具体位域计算出来的。核心技巧中断标志的清除很多新手会错误地直接向状态寄存器写0来清除所有标志。这在某些模块中可行但在SDHI中必须采用“写0清除写1保持”的位操作方式。错误的清除方式可能导致标志位无法被清除从而再也无法产生中断。建议将清除操作封装成宏或函数例如#define SDHI_CLEAR_INFO2_FLAG(reg_value) (SD_INFO2 (reg_value)) // 使用时SDHI_CLEAR_INFO2_FLAG(0x0000EAFF); // 清除BRE等标志保留其他3.2 DMA传输请求解放CPU的利器对于大数据量的读写如读写文件使用DMA可以极大减轻CPU负担提高系统整体性能。SDHI模块可以产生DMA传输请求与MCU的DMA控制器配合工作。3.2.1 写DMA请求SD_BUF Write当SDHI需要从主机内存获取数据写入SD卡时会使用写DMA请求。触发条件SD_INFO2.BWE位被置为1表示SD_BUF可写且SD_DMAEN.DMAEN位为1使能DMA。请求保持DMA请求会一直保持有效直到一个完整数据块块大小由SD_SIZE寄存器定义的数据通过DMA传输完毕。关键约束DMA传输的次数必须是块大小的整数倍。这意味着你配置的DMA传输数据总量必须是SD_SIZE的整数倍。如果文件大小不是块大小的整数倍最后一次传输可能需要CPU介入处理剩余部分。异常处理如果发生通信错误或超时DMA请求不会自动取消。软件必须在错误处理中通过清除DMAEN位或进行软件复位SOFT_RST.SDRST来终止DMA请求。3.2.2 读DMA请求SD_BUF Read当SDHI从SD卡读取数据到SD_BUF并准备让DMA搬移到主机内存时会使用读DMA请求。触发条件SD_INFO2.BRE位被置为1表示SD_BUF数据就绪可读且SD_DMAEN.DMAEN位为1。其他特性如请求保持、块整数倍约束、异常处理与写DMA请求类似。3.2.3 DMA配置实战要点双缓冲与循环模式为了达到最高吞吐率通常将DMA配置为双缓冲循环模式。当DMA正在从缓冲区A向SD_BUF搬运数据或反向时SDHI可以同时处理缓冲区B的数据。这需要精心设计缓冲区管理和中断同步。地址与数据宽度确保DMA源/目标地址对齐数据宽度8位、16位、32位与SD_BUF的访问宽度匹配。RA8D2的SDHI通常支持32位访问因此DMA也应配置为32位传输。与中断协同DMA完成传输会产生自己的传输完成中断。你需要协调SDHI的ACEND访问结束中断和DMA传输完成中断以准确知道一次多块传输何时真正结束。4. 数据传输实战单块与多块读写操作流程理解了底层机制后我们来看最核心的数据传输操作。手册提供了清晰的流程图我们将结合代码片段和注意事项进行解读。4.1 命令操作基础任何数据传输都以发送命令开始。命令分为无响应无数据、有响应无数据、有响应有数据等类型。其基本操作序列是固定的清标志清除SD_INFO1和SD_INFO2中的相关状态位避免旧标志影响。配时钟与掩码设置SD_CLK_CTRL时钟频率配置SD_INFO1_MASK和SD_INFO2_MASK决定哪些事件触发中断。发命令将命令参数写入SD_ARG将命令索引和类型写入SD_CMD。写入SD_CMD的瞬间硬件即开始发送命令。等响应/清标志等待RSPEND中断清除该标志然后从SD_RSP10等寄存器读取响应内容并解析。错误处理检查CMDE、CRCE、RSPTO等错误标志并进行相应处理如重试、报错。4.2 单块读操作CMD17详解单块读是读取一个指定扇区块的数据。以下是基于中断模式的详细步骤和代码逻辑4.2.1 操作流程拆解初始化阶段// 1. 清除所有可能的状态标志 SD_INFO1 0x000007BC; // 清除ACEND, RSPEND, SDCDIN等标志 SD_INFO2 0x0000EAFF; // 清除BRE, BWE及各种错误标志 // 2. 配置控制寄存器 SD_CLK_CTRL CLOCK_DIV_VALUE; // 设置SD时钟分频 SD_SIZE BLOCK_SIZE; // 设置块大小通常为512 SD_INFO1_MASK 0x0000031C; // 屏蔽所有中断先不使能 SD_INFO2_MASK 0x00000B00; // 屏蔽所有中断发送读命令SD_ARG sector_address; // 设置要读取的扇区地址 SD_CMD 0x00000011; // CMD17的命令代码等待并处理响应 命令发出后SDHI等待卡响应。响应到来后RSPEND置位如果未屏蔽则产生中断。// 在RSPEND中断服务程序或轮询中 SD_INFO1 ~(1 RSPEND_BIT_POS); // 清除RSPEND标志 uint32_t response SD_RSP10; // 读取响应 // 检查响应中的错误位如卡状态、地址是否无效 if (response_has_error(response)) { // 错误处理设置STP位停止命令序列或进行重试 SD_STOP | (1 STP_BIT_POS); return ERROR; }准备接收数据并启用中断 响应正确后卡开始发送数据。我们需要在数据到来前使能数据就绪中断。SD_INFO1_MASK 0x00000319; // 使能ACEND中断保持其他屏蔽 SD_INFO2_MASK 0x00000A00; // 使能BRE中断屏蔽其他接收数据 当一块数据接收完毕并存入SD_BUF后BRE位被置1触发中断。// 在BRE中断服务程序中 SD_INFO2 ~(1 BRE_BIT_POS); // 清除BRE标志 // 从SD_BUF0读取一个块的数据例如512字节 for (int i 0; i BLOCK_SIZE / 4; i) { data_buffer[i] SD_BUF0; // 假设32位访问 }操作完成 整个单块读操作完成包括数据CRC校验等ACEND位被置1。// 在ACEND中断服务程序中 SD_INFO1 ~(1 ACEND_BIT_POS); // 清除ACEND标志 // 单块读操作完成4.2.2 关键陷阱数据缓冲区访问时机手册中特别警告“在读取SD_BUF0的过程中如果正在接收数据可能会产生通信错误或超时。” 这意味着你必须确保在BRE中断发生后尽快将数据从SD_BUF搬走。SD_BUF是一个硬件FIFO或缓冲区如果读取太慢下一块数据或CRC状态到来时缓冲区已满就会发生溢出错误。在高速模式下这个问题尤为突出。使用DMA是解决此问题的最佳方案。4.3 单块写操作CMD24详解单块写是向一个指定扇区写入数据。流程与读操作对称但方向相反。4.3.1 操作流程对比初始化与清标志同单块读。发送写命令将SD_CMD设为0x00000018CMD24。等待响应并检查同单块读。准备发送数据并启用中断SD_INFO1_MASK 0x00000319; // 使能ACEND中断 SD_INFO2_MASK 0x00000900; // 使能BWE中断缓冲区空可写发送数据 当SD_BUF准备好接收主机数据时BWE置1。// 在BWE中断服务程序中 SD_INFO2 ~(1 BWE_BIT_POS); // 清除BWE标志 // 向SD_BUF0写入一个块的数据 for (int i 0; i BLOCK_SIZE / 4; i) { SD_BUF0 data_buffer[i]; } // 写入完成后硬件自动开始向卡发送数据并等待卡的CRC状态和Busy信号操作完成 卡接收完数据完成内部编程并返回状态后ACEND置1。4.3.2 写操作的特殊性Busy等待写操作比读操作多了一个“Busy”阶段。在卡接收到数据后需要时间将数据编程到闪存单元中。在此期间DAT0线会被卡拉低Busy状态。SDHI硬件会自动监测这个状态。ACEND中断只有在卡释放Busy状态后才会产生。因此单块写操作的耗时通常比读操作长尤其是在卡性能较差或磨损严重时。4.4 多块读写操作CMD18/CMD25与流控制多块读写用于连续传输多个扇区效率远高于循环执行单块操作。其核心在于流控制。4.4.1 多块读CMD18额外配置除了基本配置还需设置SD_STOP.SEC 1使用块计数停止并在SD_SECCNT寄存器中设置要读取的块数。发送CMD18命令码为0x00000012。循环接收数据流程与单块读类似但BRE中断会连续产生多次次数等于SD_SECCNT。每次BRE中断都需要从SD_BUF读取一个块的数据。自动停止当传输完指定块数后SDHI硬件会自动发送CMD12停止传输命令来终止多块读操作。ACEND中断会在CMD12的响应也接收完毕后产生。4.4.2 多块写CMD25额外配置同多块读设置SD_SECCNT。发送CMD25命令码为0x00000019。循环发送数据BWE中断会连续产生。每次中断向SD_BUF写入一个块的数据。停止与Busy管理传输完指定块数后硬件自动发送CMD12。但需要注意的是最后一个数据块写入后卡可能进入一个较长的Busy状态进行最终编程。ACEND中断会等待这个Busy状态结束。4.4.3 使用内部定时器与外部定时器手册提到了多块写的两种模式使用内部定时器和外部定时器。这主要与写超时管理有关。内部定时器SDHI模块内部有一个超时计数器由SD_OPTION.TOP位域配置。如果在数据发送后在规定时间内未收到卡的响应CRC状态则会产生DTO数据超时错误。这是默认和常用的模式。外部定时器在一些特定应用或MMC协议中可能需要更灵活或更长的超时管理。此时可以禁用内部定时器设置SD_OPTION.TOUTMASK 1由软件使用一个独立的硬件定时器来管理超时。这增加了软件的复杂性但提供了更高的灵活性。性能优化心得多块传输的块数选择不是一次传输的块数越多越好。SD_SECCNT是一个16位寄存器理论上最多可设置65535个块。但实践中需要考虑内存缓冲区你的应用层或DMA缓冲区是否能容纳这么多数据卡的性能一些低速卡或处于老化状态的卡在连续写入大量数据时内部的垃圾回收操作可能导致后续块写入速度急剧下降甚至超时。系统实时性一次传输占用SDHI总线时间过长可能会影响其他需要访问SD卡的任务。 我通常从一个较小的值开始测试如16或32个块根据实际性能和系统需求进行调整。对于实时性要求高的系统可能宁愿选择较小的块数以更频繁地释放总线控制权。5. 错误处理与调试从寄存器位到问题根源在SDHI驱动开发中90%的时间可能都在调试各种错误。手册中SD_INFO2、SD_ERR_STS1、SD_ERR_STS2寄存器里的错误标志位就是你最好的侦探工具。5.1 常见通信错误解析CRC错误CRCECRCTKE接收到的数据块CRC状态令牌错误。RDCRCE读取的数据块CRC校验错误。可能原因时钟信号质量差毛刺、抖动、电源不稳定、布线干扰、卡接触不良或损坏。提高时钟稳定性、检查PCB布线确保时钟和数据线等长、有完整参考平面、清洁卡槽触点是首要排查方向。命令错误CMDECMDE0/CMDE1发送的命令索引与卡返回的响应中的命令索引不匹配。可能原因在命令传输过程中被意外干扰、卡处于非预期状态如Busy、或在上一条命令的流程未完全结束时就发送了下一条命令。确保严格遵循命令-响应时序在发送新命令前确认上一条命令已彻底完成ACEND或错误状态已处理。数据长度错误ENDECRCLENE/RDLENE/RSPLENE接收到的令牌、数据或响应长度与预期不符。可能原因配置错误如SD_SIZE设置与实际传输块大小不符、卡通信协议不匹配如尝试用SD协议与MMC卡通信。仔细核对初始化阶段获取的卡信息CSD寄存器和你的驱动配置。5.2 超时错误解析响应超时RSPTO发送命令后超过640个SDHI时钟周期未收到任何响应。可能原因卡不存在未插卡或检测失败、卡处于睡眠或非活动状态、命令本身非法如地址超出范围。首先检查卡检测状态然后确认命令和参数是否正确。数据超时DTOCRCTO发送写数据后超时未收到CRC状态令牌。RDTO发送读命令后超时未收到数据。BSYTO等待卡Busy状态超时。可能原因卡处理速度慢尤其是写操作、时钟频率过高卡不支持、卡性能下降或损坏。尝试降低时钟频率、增加超时时间调整SD_OPTION.TOP、或检查卡的健康状态。5.3 调试流程与排查表当操作失败时建议遵循以下排查流程步骤操作目的1. 检查物理层测量SD卡槽的VDD电压3.3V±10%、CLK波形频率、幅度、过冲。排除电源和时钟问题这是最常见的问题根源。2. 检查卡状态读取SD_INFO1确认SDCDIN或SDD3IN已置位。发送CMD0GO_IDLE_STATE进行软件复位。确认硬件连接和卡的基本响应。3. 执行初始化发送CMD8、ACMD41等初始化命令序列读取OCR、CID、CSD寄存器。确认卡类型、容量、支持的电平和工作模式。4. 复现错误执行出错的读写操作并在出错后立即读取SD_INFO2、SD_ERR_STS1、SD_ERR_STS2。定位具体的错误类型。5. 降低条件将时钟频率降到最低如400kHz进行简单读写测试。排除时序和信号完整性问题。6. 逻辑分析使用逻辑分析仪抓取CMD、CLK、DAT[3:0]信号对比SD物理层协议。终极手段可视化分析通信过程中的每一位数据。一个真实的调试案例在一次项目中多块写操作随机失败报CRCE错误。降低时钟频率后问题消失。用示波器观察CLK信号发现在高速率下由于PCB走线过长且靠近噪声源CLK信号存在振铃和边沿退化。通过在CLK线上串联一个33欧姆的小电阻靠近MCU端并优化电源滤波问题得以解决。这个案例说明很多软件问题本质是硬件问题。6. 高级话题与优化建议在掌握了基本操作后可以考虑以下进阶内容以提升驱动性能和可靠性。6.1 4位宽模式与高速模式默认的SDHI通信使用1条数据线DAT0。为了提高吞吐量可以在初始化后切换到4位宽模式使用DAT0-DAT3。通过ACMD6命令设置卡的总线宽度。配置SDHI控制器相应的寄存器以启用4位模式。注意在4位模式下SDnDAT3引脚用于数据传输不能再用于卡检测。此外SD卡支持多种高速模式High Speed, SDR50, DDR50等MMC卡也有HS200等模式。启用这些模式需要卡和主机控制器都支持该模式。通过特定的切换命令如CMD6和参数来协商和切换。切换后需要提高SDHI的时钟频率。高速模式对PCB布局、电源完整性和信号完整性的要求极为苛刻必须严格按照硬件设计指南进行布局布线。6.2 电源管理与睡眠唤醒为了节能SDHI模块和SD卡都可以进入低功耗状态。控制器睡眠可以通过关闭SDHI模块的时钟或设置低功耗模式来省电。唤醒后需要重新初始化部分寄存器。卡睡眠通过发送CMD7取消卡选中或发送CMD0使卡进入空闲状态。唤醒时需要重新执行部分初始化序列通常比冷启动快。需要妥善处理睡眠期间可能发生的卡移除和插入事件。6.3 驱动层设计模式一个健壮的SDHI驱动不应是简单的顺序代码而应采用状态机模型。分层设计分为底层硬件抽象层HAL负责寄存器操作、协议层实现CMD序列、块设备层提供read/write接口。异步非阻塞利用中断和DMA实现非阻塞的读写操作。上层应用发起请求后立即返回操作完成后通过回调函数或消息队列通知应用。错误重试机制对于可恢复的错误如CRC错误、超时驱动内部应实现有限次数的自动重试。重试前可以尝试降低时钟频率或复位SDHI控制器。最后驱动开发离不开测试。除了功能测试还应进行压力测试长时间连续读写、异常测试热插拔、突然断电和兼容性测试不同品牌、不同容量、新旧程度的SD/MMC卡。只有经过充分测试的驱动才能在实际产品中稳定运行。