1. 项目概述:从寄存器手册到驱动实战
如果你正在为TI的AM275x系列处理器开发MMC/SD卡驱动,或者正在调试一个存储相关的硬件故障,那么你肯定对那一大堆以MMCSD_开头的寄存器名字又爱又恨。手册里每个寄存器都给出了偏移地址、位域定义和复位值,但把它们串联起来,理解它们如何协同工作,从而让一张小小的SD卡在你的系统里稳定地读写数据,这完全是另一回事。
我花了相当长的时间,在几个基于AM275x的嵌入式存储项目里,从零开始搭建驱动、调试DMA传输超时、解决ECC校验错误。这个过程让我深刻体会到,仅仅“知道”每个寄存器是干什么的远远不够,关键是要理解它们在一个完整的数据传输事务中扮演什么角色,以及配置错误会导致哪些诡异的问题。比如,为什么DMA传输到一半就卡住了?为什么开启了Auto CMD23后命令却不执行?PRESENTSTATE寄存器里那一堆状态位到底哪个才反映了卡的真实状态?
这篇内容,就是把我踩过的坑、理顺的逻辑和验证过的配置流程,系统地梳理出来。我们不会停留在手册的简单翻译上,而是聚焦于如何将这些寄存器组合起来,完成一次从主机内存到SD卡的安全、高效数据传输。核心会围绕三个层面展开:首先是确保数据正确的错误控制层(以ECC寄存器为例),其次是提升搬运效率的DMA传输层,最后是发号施令的命令与控制层。无论你是正在编写底层驱动,还是需要深度优化存储性能,希望这些从实际项目中总结出的细节和心法,能帮你少走弯路。
2. 核心设计思路:分层解耦的寄存器视图
面对AM275x MMCSD控制器里上百个寄存器,直接扎进去逐个研究很容易迷失。我的经验是,必须建立一个清晰的逻辑视图,将它们按功能分层。这不仅仅是分类,更是理解控制器内部数据流和控制流的关键。
2.1 错误控制与完整性保障层
这是最底层,也是确保系统长期稳定运行的基石。输入材料中提到的MMCSD_ECC_AGGR_TXMEM_DED_*一系列寄存器,就是典型代表。DED代表“双位错误检测”(Double Error Detection),这是ECC(纠错码)的一种严重错误类型。单比特错误可以纠正,双比特错误只能检测无法纠正,通常意味着内存物理故障或严重的信号完整性问题。
这一组寄存器采用了嵌入式系统中常见的中断聚合(Aggregation)设计模式。为什么需要聚合?想象一下,控制器内部可能有多个TX Memory(发送内存)单元,每个都可能产生ECC错误。如果每个错误都产生一个独立的中断,中断服务程序(ISR)就需要频繁查询多个状态寄存器,效率低下。聚合机制将这些分散的错误源状态汇集起来,通过一个统一的“开关”和“状态”寄存器来管理。
MMCSD_ECC_AGGR_TXMEM_DED_STATUS_REG0:这是“火警报警器”。它的TXMEM_PEND位就是聚合后的中断挂起状态位。当任何一个被监控的TX Memory发生双位ECC错误时,此位被硬件置1。驱动在ISR中首先读取这个寄存器,确认是ECC错误触发了中断。MMCSD_ECC_AGGR_TXMEM_DED_ENABLE_SET/CLR_REG0:这是“报警器的开关”。TXMEM_ENABLE_SET和TXMEM_ENABLE_CLR位用于使能或禁用上述中断。通常初始化时通过SET寄存器使能,在需要关闭中断时使用CLR寄存器。注意它们的类型是R/W1TS(写1置位)和R/W1TC(写1清除),这意味着你写0是无效的,必须写1才能改变状态。这是一个重要的防误操作设计。MMCSD_ECC_AGGR_TXMEM_DED_EOI_REG:这是“报警器复位按钮”。EOI(End Of Interrupt)是中断控制器中的常见概念。在处理完中断后,向EOI_WR位写1,通知中断控制器该中断已处理完毕,可以接收新的同类中断。忘记写EOI是导致中断丢失或只触发一次的常见错误。MMCSD_ECC_AGGR_TXMEM_AGGR_*寄存器:这组寄存器管理更广泛的聚合错误,如PARITY(奇偶校验错)和TIMEOUT(总线超时)。它们的使能(ENABLE_SET/CLR)和状态(STATUS_SET/CLR)是分开的,并且状态寄存器的位宽可能更大(如TIMEOUT占2位),可能用于表示不同严重等级或来源的超时。
实操心得:错误处理策略在实际驱动中,对于ECC DED错误,我的策略是“记录并降级”。一旦检测到,除了在日志中记录错误地址(通常需要通过其他调试寄存器获取)外,我会尝试重试当前操作。如果连续发生,则可能标记该存储区域为不可靠,甚至触发系统预警。切勿简单地清除中断后忽略它,双位错误往往是硬件故障的先兆。
2.2 DMA传输与数据搬运层
这是性能的关键。SD卡读写,尤其是大文件操作,核心瓶颈往往在数据搬运。MMCSD控制器支持两种DMA模式:传统的SDMA(Simple DMA)和更先进的ADMA(Advanced DMA)。输入材料主要涉及SDMA相关寄存器。
- 地址与块定义:
MMCSD_CTL_CFG_SDMA_SYS_ADDR_LO/HI构成了32位的系统内存物理地址。这里有个关键细节:当Host Version 4 Enable启用时,这个寄存器被重定义为32位块计数(Block Count),而DMA地址则使用另一组ADMA System Address寄存器以支持64位寻址。在初始化时,必须根据你使用的Host Controller版本和DMA模式,正确解读这个寄存器的角色。 - 数据块规格:
MMCSD_CTL_CFG_BLOCK_SIZE寄存器低12位XFER_BLK_SIZE定义了每个数据块的大小(比如512字节或2048字节)。高3位SDMA_BUF_SIZE定义了SDMA的缓冲区边界。这是SDMA的一个特点:它要求物理内存是连续的,并且在达到这个边界时,DMA会暂停并产生中断,要求驱动更新SDMA_SYS_ADDR寄存器以指向下一块连续内存。这限制了SDMA的使用灵活性,也是ADMA被推荐用于高性能场景的原因。 - 传输量控制:
MMCSD_CTL_CFG_BLOCK_COUNT寄存器定义了要传输的块数。手册明确指出,它只在多块传输(MULTI_BLK_SEL=1)且块计数使能(BLK_CNT_ENA=1)时才有效。设置为0意味着不传输任何数据块。一个常见的坑是:如果你要进行“无限”流传输(例如音频实时录制),需要将BLK_CNT_ENA设为0,并通过其他方式(如检查卡状态)来停止传输。
2.3 命令与协议控制层
这一层是大脑,它告诉控制器“做什么”和“怎么做”。TRANSFER_MODE和COMMAND寄存器是核心。
MMCSD_CTL_CFG_TRANSFER_MODE:此寄存器配置传输的宏观行为。DMA_ENA:开关DMA引擎。BLK_CNT_ENA:是否使用BLOCK_COUNT寄存器。AUTO_CMD_ENA:这是优化性能的关键。01b启用Auto CMD12,用于在多块传输结束时自动发送停止命令。10b启用Auto CMD23,它在多块传输开始前自动发送设置块数的命令,尤其适合与ADMA配合。手册强调,对于支持CMD23的卡(通过SCR寄存器查询),应优先使用Auto CMD23,因为它更高效。DATA_XFER_DIR:方向控制。MULTI_BLK_SEL:单块/多块选择。RESP_TYPE,RESP_ERR_CHK_ENA,RESP_INTR_DIS:这是一组高级特性。你可以选择让控制器自动检查命令响应(R1/R5)中的错误,并直接产生错误中断,从而减轻驱动负担。
MMCSD_CTL_CFG_COMMAND:此寄存器用于发起具体的SD命令。CMD_INDEX:命令号,如CMD17(读单块)、CMD18(读多块)、CMD24(写单块)、CMD25(写多块)。CMD_TYPE:定义特殊命令(挂起、恢复、中止),用于复杂的电源管理或错误恢复流程。DATA_PRESENT:指示此命令是否伴随数据传输。对于CMD12(停止传输)或某些使用DAT线忙信号的命令(如CMD38,擦除),此位应设为0,尽管它们“使用”了DAT线。CMD_INDEX_CHK_ENA和CMD_CRC_CHK_ENA:启用对响应中命令索引和CRC的校验,提高通信可靠性。RESP_TYPE_SEL:选择期望的响应类型(无响应、136位、48位等)。
注意事项:命令序列化手册在
PRESENTSTATE寄存器的INHIBIT_CMD和INHIBIT_DAT位描述中埋了一个重要约束:当INHIBIT_CMD为1时,控制器正忙于处理上一条命令,此时写入COMMAND寄存器是无效的。驱动必须轮询此位变为0后,才能发送下一条命令。对于数据命令,还需检查INHIBIT_DAT。忽略这个状态检查是导致命令丢失或系统挂起的最常见原因之一。
3. 实操流程:配置一次完整的DMA写卡操作
理论清晰后,我们来看如何将这些寄存器组合起来,完成一次实际的多块数据DMA写入。假设我们要将系统内存0x80000000处的数据,写入SD卡,共10个块,每块512字节。
3.1 第一阶段:传输参数配置
在发起任何命令之前,必须先配置好DMA和块参数。这个阶段不涉及卡的总线通信。
配置DMA系统地址:由于我们使用SDMA,且假设Host Version 4未启用,所以
SDMA_SYS_ADDR_LO/HI寄存器用作地址。- 写入
MMCSD_CTL_CFG_SDMA_SYS_ADDR_LO:0x0000(地址的低16位:0x80000000 & 0xFFFF) - 写入
MMCSD_CTL_CFG_SDMA_SYS_ADDR_HI:0x8000(地址的高16位:0x80000000 >> 16)
- 写入
配置块大小与SDMA边界:
- 写入
MMCSD_CTL_CFG_BLOCK_SIZE。我们需要设置XFER_BLK_SIZE为512(十六进制0x200)。同时,根据你的系统内存布局,设置SDMA_BUF_SIZE。例如,如果你的内存以4KB为边界对齐,那么缓冲区大小是12位(因为SDMA_BUF_SIZE是3位字段,其值N代表缓冲区大小为4KB * 2^N,若N=0,即为4KB)。假设我们设为0。 - 计算寄存器值:
(0 << 12) | 0x200=0x0200。写入寄存器。
- 写入
配置块数量:
- 写入
MMCSD_CTL_CFG_BLOCK_COUNT:0x000A(十进制10)。
- 写入
3.2 第二阶段:传输模式与命令配置
接下来,配置控制器以何种方式执行传输,并填充命令参数。
配置传输模式:
- 写入
MMCSD_CTL_CFG_TRANSFER_MODE。我们需要:使能DMA (DMA_ENA=1)、使能块计数(BLK_CNT_ENA=1)、选择多块传输(MULTI_BLK_SEL=1)、方向为写(DATA_XFER_DIR=0,主机到卡)。为了高效,我们启用Auto CMD23 (AUTO_CMD_ENA=10b)。同时,为了简化驱动,我们让控制器检查响应错误(RESP_ERR_CHK_ENA=1),并禁用响应完成中断(RESP_INTR_DIS=1),这样错误会通过错误中断统一报告。响应类型选择内存卡(RESP_TYPE=0)。 - 计算寄存器值:
(0 << 8) | (1 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (2 << 2) | (1 << 1) | (1 << 0)=0x00A7。注意位域位置需参考手册图表。写入寄存器。
- 写入
配置命令参数:
- 对于写多块命令CMD25,其命令索引是25(十六进制
0x19)。我们将它写入MMCSD_CTL_CFG_ARGUMENT1_LO/HI。通常CMD25的参数是卡内起始存储地址(以字节为单位,但需按块对齐)。假设我们要从卡内地址0x00010000开始写。那么:ARGUMENT1_LO=0x0000ARGUMENT1_HI=0x0001
- 关键一步:配置Auto CMD23的参数。当
AUTO_CMD_ENA设为10b时,控制器会在发送CMD25前自动发送CMD23。CMD23的参数(块数)需要预先设置。根据手册,在Host Version 4 Enable为1时,SDMA_SYS_ADDR寄存器被重用作32位块计数。但为了兼容性,TI可能通过其他机制或固定使用SDMA_SYS_ADDR寄存器组来传递这个参数。在典型SDMA+Auto CMD23的配置中,块数(10)我们已经设置在了BLOCK_COUNT寄存器,控制器内部可能会自动使用这个值作为CMD23的参数。更稳妥的做法是查阅芯片勘误表或应用笔记确认。有些实现中,CMD23的参数需要软件通过另一个命令预先发送。
- 对于写多块命令CMD25,其命令索引是25(十六进制
填充命令寄存器:
- 写入
MMCSD_CTL_CFG_COMMAND。设置:CMD_INDEX=25(0x19),DATA_PRESENT=1,CMD_TYPE=00b(普通命令),启用命令索引和CRC检查(CMD_INDEX_CHK_ENA=1,CMD_CRC_CHK_ENA=1),响应类型选择48位带忙(RESP_TYPE_SEL根据手册对应值,例如01b)。SUB_CMD对于主命令设为0。 - 计算寄存器值:
(0x19 << 8) | (1 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (1 << 0)(假设RESP_TYPE_SEL=01b)。注意位域偏移。假设结果为0x19B0。写入寄存器。
- 写入
3.3 第三阶段:启动传输与状态监控
启动传输:写入
COMMAND寄存器的操作本身,在配置好所有前置寄存器后,就会触发控制器开始执行命令序列(先发Auto CMD23,再发CMD25)并启动DMA传输。轮询状态:驱动此时不应干等,而应进入中断服务模式或轮询状态寄存器。
- 轮询法:持续读取
MMCSD_CTL_CFG_PRESENTSTATE寄存器。关注几个关键位:INHIBIT_CMD和INHIBIT_DAT:在传输过程中,它们可能为1。当传输完成,INHIBIT_DAT会变为0,并产生传输完成中断(如果使能了)。WR_XFER_ACTIVE:写传输活跃位,为1表示正在传输。BUF_WR_ENA:对于非DMA写,此位表示缓冲区可写。对于DMA,此位意义不大。
- 中断法(推荐):配置好相应中断使能寄存器(如Normal Interrupt Status Enable Register),使能“传输完成”、“命令完成”、“错误”等中断。在中断服务例程中:
- 读取Normal Interrupt Status Register,判断中断来源。
- 如果是传输完成中断,检查是否有错误。
- 如果是错误中断,则读取Error Interrupt Status Register和
PRESENTSTATE寄存器(特别是CMD_NOT_ISS_BY_ERR位)进行详细错误诊断。 - 清除中断状态位(通常通过写1清除)。
- 如果是SDMA,在缓冲区边界中断时,需要更新
SDMA_SYS_ADDR寄存器。
- 轮询法:持续读取
处理完成与错误:
- 传输成功完成后,
BLOCK_COUNT寄存器应减为0,SDMA_SYS_ADDR寄存器指向最后一个传输字节之后的下一个地址(可用于连续传输)。 - 如果发生错误,如ECC错误,
MMCSD_ECC_AGGR_TXMEM_DED_STATUS_REG0的TXMEM_PEND位会被置1。在错误处理中,除了记录日志,可能需要重试操作,或检查硬件连接(如SD卡座接触、电源噪声)。
- 传输成功完成后,
4. 深度解析:关键寄存器位域与联动逻辑
要真正驾驭这些寄存器,必须理解一些关键位域的细微之处和它们之间的联动关系。
4.1 ECC中断的使能与清除机制
MMCSD_ECC_AGGR_TXMEM_DED_ENABLE_SET_REG0和ENABLE_CLR_REG0的R/W1TS/R/W1TC类型值得深入讨论。这种设计强制软件进行显式的“置位”和“清除”操作,避免了因误写0而意外关闭中断。标准的初始化流程是:
// 1. 清除任何可能已挂起的中断状态(通过状态寄存器或全局中断清除) // 2. 使能ECC DED中断 MMCSD_ECC_AGGR_TXMEM_DED_ENABLE_SET_REG0 = 0x00000001; // 写1到bit0 (TXMEM_ENABLE_SET) // 3. 在全局中断控制器中使能该中断线在中断服务例程中:
// 1. 读取状态寄存器,确认是TXMEM DED错误 if (MMCSD_ECC_AGGR_TXMEM_DED_STATUS_REG0 & 0x1) { // 2. 处理错误... // 3. 清除中断挂起位(通常向状态位写1清除,但需查手册确认。有时有单独的清除寄存器) // 4. 写EOI寄存器,通知中断控制器处理完毕 MMCSD_ECC_AGGR_TXMEM_DED_EOI_REG = 0x00000001; // 写1到bit0 (EOI_WR) }联动陷阱:AGGR_ENABLE_SET/CLR和AGGR_STATUS_SET/CLR这两组寄存器管理着PARITY和TIMEOUT错误。它们与DED_*寄存器是独立的通道。这意味着你需要分别使能和处理这两类错误。一个健壮的驱动应该同时使能并监控它们。
4.2 SDMA与ADMA的地址寄存器歧义
MMCSD_CTL_CFG_SDMA_SYS_ADDR_LO/HI的“双重身份”是容易混淆的地方。其行为完全由Host Control 2寄存器中的Host Version 4 Enable位决定。
| Host Version 4 Enable | SDMA_SYS_ADDR_LO/HI 用途 | DMA 地址寄存器 | 块计数寄存器 |
|---|---|---|---|
| 0 | SDMA系统地址(低32位) | SDMA_SYS_ADDR_LO/HI | BLOCK_COUNT (16位) |
| 1 | 32位块计数 | ADMA System Address Register | SDMA_SYS_ADDR_LO/HI + BLOCK_COUNT? |
关键决策点:在驱动初始化时,你必须检测或配置Host Controller的版本。如果支持V4.10且你计划使用ADMA(性能更好,支持64位地址和描述符链表),那么你需要:
- 设置
Host Version 4 Enable = 1。 - 使用
ADMA System Address寄存器设置描述符表地址。 - 将
SDMA_SYS_ADDR_LO/HI作为32位块计数寄存器使用(如果块数超过65535)。 - 注意,此时
BLOCK_COUNT寄存器的角色手册描述有些模糊,可能需要设置为0以激活32位块计数模式。务必参考最新的芯片勘误表和驱动示例代码。
4.3 Auto CMD12 与 Auto CMD23 的抉择
TRANSFER_MODE寄存器中的AUTO_CMD_ENA字段是优化多块传输的关键。
- Auto CMD12 (01b):这是传统模式。在多块传输(CMD18/CMD25)的最后一个数据块传输完成后,硬件自动发送一个CMD12命令来终止传输。它的优点是兼容性好。缺点是,对于需要精确控制块数或与预定义命令序列(如CMD23)配合的场景不够灵活。
- Auto CMD23 (10b):这是推荐模式,尤其对于支持CMD23的SD卡(大部分现代卡都支持)。它在开始多块传输前,自动发送一个CMD23命令,该命令的参数指定了要传输的块数。这样,卡端提前知道传输总量,可以进行更好的内部优化。它与DMA(特别是ADMA)的配合是天衣无缝的,因为块数在传输开始前就已确定。
- Auto CMD Auto Select (11b, 如果支持):这是V4.10引入的智能模式。控制器自动检测卡是否支持CMD23(通过之前查询命令获取的SCR寄存器信息),并自行选择使用Auto CMD23还是Auto CMD12。这简化了驱动逻辑。
配置建议:
- 初始化时,通过发送CMD55+ACMD51读取SCR寄存器,检查卡是否支持CMD23(SCR bit[33])。
- 如果支持,将
AUTO_CMD_ENA设置为10b(或11b如果支持自动选择)。 - 确保在发送多块读写命令(CMD18/CMD25)前,正确的块数已经设置(对于Auto CMD23,块数需要通过CMD23的参数传递,具体位置需确认,可能是
SDMA_SYS_ADDR寄存器或特定寄存器)。 - 如果卡不支持CMD23,则回退到
01b(Auto CMD12)。
4.4 PRESENTSTATE 寄存器的状态机解读
MMCSD_CTL_CFG_PRESENTSTATE是一个只读的状态窗口,驱动的大部分决策都基于它。理解其位域间的状态机至关重要。
- 命令与数据抑制位 (
INHIBIT_CMD,INHIBIT_DAT):这是驱动流程控制的基石。一个安全的命令发送函数应该如下:int mmcsd_send_command(uint16_t cmd, uint32_t arg) { // 1. 等待CMD线空闲 while (PRESENTSTATE & (1 << 0)) { // INHIBIT_CMD // 可加入超时和错误处理 } // 2. 如果命令涉及数据,等待DAT线空闲 if (cmd_has_data(cmd)) { while (PRESENTSTATE & (1 << 1)) { // INHIBIT_DAT // 可加入超时和错误处理 } } // 3. 填充ARGUMENT和COMMAND寄存器 ARGUMENT1_LO = arg & 0xFFFF; ARGUMENT1_HI = (arg >> 16) & 0xFFFF; COMMAND = cmd_reg_value; return 0; } - 缓冲区状态位 (
BUF_RD_ENA,BUF_WR_ENA):这两个位主要针对PIO(非DMA)模式。在DMA模式下,数据直接在内核和卡之间搬运,不经过软件可见的缓冲区,因此这两个位通常无效或反映内部FIFO状态。在PIO模式下,当BUF_WR_ENA变为1时,表示可以往DATA_PORT寄存器写入一个数据块(通常是32位);当BUF_RD_ENA变为1时,表示可以从DATA_PORT读取一个数据块。驱动需要根据这些状态位来同步数据吞吐。 - 传输活跃位 (
RD_XFER_ACTIVE,WR_XFER_ACTIVE):这两个位清晰地指示了读/写传输是否在进行中。它们的变化边沿会触发中断。例如,WR_XFER_ACTIVE从1变0,表示所有数据块都已发送给卡,并且卡已经响应了最终的CRC状态(即使卡可能还在内部编程)。此时可以认为主机端的写入事务已完成。 - 卡状态与调试位 (
CARD_INSERTED,CARD_DETECT,SDIF_DATxIN):CARD_INSERTED是经过去抖动的卡插入状态,其变化会产生中断。CARD_DETECT直接反映SDCD#引脚的电平,可能包含毛刺。SDIF_DATxIN直接读取DAT线的电平,在调试CMD线不通、CRC错误或卡忙信号问题时极其有用。例如,写操作后卡通过DAT0拉低表示忙,你可以轮询SDIF_DAT0IN直到它变高。
5. 常见问题排查与调试技巧
在实际开发中,寄存器配置看似正确,但传输失败是家常便饭。以下是我总结的一些常见问题场景和排查手段。
5.1 DMA传输启动失败或卡住
- 症状:配置好所有寄存器,写入
COMMAND寄存器后,INHIBIT_CMD一直为1,无任何进展,也不进入中断。 - 排查步骤:
- 检查电源和时钟:确保
Power Control和Clock Control寄存器已正确配置,SD卡供电稳定,时钟已使能并达到正确频率。 - 检查卡识别阶段:DMA传输前,必须完成卡的初始化和识别(CMD0, CMD8, ACMD41等)。确认卡已处于传输状态(CMD7选中卡)。
- 检查
PRESENTSTATE:CARD_INSERTED和CARD_DETECT是否为1?确保卡在位。INHIBIT_DAT是否为0?可能上一个数据命令未完成。
- 检查命令响应:即使DMA使能,命令本身也会有响应。使能命令完成中断,或轮询
INHIBIT_CMD变为0后,读取RESPONSE寄存器,检查是否有响应错误(如CMD索引错误、CRC错误)。这可能是卡不支持该命令或参数错误。 - 检查Auto CMD配置:如果使用了Auto CMD23,但卡不支持CMD23,会导致命令序列失败。检查SCR寄存器或回退到Auto CMD12。
- 检查DMA地址对齐:SDMA对系统内存地址有对齐要求(通常与
SDMA_BUF_SIZE设置的边界对齐)。确保地址是缓存行对齐的(如32字节)。使用未对齐地址可能导致不可预知的行为。 - 检查中断状态:读取
Normal Interrupt Status和Error Interrupt Status寄存器。可能有错误中断产生了但未被处理。常见的错误是“Command Not Issued By Error”或“Auto CMD Error”。
- 检查电源和时钟:确保
5.2 数据传输中出现CRC错误或数据错误
- 症状:传输能启动,但中途失败,错误中断状态显示CRC错误或数据错误。
- 排查步骤:
- 降低时钟频率:这是首要步骤。过高的SD时钟频率在PCB布线不佳、阻抗不匹配或电源噪声下极易出错。尝试将时钟分频比调大,降低频率。
- 检查信号完整性:使用示波器观察CMD和DAT线。看波形是否有过冲、振铃或边沿过于缓慢。检查上拉电阻是否合适。
- 检查
PRESENTSTATE中的RETUNING_REQ位:在高速模式(如SDR104)下,温度漂移可能导致采样点偏移。如果此位为1��需要执行再调谐(Tuning)流程,即发送CMD19或CMD21。确保Host Control 2寄存器中的Execute Tuning位和Sampling Clock Select位已正确配置。 - 检查DMA缓冲区内存属性:确保提供给DMA的内存区域是非缓存(Non-cacheable)或者已经正确执行了缓存维护操作(Clean & Invalidate)。CPU缓存中的数据如果没有写回内存,DMA控制器读到的是旧数据;同样,DMA写入内存的数据如果还在CPU缓存中,CPU读到的是旧数据。这是嵌入式系统DMA调试中最经典的坑。
- 检查ECC/奇偶校验错误:如果错误是随机的,读取
MMCSD_ECC_AGGR_TXMEM_DED_STATUS_REG0和MMCSD_ECC_AGGR_TXMEM_AGGR_STATUS_SET寄存器,看是否有ECC双位错误或奇偶错误。这可能指向控制器内部TX Memory的硬件问题或严重的电源噪声。
5.3 Auto CMD23不生效
- 症状:配置了
AUTO_CMD_ENA=10b,但控制器似乎没有发送CMD23,或者多块传输无法正确停止。 - 排查步骤:
- 确认卡支持CMD23:通过发送CMD55+ACMD51读取SCR寄存器,确认bit[33]为1。
- 确认Host Controller版本:Auto CMD23需要Host Controller版本在3.0及以上。检查
Host Controller Version寄存器。 - 确认块数已设置:对于Auto CMD23,需要提前设置好要传输的块数。这个块数设置在哪里?根据手册描述,在V4.10使能时,是
SDMA_SYS_ADDR寄存器。在V4.10未使能时,可能需要通过其他方式。最可靠的方法是:在发送主命令(CMD18/25)之前,手动发送一次CMD23命令来设置块数。这可以验证CMD23本身是否被卡接受。 - 检查命令序列:使用逻辑分析仪抓取SD总线波形,直接观察控制器是否在CMD18/25之前发出了CMD23。这是最直接的证据。
5.4 中断无法触发或频繁触发
- 症状:配置了中断使能,但预期中断不来;或者中断频繁发生,甚至无法退出。
- 排查步骤:
- 中断使能金字塔:MMCSD的中断使能是分层的。首先,在MMCSD控制器内部,需要使能特定的中断类型(如Normal Interrupt Enable Register)。其次,该中断信号需要连接到处理器的中断控制器(如GIC)。最后,在处理器全局中断使能。确保这三层都已打开。
- 清除中断状态:在中断服务例程(ISR)中,必须读取并清除MMCSD内部的中断状态寄存器(通常通过写1清除相应位)。如果忘记清除,中断状态会一直保持,导致中断持续触发或无法接收新中断。
- EOI操作:对于聚合错误中断(如ECC DED),在ISR末尾需要写
EOI寄存器。忘记写EOI会导致中断控制器认为该中断仍在处理,从而屏蔽后续同类中断。 - 中断风暴:如果中断过于频繁,检查是否是错误状态持续产生(如持续的CRC错误)。降低时钟频率,检查硬件连接。也可能是ISR处理太慢,导致中断堆积。可以考虑在ISR中仅做最低限度的状态保存和标志设置,将耗时处理放到任务线程中。
调试这类底层驱动,逻辑分析仪和芯片手册的时序图是你的最佳伙伴。抓取CMD和DAT线上的实际波形,与SD物理层规范对比,能发现绝大多数协议层面的问题。同时,养成在关键寄存器读写处添加详细日志的习惯,记录下配置值和状态值,这对于追踪偶发性故障至关重要。