深入解析MC68SZ328 MMC/SD控制器:从寄存器编程到安全机制实战
1. 项目概述
在嵌入式系统开发中,存储卡接口是连接微控制器与外部存储设备的关键技术。无论是工业控制中的数据日志记录,还是消费电子产品中的多媒体存储,MMC/SD协议都扮演着核心角色。这套协议的精髓在于其标准化的命令集和严谨的状态管理机制,主机通过发送一系列命令(如CMD7选择卡、CMD16设置块长度)并解析卡的响应(如R1格式的卡状态寄存器),来实现对存储卡的初始化、读写操作乃至安全管理。其技术价值不仅在于提供了一种可靠、高效的块设备访问方式,更在于通过密码锁定、强制擦除等安全功能,为嵌入式应用中的数据完整性筑起了防线。
本文将以Motorola(现NXP)经典的MC68SZ328微控制器中的MMC/SD主机控制器模块为例,进行一次深度的技术剖析。我们不会停留在手册的简单翻译层面,而是结合我多年在嵌入式存储驱动开发中的实际经验,深入解析其命令执行的内在逻辑、状态寄存器的每一个比特位所代表的现场信息,以及如何通过精准的寄存器编程来驾驭整个通信流程。你会发现,理解这些底层细节,对于解决实际开发中遇到的“卡初始化失败”、“读写超时”、“密码锁定异常”等问题至关重要。无论你是正在为老旧设备维护驱动,还是在新平台上进行MMC/SD接口的裸机开发,这篇文章都将为你提供可直接参考的编程模型和避坑指南。
2. 核心机制深度解析
要驾驭MC68SZ328的MMC/SD控制器,不能仅仅满足于知道寄存器地址和命令列表。我们必须深入理解其设计哲学和核心交互机制,这就像学习一门语言的语法和语境,远比死记硬背单词表来得重要。
2.1 命令-响应模型:通信的基石
MMC/SD协议本质上是一种主从式的同步串行通信。主机(MC68SZ328)始终是对话的发起者,它通过CMD线发送一个命令帧,卡则在之后通过同一条CMD线返回一个响应帧。这个过程看似简单,但细节决定成败。
一个命令帧由48位组成:起始位(总是0)、传输位(1代表主机到卡)、命令索引(6位,即CMD0~CMD63)、命令参数(32位)以及7位CRC校验和结束位(总是1)。响应帧的格式则根据命令类型不同而有所差异,常见的有R1(48位,包含32位卡状态)、R2(136位,用于发送CID或CSD)、R3(48位,发送OCR寄存器内容)等。
在MC68SZ328的编程模型中,你需要通过CMD寄存器(0xFFFE032C)写入命令索引,通过ARGUMENTH和ARGUMENTL寄存器(0xFFFE0330, 0xFFFE0332)组合成32位参数。控制器硬件会自动为你组装成完整的命令帧并发送。关键在于CMD_DAT_CONT寄存器(0xFFFE0310)的FRES字段,你必须根据你期望的响应类型正确设置它(例如,001对应R1格式)。如果设置错误,控制器将无法正确解析卡返回的响应,导致通信失败。一个常见的错误是在发送CMD2(ALL_SEND_CID)时,仍然使用R1的响应格式设置,实际上应该设置为R2(010)。
2.2 卡状态寄存器:诊断的眼睛
卡在R1响应中返回的32位状态字,是诊断一切通信问题的核心。手册中的表17-4是一个宝库,但我们需要更动态地理解它。
状态位可以分为几个大类:错误指示位(如OUT_OF_RANGE,ADDRESS_ERROR,COM_CRC_ERROR)、卡状态位(如CARD_IS_LOCKED,CURRENT_STATE)和操作状态位(如READY_FOR_DATA)。它们的“清除条件”尤其关键:
- Clear by read (C):这类错误位(如
COM_CRC_ERROR)一旦发生就会锁存,直到主机通过发送CMD13(SEND_STATUS)命令读取状态寄存器后才会清除。如果你不读,这个错误标志会一直存在,干扰后续的状态判断。 - Always related to previous command (B):这类状态(如
CURRENT_STATE)总是针对上一个命令。一旦收到一个新的有效命令,它就会被更新。这意味着你不能缓存上一次CMD13读取的状态来代表当前状态。 - According to card’s current state (A):这类状态(如
CARD_IS_LOCKED)实时反映卡的当前状况,与命令读取无关。
在实际调试中,我习惯在每次重要操作(如写块、擦除)后,主动发送CMD13读取状态,并解析关键的几个位:READY_FOR_DATA(判断卡是否忙完)、CURRENT_STATE(确认卡是否进入了期望的状态,例如写操作后应从prg态回到tran态),以及检查是否有COM_CRC_ERROR或ILLEGAL_COMMAND等错误。这比盲目重试或长时间等待超时要高效得多。
2.3 安全与锁定机制:数据保护的阀门
手册中提到的密码锁定(Lock/Unlock)和强制擦除(Forced Erase)功能,是MMC/SD协议中容易被忽略但非常重要的安全特性。它们通过CMD42(LOCK_UNLOCK)实现。
密码锁定流程:首先,你需要通过CMD16将块长度设置为1字节。然后发送CMD42,并在数据线上伴随一个1字节的数据块。这个数据块的内容决定了操作:设置密码、锁定卡、解锁卡。密码本身则是在另一个独立的密码设置命令的数据阶段传输。这里的关键点是,锁定状态由卡内部的CARD_IS_LOCKED状态位标识。主机尝试访问一个已锁定的卡进行数据读写或擦除时,卡会拒绝操作并在状态寄存器中设置LOCK_UNLOCK_FAILED错误位。
强制擦除流程:这是忘记密码时的“终极手段”。操作序列与锁定/解锁类似,但在CMD42伴随的1字节数据块中,必须且只能设置ERASE位。手册特别强调,如果该字节中除了ERASE位还有其他位被置位,卡会拒绝请求并上报LOCK_UNLOCK_FAILED错误。这个设计防止了误操作。一旦强制擦除被接受,卡内的所有用户数据、密码以及密码长度寄存器都会被永久擦除,卡同时被解锁。
实操心得:在实现锁定/解锁功能时,务必在每一步后检查卡状态。例如,发送设置密码的CMD42后,应立即发送CMD13确认无
LOCK_UNLOCK_FAILED错误。在消费类产品中,如果涉及用户密码,一定要在UI层提供明确的“忘记密码”选项,其背后就是触发这个强制擦除流程,同时要向用户明确提示这将销毁所有数据。
3. 编程模型与寄存器精讲
理解了核心机制后,我们进入实战环节:如何通过MC68SZ328的寄存器来具体操控这一切。这部分内容直接对应手册的第17.7节,但我会加入大量手册未明说、却在调试中至关重要的细节。
3.1 控制器初始化与时钟配置
任何操作开始前,必须正确初始化和使能MMC/SD控制器模块。这个过程有严格的顺序要求,手册在STR_STP_CLK寄存器的描述里用警告框给出了“魔法序列”。
关键步骤与原理:
检查时钟状态:在配置时钟前,必须先读取
STATUS寄存器(0xFFFE0304),确认MMCSDCR位为0,表示MMC/SD时钟已停止。这是前提,否则对时钟寄存器的写入可能无效。执行使能序列:向
STR_STP_CLK寄存器(0xFFFE0300)依次写入:0x0008: 二进制0000 0000 0000 1000。这设置了SYSRST位(bit 3),对模块进行软复位。0x000D: 二进制0000 0000 0000 1101。这同时设置了SYSRST和MMCSDEN(bit 2)以及STOP_CLK(bit 0)。在复位后使能模块,并确保时钟停止。0x0005: 二进制0000 0000 0000 0101。这清除了SYSRST,保持了MMCSDEN使能,并清除了STOP_CLK(同时START_CLK位为0,表示时钟由内部状态机控制)。至此,模块使能完成。
注意事项:���个序列必须严格遵守,错序或遗漏都可能导致控制器无法正常工作。我曾在早期项目中因遗漏第二步导致时钟无法启动,排查了很久。
配置时钟速率:通过
CLK_RATE寄存器(0xFFFE0308)配置。时钟频率计算公式为:MMCSD_CLK = SYSCLK / (PRESCALER * CLOCKRATE)。PRESCALER(Bits 5-3):预分频器,取值1-5(某些编码对应相同分频,如010和011都对应/3)。CLOCKRATE(Bits 2-0):时钟分频器,取值1, 2, 4, 8, 16, 32, 64。- 计算示例:假设系统时钟
SYSCLK为33MHz,目标SD卡时钟MMCSD_CLK为400kHz(初始化阶段常用频率)。- 尝试设置:
PRESCALER= 5,CLOCKRATE= 16。 - 计算:
33,000,000 / (5 * 16) = 412,500 Hz,接近400kHz,可接受。 - 对应寄存器值:
PRESCALER字段写101B(5),CLOCKRATE字段写100B(16)。PRESCALER位于bit5-3,需要左移3位:5 << 3 = 0x28。CLOCKRATE位于bit2-0:16的二进制10000只取低3位是000B,但注意手册表格,100B对应/16,所以这里应写入0x04。两者合并:0x28 | 0x04 = 0x2C。但查看手册复位值0x0036(即PRESCALER=110B(6?实际对应/5), CLOCKRATE=110B(6?实际对应/64)),说明编码需要查表转换。根据表17-9,PRESCALER的110对应SYSCLK/5,CLOCKRATE的110对应CLK/64。因此,要设置PRESCALER=5(即101B),CLOCKRATE=16(即100B),则寄存器值应为:(5<<3) | 4=0x28 | 0x04 = 0x2C。写入CLK_RATE寄存器即可。
- 尝试设置:
3.2 命令发送与数据控制流程
这是主机与卡交互的核心。我们以发送一个“读取单个块”命令(CMD17)为例,拆解整个过程。
步骤分解:
- 设置块长度:虽然CSD中有默认块长度,但显式设置是一个好习惯。向
BLK_LEN寄存器(0xFFFE031C)写入0x0200(512字节)。 - 配置命令控制:设置
CMD_DAT_CONT寄存器(0xFFFE0310)。FRES[2:0]: 期望响应格式。对于CMD17,卡返回R1,所以设置为001。DATEN: 此命令包含数据阶段(我们要读数据),所以置1。WRRD: 这是读操作,所以置0。STRBLK: 单块读是块模式,不是流模式,所以置0。BUSW: 根据实际硬件连接设置总线宽度,1位模式为00,4位模式为10。- 其他位(如
INIT,BSY,STPRDW,STRRW,CRLOFF)通常在此类操作中保持0。 - 假设使用1位总线,不期待忙信号,则计算值:
FRES=001b=1,DATEN=1,WRRD=0,STRBLK=0,BUSW=00。合并:(1<<0) | (1<<3) = 0x0009。
- 设置超时:配置
RES_TO(响应超时)和READ_TO(读数据超时)。RES_TO一般可设为默认值64个时钟周期或稍大。READ_TO用于数据读取超时,单位是(主时钟/256),需要根据时钟频率和块大小计算一个安全值。例如,在400kHz时钟下读取512字节,理论最小时间约为(512*8) / 400,000 ≈ 10ms。换算成时钟周期数再除以256,得到一个较大的值,比如0xFFFF(最大值)在初始化阶段是安全的。 - 填充命令与参数:向
CMD寄存器(0xFFFE032C)写入命令索引17。向ARGUMENTH和ARGUMENTL寄存器写入要读取的数据块的32位地址。 - 触发命令发送:向
CMD寄存器写入操作本身,通常就会触发硬件开始发送命令帧。有些平台可能需要写一个特定的触发位,但MC68SZ328看来是写入即触发。 - 等待与响应处理:
- 轮询
STATUS寄存器(0xFFFE0304)的ECR位,等待其置1,表示命令响应已接收。 - 同时检查
RCRCERR和TORERR位,确保无错误。 - 从
RES_FIFO寄存器(0xFFFE0334)读取响应内容(对于R1,是32位卡状态)。可能需要连续读取多次,因为这是一个8x16位的FIFO。读取到的状态字需要按手册表17-4解析,确认READY_FOR_DATA位为1,且无其他错误。
- 轮询
- 数据接收:命令响应无误后,控制器会自动进入数据接收阶段。此时应轮询
STATUS寄存器的DTD位,等待数据传送完成。同时,数据会通过BUFFER_ACCESS寄存器(0xFFFE0338)被读取。如果启用了DMA,则ABFE(应用缓冲FIFO空)或ABFF(满)位会触发DMA请求。
3.3 中断与DMA操作模式
对于高效的数据传输,使用中断和DMA是必不可少的。MC68SZ328的MMC/SD控制器提供了相应的支持。
中断模式:
- 通过
INT_MASK寄存器(0xFFFE0328)可以屏蔽或使能四种中断源:BUFRDY(缓冲就绪)、ECR(命令响应结束)、PDONE(编程完成)、DTRAN(数据传输完成)。 - 例如,在读取多块数据时,可以使能
BUFRDY中断。当内部FIFO缓冲区有数据可读(非空)时,会触发中断,在中断服务程序中从BUFFER_ACCESS寄存器读取数据。 - 关键细节:手册提到“Writing to this register twice allow the clearance of a current interrupt MMCSDIRQ”。这意味着清除中断标志需要向
INT_MASK寄存器执行两次写操作(写入任何值均可)。这是一个非常规设计,极易被忽略。通常的做法是,在中断服务程序(ISR)中,先读取STATUS寄存器确定中断源,处理完毕后,再向INT_MASK寄存器写入两次(例如,写入当前值)以清除硬件中断信号。
DMA操作:
- 当
ABFE(缓冲区空)或ABFF(缓冲区满)状态位变化时,控制器会向DMA控制器发出DMA_REQ_B请求信号。 - 在DMA模式下,主机CPU不直接读写
BUFFER_ACCESS寄存器,而是由DMA控制器自动完成数据在缓冲区与系统内存之间的搬运。 - 对于512字节的块,内部8x16位(即16字节)的FIFO需要32次DMA操作才能搬完一个块。你需要正确配置DMA控制器的传输计数和地址递增。
- 注意事项:在启动DMA传输前,务必确保
BLK_LEN和NOB(块数)寄存器已正确设置,并且CMD_DAT_CONT中的DATEN等位已配置好。DMA的开启时机通常是在发送了读写命令并收到成功响应之后。
4. 典型操作流程与代码实现
理论结合实践,我们来看几个关键的操作流程,并勾勒出大致的C语言代码框架。请注意,以下代码为示意性伪代码,需要根据你的具体编译器和硬件抽象层进行调整。
4.1 卡初始化流程
卡的初始化是一个标准序列,目的是让卡从空闲状态进入传输状态,并识别卡的类型(MMC或SD)。
// 假设已定义好所有寄存器地址的宏,如 MMC_STR_STP_CLK, MMC_STATUS 等 // 以及一些工具函数:mmc_write_reg, mmc_read_reg, mmc_delay int mmc_card_init(void) { uint16_t status; uint32_t ocr_response; int retry = 0; // 1. 控制器硬件初始化(执行魔法序列) mmc_write_reg(MMC_STR_STP_CLK, 0x0008); // 复位 mmc_delay(10); // 短暂延时 mmc_write_reg(MMC_STR_STP_CLK, 0x000D); // 使能并停时钟 mmc_delay(10); mmc_write_reg(MMC_STR_STP_CLK, 0x0005); // 清除复位,时钟交还状态机 mmc_delay(100); // 等待稳定 // 2. 配置低速时钟(400kHz或更低用于初始化) mmc_write_reg(MMC_CLK_RATE, 0x002C); // 示例:PRESCALER=5, CLOCKRATE=16 // 3. 发送CMD0(GO_IDLE_STATE),让所有卡进入空闲态 mmc_send_command(0, 0, 0x00); // CMD0,无参数,无响应期望(FRES=000) // 检查STATUS寄存器,确保无TORERR等错误 // 4. 发送CMD8(SEND_IF_COND)用于SD卡V2.0识别(参数0x1AA) mmc_send_command(8, 0x000001AA, 0x01); // 参数含供电电压信息,期望R7响应(格式同R1) // 如果卡响应且返回参数中的低8位也是0xAA,则可能是SD卡V2.0或���高版本 // 5. 发送ACMD41(SD_APP_OP_COND)或CMD1(MMC卡),带HCS位(Host Capacity Support)判断 uint32_t arg = 0x40FF8000; // 参数:支持高容量,电压范围3.2-3.4V等 do { if (is_sd_card) { // 根据CMD8响应判断 mmc_send_command(55, 0, 0x01); // 先发APP_CMD (CMD55) mmc_send_command(41, arg, 0x03); // 再发ACMD41,期望R3响应 } else { mmc_send_command(1, arg, 0x03); // MMC卡用CMD1,期望R3响应 } ocr_response = mmc_read_response(); // 从RES_FIFO读取OCR mmc_delay(10); retry++; if (retry > 1000) { // 超时处理 return -1; // 初始化失败 } } while (!(ocr_response & (1 << 31))); // 等待卡不再繁忙(OCR bit31) // 6. 如果是SD卡,通过ACMD41响应判断是否支持高容量(HCS) if (is_sd_card && (ocr_response & (1 << 30))) { card_type = CARD_TYPE_SDHC_SDXC; } // 7. 发送CMD2(ALL_SEND_CID)获取CID mmc_send_command(2, 0, 0x02); // 期望R2长响应 // 从RES_FIFO多次读取获取完整CID // 8. 发送CMD3(SET_RELATIVE_ADDR)为卡分配相对地址(RCA) mmc_send_command(3, 0x00010000, 0x01); // 示例:分配RCA=0x0001,期望R1响应 card_rca = 0x0001; // 9. 发送CMD9(SEND_CSD)获取CSD mmc_send_command(9, card_rca << 16, 0x02); // 期望R2响应 // 解析CSD获取块长度、容量等信息 // 10. 发送CMD7(SELECT_CARD)选择卡,使其进入传输状态 mmc_send_command(7, card_rca << 16, 0x01); // 期望R1b响应(带忙检查) // 等待卡不再繁忙(通过轮询STATUS或CMD13检查状态) // 11. 发送CMD16(SET_BLOCKLEN)设置块长度(标准卡) if (card_type != CARD_TYPE_SDHC_SDXC) { // SDHC/SDXC卡块长度固定为512字节 mmc_send_command(16, 512, 0x01); // 设置块长度为512字节 } // 12. 可选:发送ACMD6(SET_BUS_WIDTH)切换到4位总线模式(如果支持) if (support_4bit_bus) { mmc_send_command(55, card_rca << 16, 0x01); mmc_send_command(6, 0x02, 0x01); // 参数0x02代表4位总线 // 同时更新CMD_DAT_CONT寄存器的BUSW字段 } // 13. 可选:提高工作时钟频率 mmc_write_reg(MMC_CLK_RATE, calculate_high_speed_clk_value()); return 0; // 初始化成功 }4.2 单块与多块读写操作
初始化完成后,就可以进行数据读写了。单块读写是基础,多块读写则能提升效率。
单块读(CMD17)流程:
- 确保卡处于传输状态(
CURRENT_STATE为tran)。 - 设置
BLK_LEN寄存器(如果之前没设或需要改变)。 - 配置
CMD_DAT_CONT:FRES=R1,DATEN=1,WRRD=0,STRBLK=0。 - 设置
READ_TO为一个合理的超时值。 - 向
CMD写入17,向ARGUMENT写入块地址。 - 等待
ECR,读取响应并检查状态。 - 等待
DTD,同时从BUFFER_ACCESS或通过DMA读取数据。 - 数据读取完成后,可发送CMD13确认操作成功。
多块读(CMD18)与停止(CMD12)流程: 多块读用于连续读取多个扇区。在发送CMD18并指定起始地址后,卡会连续发送数据块,直到主机发送CMD12(STOP_TRANSMISSION)命令。
// 启动多块读 mmc_write_reg(MMC_NOB, block_count); // 设置要读取的块数(可选,某些卡支持) mmc_send_command(18, start_address, 0x01); // 发送CMD18 // 循环读取数据块 for (int i = 0; i < block_count; i++) { while (!(mmc_read_reg(MMC_STATUS) & STATUS_DTD_MASK)) { // 等待当前块数据就绪(或通过中断/DMA) } // 从BUFFER_ACCESS读取一个块的数据(512字节) read_data_block(buffer + i * 512); // 清除状态,准备下一个块 } // 发送停止命令 mmc_send_command(12, 0, 0x01); // CMD12,期望R1b响应 // 等待卡返回空闲状态单块写(CMD24)与多块写(CMD25)流程: 写操作与读操作类似,但方向相反,且通常在数据传送后卡会进入编程状态(prg),需要主机等待其完成。
- 发送写命令(CMD24或CMD25)。
- 等待
ECR和响应。 - 开始向
BUFFER_ACCESS写入数据,或启动DMA写入。 - 等待
DTD。 - 关键步骤:发送CMD13查询状态,直到
CURRENT_STATE从prg变回tran,且READY_FOR_DATA为1。在此期间,卡可能拉低数据线表示忙。
4.3 擦除与写保护操作
擦除操作(CMD38)需要先使用CMD32-CMD37标记要擦除的扇区或擦除组范围,然后发送CMD38执行擦除。写保护操作则通过CMD28(设置写保护)、CMD29(清除写保护)、CMD30(查询写保护状态)进行。
擦除操作示例:
// 标记擦除组起始地址 mmc_send_command(35, erase_group_start_addr, 0x01); // CMD35 // 标记擦除组结束地址 mmc_send_command(36, erase_group_end_addr, 0x01); // CMD36 // 执行擦除 mmc_send_command(38, 0, 0x01); // CMD38,期望R1b响应(带忙) // 等待擦除完成(卡会进入繁忙状态) do { mmc_send_command(13, card_rca << 16, 0x01); // CMD13查询状态 status = mmc_read_response(); } while (!(status & (1 << 8))); // 等待READY_FOR_DATA位为1避坑指南:擦除操作耗时较长,务必在发送CMD38后持续查询卡状态,直到
READY_FOR_DATA置位。不要依赖固定的延时。同时,检查状态字中的ERASE_SEQ_ERROR或ERASE_PARAM位,确保擦除序列和参数正确。
5. 调试技巧与常见问题排查
开发MMC/SD驱动时,问题排查是家常便饭。以下是我总结的一些实战经验和常见问题的排查思路。
5.1 硬件连接与信号完整性
- 上拉电阻:CMD和DAT线通常需要上拉电阻(通常10kΩ-100kΩ),以确保空闲时为高电平。缺少上拉可能导致通信不稳定。
- 电源与地线:确保为存储卡提供干净、稳定的电源,地线回路良好。突然的电流波动可能导致卡复位或通信错误。
- 时钟信号:用示波器检查MMCSD_CLK的波形。频率是否准确?占空比是否接近50%?是否有过冲或振铃?过差的时钟信号是导致CRC错误和超时的常见原因。
- CMD/DAT信号:在通信时,用逻辑分析仪或示波器捕获CMD和DAT线上的波形。对照MMC/SD协议时序图,检查起始位、停止位、数据位和CRC是否正常。
5.2 软件调试与状态追踪
- 寄存器值检查:在每一步关键操作前后,打印或记录所有相关寄存器的值(
STATUS,CMD_DAT_CONT,CLK_RATE等)。与手册的预期值进行比对。 - 命令响应分析:实现一个函数,将CMD13读取到的32位状态字,按位解析成可读的错误信息和状态信息。例如:
void print_card_status(uint32_t status) { if (status & (1 << 31)) printf("OUT_OF_RANGE "); if (status & (1 << 30)) printf("ADDRESS_ERROR "); if (status & (1 << 29)) printf("BLOCK_LEN_ERROR "); if (status & (1 << 28)) printf("ERASE_SEQ_ERROR "); if (status & (1 << 27)) printf("ERASE_PARAM "); if (status & (1 << 26)) printf("WP_VIOLATION "); if (status & (1 << 25)) printf("CARD_IS_LOCKED "); if (status & (1 << 24)) printf("LOCK_UNLOCK_FAILED "); if (status & (1 << 23)) printf("COM_CRC_ERROR "); if (status & (1 << 22)) printf("ILLEGAL_COMMAND "); // ... 解析其他位 uint8_t state = (status >> 9) & 0x0F; printf("Current State: %d\n", state); } - 超时处理:合理设置
RES_TO和READ_TO寄存器。设置过小会导致不必要的超时错误;设置过大会在卡无响应时导致程序长时间挂起。建议初始化为较大值保证初始化成功,初始化后再根据实际需求调整。
5.3 常见错误代码与解决方案速查表
| 现象/错误位 | 可能原��� | 排查步骤与解决方案 |
|---|---|---|
COM_CRC_ERROR | 1. 物理连接不良(线缆、触点)。 2. 时钟频率过高或信号质量差。 3. 主机CRC计算错误(但MC68SZ328硬件生成CRC,此点可能不适用)。 | 1. 检查硬件连接,确保接触可靠。 2. 降低时钟频率( CLK_RATE),用示波器观察时钟和数据信号。3. 确认命令帧格式正确(48位)。 |
ILLEGAL_COMMAND | 1. 在当前卡状态下发送了不允许的命令。 2. 命令索引错误。 3. 对于SD卡,未先发送CMD55就发送ACMDxx。 | 1. 发送CMD13查询CURRENT_STATE,确认卡状态(如tran态才能读写)。2. 检查 CMD寄存器写入的值是否正确。3. 发送应用命令前,务必先发送CMD55。 |
OUT_OF_RANGE | 访问的地址超出了卡的容量范围。 | 1. 检查输入的地址参数。 2. 通过CSD寄存器正确计算卡容量。 3. 对于SDHC/SDXC卡,地址是块地址(LBA),而不是字节地址。 |
| 卡无响应(超时) | 1. 卡未正确供电或已损坏。 2. 控制器未正确初始化(时钟未开启)。 3. CMD线始终为低,可能被卡拉低(卡处于错误状态)。 | 1. 测量卡供电电压。 2. 检查 STR_STP_CLK和CLK_RATE寄存器配置,用示波器确认有时钟输出。3. 尝试发送CMD0(GO_IDLE_STATE)进行全局复位。 |
| 读写数据错误 | 1.BLK_LEN设置与卡不匹配(非512字节卡)。2. 数据缓冲区访问与控制器FIFO不同步。 3. DMA配置错误(地址、长度、触发源)。 | 1. 确认块长度,标准卡为512字节,通过CSD确认。 2. 在轮询模式下,严格根据 DTD或ABFE/ABFF状态位读写BUFFER_ACCESS。3. 检查DMA通道配置,确认传输大小是16位的倍数,且与 BLK_LEN匹配。 |
LOCK_UNLOCK_FAILED | 1. 对已锁定的卡执行非法访问。 2. 对已解锁的卡执行解锁操作。 3. 强制擦除时,数据块中 ERASE位不是唯一置位的位。 | 1. 先发送CMD13检查CARD_IS_LOCKED位。2. 确保操作序列正确:CMD16设置块长->CMD42发送带密码/锁/解锁/擦除指令的数据块。 3. 强制擦除时,确保伴随CMD42的1字节数据块仅为0x01(仅 ERASE位为1)。 |
5.4 高级功能:中断模式(CMD40)的使用
手册第17.5.5节描述了中断模式(GO_IRQ_STATE)。这是一种低功耗特性,允许卡在需要服务时主动通知主机,而不是主机不断轮询。
使用要点:
- 进入条件:所有卡必须处于待机状态(Stand-by State)。
- 进入命令:主机发送CMD40(GO_IRQ_STATE)。
- 卡行为:卡进入Wait-IRQ-State,等待内部事件(如数据准备好、写完成)。事件发生时,卡会通过开漏模式的CMD线发送响应。
- 主机行为:主机在等待期间必须保持时钟活动。收到中断响应后,主机需通过标准流程(如CMD7选择卡,然后CMD13查询状态)来服务卡。
- 退出中断模式:主机可以自己生成一个CMD40响应(卡位=0,使用RCA地址0x0000)来将所有卡带回待机状态。
个人体会:中断模式在需要低功耗的电池供电设备中很有用,但它增加了状态管理的复杂性。在大多数嵌入式应用中,简单的轮询方式(定期发送CMD13)可能更易于实现和调试。除非你的应用对功耗极其敏感,否则可以先实现轮询,稳定后再考虑优化为中断模式。
深入理解MC68SZ328的MMC/SD控制器,就像掌握了一套与存储卡对话的精密语言。从最底层的寄存器配置,到命令响应的交互协议,再到错误状态的精准排查,每一个环节都需要耐心和细致。这份手册提供了完整的蓝图,而真正的掌握来自于动手实践和问题解决。希望这篇结合了手册精髓与实践经验的解析,能成为你开发路上的得力助手。当你下次再遇到“卡初始化失败”时,你不会再感到茫然,而是会习惯性地掏出逻辑分析仪,查看CMD线上的波形,或是发送一个CMD13,仔细解读那32位状态字所诉说的故事。
