这个问题非常典型,也很有代表性。遇到 FFFC
错误,并且与特定的 sector(1024)相关,这几乎可以肯定是指向了SD卡驱动中地址处理的问题。
FFFC
不是一个数据错误,而是 SD卡返回的响应(Response)中的错误状态位。在SD协议中,R1响应格式的 bit 15-bit 0 是卡的状态错误码。FFFC
换算成二进制是 1111 1111 1111 1100
,其中为0的位(bit 1和bit 0)表示错误。
重点关注这两位:
Bit 1 (ADDRESS_ERROR
): 表示命令中使用的地址(即你要读写的sector号)不符合卡的对齐要求、超出了卡的范围、或者在一个擦除命令中地址不连续。
Bit 0 (OUT_OF_RANGE
): 表示命令参数中使用的地址超出了卡的可寻址范围。
问题根源:地址模式切换(Byte Address vs Block Address)
最关键的一点是:SD卡在默认上电后使用字节地址(Byte Address)模式,但绝大多数SD NAND/卡在初始化后,应该切换到块地址(Block Address)模式(即CMD_SET_BLOCKLEN)来进行读写。
字节地址模式:如果你要读写第 1024
个sector,每个sector是 512
字节,那么你需要计算的地址是 1024 * 512 = 0x80000
。
块地址模式:如果你已经正确地使用 CMD16 (SET_BLOCKLEN)
命令将块长度设置为 512
字节,那么后续的读写命令(如 CMD24 (WRITE_BLOCK)
)就可以直接使用sector号作为地址,即 1024
。
你的驱动代码最可能存在的问题是:
没有正确处理地址模式的切换,或者错误地在读写命令中混合使用了两种模式。
错误情况:你可能在驱动中使用了块地址模式(即直接传递sector号),但却没有发送 CMD16
命令来设置块长度。或者,你发送了 CMD16
,但在后续的 CMD24/CMD17
命令中,错误地仍然将sector号乘以了512,当作字节地址发送。
为什么是1024?
1024(0x400)是一个很“整”的数。0x400 * 512 =
0x200000。当你错误地使用字节地址模式时,前1023个sector的地址(0 到
1023*512)可能还在某种容错范围内,或者卡内部处理没有报错。但一到1024这个边界点,地址 0x200000
可能恰好触发了卡内部地址检查机制的异常,从而导致返回 ADDRESS_ERROR
或 OUT_OF_RANGE
错误。
请重点检查并修改你的SD驱动代码中的以下部分:
在发送完 ACMD41
完成卡初始化之后,必须发送 CMD16
来设置块长度。
// ... 其他初始化代码 (CMD0, CMD8, ACMD41等) ...// 设置块长度为 512 字节,这是标准sector大小if(sd_send_cmd(CMD16, 512, 0) != 0x00) { // 注意这里的参数是 512 printf("Set block length failed! "); return SD_ERROR;}// ... 初始化成功 ...
在写sector(例如 CMD24
)的函数中,直接使用sector号作为地址参数,不要再乘以512。
修改前(可能是错误的):
// 错误的示例:将块地址转换成了字节地址sector_address = sector_number * 512; // <--- 这很可能是问题所在!sd_send_cmd(CMD24, sector_address, 0xFF); // 写入命令
修改后(正确的):
// 正确的示例:直接使用sector号(块地址)// 前提是已经用CMD16设置好了块长度sd_send_cmd(CMD24, sector_number, 0xFF); // 注意这里直接是 sector_number
读sector(CMD17
)的函数也存在同样的问题,请一并修改。
// 读命令也直接使用sector号sd_send_cmd(CMD17, sector_number, 0xFF);
导致在写第1024个sector时报 FFFC
错误的最可能原因是:在读写命令中,错误地将sector号乘以512后作为字节地址发送,而没有正确使用CMD16命令切换到块地址模式。
请按以下步骤修复:
确保在初始化流程中,成功发送了 CMD16 (SET_BLOCKLEN)
并将块长度设置为 512
。
确保在 CMD24/CMD17
等读写命令中,传入的地址参数是 sector号本身,而不是 sector_number * 512
。
完成这两步修改后,你的SD NAND应该就能正确读写所有sector了,包括1024及之后的sector。
建议你使用逻辑分析仪或ST-Link的调试功能,抓取一下初始化过程和发送写命令时的实际数据帧,确认 CMD16
是否发送,以及 CMD24
的地址参数到底是什么,这是定位问题最直接的方法。
上一篇:SDIO 时钟信号上升和下降时间
下一篇:sdnand电路设计