您提到的 data0 keep low的时间
指的是在 SPI 通信模式下,SD NAND 的 DATA0 (MISO) 线被拉低的时间。这通常表示 SD NAND 正处于繁忙状态,内部正在执行编程(写入)或擦除操作,此时主机必须等待它准备好才能发送下一个命令。
问题根源: 您丢失了45秒的数据,根本原因是在这45秒内,连续多次的写入命令都遇到了SD NAND的内部繁忙期,并且您的程序没有正确地等待它变得不繁忙,导致写入失败或数据丢失。
是的,绝大多数情况下必须。
SD协议规范: SD卡(包括SD NAND)的物理读写操作必须以块(Block)为单位进行。标准的块大小就是 512字节。这是硬件的限制。
底层驱动限制: 您使用的底层SD/SPI驱动函数(如 sd_write()
或 SPI_WriteBlock()
)几乎肯定要求您传入恰好一个块(512字节)或多个块(512字节的倍数)的数据。
您的操作: 您“2s写一次数据,看每个512B写完”,这个思路是完全正确的。您已经是在按块写入。
所以,问题不出在数据包大小上,而出在写入流程和状态处理上。
这是一个标准的写入流程,您的流程可能缺少了第4步或第5步:
主机发送写入命令(CMD24/25): 告诉SD NAND“我要开始写数据了”。
主机发送数据令牌(Start Block): 紧接着命令,发送一个起始令牌。
主机发送512字节数据 + 2字节CRC: 将您要写的数据发送过去。
SD NAND回应数据响应令牌: SD NAND会返回一个字节的响应,表明它是否正确地接收到了数据。(很多初学者会忽略检查这个响应!)
SD NAND进入编程状态(Data0保持为低): 此时,SD NAND开始 internally 将接收到的数据编程(写入)到闪存单元中。这个过程中,DATA0
线会一直被拉低。
主机等待繁忙结束: 主机必须不断地发送时钟脉冲并读取DATA0
线的状态,直到DATA0
变高,表示编程完成。
主机发送下一个命令: 只有等到DATA0
变高后,才能发送下一包数据或下一个命令。
您的“丢数”就发生在这里:
如果您在DATA0
还为低(繁忙)时,就强行发送下一包512字节的数据,SD NAND是无法接收的。这会导致:
本次写入直接失败。
或者破坏SD NAND内部的状态机,导致文件系统损坏,后续写入全部失败。
45秒丢了22次(2秒一次)数据,表明在这45秒内,每次写入操作都失败了。
您的ARM程序必须在每次写入后,增加等待繁忙结束的步骤。
以下是基于SPI模式的标准代码逻辑流程(伪代码):
// 假设有底层函数:发送命令、接收响应、交换字节等
#define SD_WRITE_BLOCK_CMD 24 // 写单块命令
#define SD_START_BLOCK_TOKEN 0xFE // 数据起始令牌
#define SD_BUSY_TIMEOUT 5000 // 等待超时时间(毫秒),可根据需要调整
/**
* @brief 向SD NAND写入一个块(512字节)
* @param pBuffer: 数据缓冲区指针
* @param WriteAddr: 写入地址
* @retval 成功: 0, 失败: 1
*/
uint8_t SD_WriteBlock(uint8_t* pBuffer, uint32_t WriteAddr) {
uint8_t response;
uint32_t timeout;
// 1. 发送写命令 CMD24
response = SD_SendCommand(SD_WRITE_BLOCK_CMD, WriteAddr, 0xFF);
if (response != 0x00) { // 期望返回0x00表示命令被接受
return 1; // 命令发送失败
}
// 2. 发送起始令牌 0xFE
SPI_ReadWriteByte(SD_START_BLOCK_TOKEN);
// 3. 发送512字节数据
for (uint16_t i = 0; i < 512; i++) {
SPI_ReadWriteByte(pBuffer[i]);
}
// 4. 发送2字节伪CRC(通常可以忽略,但协议要求发送)
SPI_ReadWriteByte(0xFF);
SPI_ReadWriteByte(0xFF);
// 5. 获取数据响应令牌(非常重要!)
response = SPI_ReadWriteByte(0xFF);
// 响应字节的高5位应该是 00101
if ((response & 0x1F) != 0x05) {
return 1; // SD卡未正确接收数据
}
// 6. 【关键步骤】等待SD卡编程完成,DATA0变高
timeout = 0;
while (SPI_ReadWriteByte(0xFF) != 0xFF) { // 不断读,直到返回0xFF
timeout++;
if (timeout > SD_BUSY_TIMEOUT) {
return 1; // 等待超时,写入失败
}
// 这里可以加一个短延时,比如delay_ms(1),避免循环太快
Your_Delay_Function(1);
}
return 0; // 写入成功
}
在您的应用层代码中:
void main_loop(void) {
uint8_t data_buffer[512];
while (1) {
// 1. 采集或准备2秒的数据,填充到data_buffer中
Prepare_Data(data_buffer);
// 2. 调用写块函数
if (SD_WriteBlock(data_buffer, target_address) != 0) {
// 处理错误:打印日志、亮错误灯、尝试重新初始化SD卡等
printf("Write Error at address: %lu
", target_address);
} else {
// 写入成功,更新下一个写入地址
target_address += 512;
}
// 3. 等待2秒
Your_Delay_Function(2000);
}
}
检查底层驱动: 确保您使用的SD卡驱动库包含了等待编程完成的步骤。很多简化版的驱动会遗漏这一点。
超时处理: 等待繁忙一定要有超时机制,否则SD卡一旦彻底卡死,您的程序也会永远卡住。
错误处理: 每次写入后都要检查错误返回值,不要假设每次写入都会成功。对于重要数据,失败后应有重试机制。
文件系统考虑: 如果您使用了FATFS等文件系统,f_write()
之后需要调用f_sync()
才能确保数据真正被写入物理介质。否则数据可能还在缓存里。
电源问题: 确保您的SD NAND供电稳定。写入瞬间功耗较大,电压跌落可能导致写入失败甚至损坏卡。
首先,请重点检查并修改您的底层 SD_WriteBlock
函数,加入等待 DATA0
变高的逻辑。 这很可能就是解决您问题的关键。
上一篇:rk3568 驱动sdnand
下一篇:没有了!