FatFS CSV写入Bug排查:如何避免向CSV文件写入空行?
解决FatFS CSV写入空行问题
我来帮你排查这个随机出现空行的问题,先从你的代码里找出几个关键问题,再给出修复方案:
空行的核心原因
你遇到的空行大概率是写入了栈上的垃圾数据导致的:
- 你调用
f_write时用了sizeof(buffer)作为写入长度,但snprintf生成的CSV行内容长度远小于50字节,剩下的buffer空间是栈里未初始化的随机数据,这些数据里可能包含\r\n,就会被当成空行写入文件。 - 另外,你的
strcmp(buffer, "") != 0判断完全没用——snprintf至少会输出\r\n,buffer永远不可能是空字符串。
还有一个潜在逻辑问题:你只有在T1-T4全不为0时才写入,但如果某个循环里这些值有0,你跳过了写入,但这不会产生空行;核心问题还是写入垃圾数据导致的。
分步修复方案
1. 只写入有效字符串长度
别再用sizeof(buffer),而是用snprintf的返回值来确定实际要写入的字节数,避免把垃圾数据写到文件里:
// 生成CSV行时获取有效长度 int line_len = snprintf(buffer, sizeof(buffer), "%s,%s,%s,%s,%s,%s\r\n",stUT,stT1,stT2,stT3,stT4,stPd); // 确保生成的字符串有效且未溢出buffer if (line_len > 0 && line_len < sizeof(buffer)) { UINT bytes_written; ff_result = f_write(&file, buffer, line_len, &bytes_written); }
2. 提前过滤无效数据
把数据有效性判断移到生成buffer之前,避免做无用的字符串拼接:
for(int i = 0; i<MAXIMUM_BUFFER_STORAGE;i ++) { // 先判断数据是否有效,无效直接跳过当前循环 if((SD_CARD_T1[i] == 0) || (SD_CARD_T2[i] == 0) || (SD_CARD_T3[i] == 0) || (SD_CARD_T4[i] == 0)) { ID++; continue; } // 下面再做字符串拼接和写入操作 char buffer[50]; // ... 你的sprintf代码 ... }
3. 修复写入字节数统计
你现在用单次写入的bytes_written乘以循环次数来统计总字节数,这完全错误——每次f_write会覆盖bytes_written的值。应该定义一个累加变量:
// 循环前初始化累加变量 UINT total_bytes = 0; // 写入成功后累加 total_bytes += bytes_written; // 最后日志输出总字节数 NRF_LOG_INFO("Write new entry %d bytes written.", total_bytes);
4. 优化浮点数输出精度(可选)
你用%f输出浮点数,默认精度会生成较长的字符串,容易导致buffer溢出,建议指定精度,比如保留两位小数:
sprintf(stPd, "%.2f", SD_CARD_Pdiff[i]);
5. 简化错误处理逻辑
你代码里f_open的错误判断重复了,简化成:
ff_result = f_open(&file, FILE_NAME, FA_READ | FA_WRITE | FA_OPEN_APPEND); if(ff_result != FR_OK) { NRF_LOG_INFO("Unable to open or create file: " FILE_NAME "."); SD_CARD_PRESENT = 0; return; }
修复后的完整代码
void Write_block_to_sd_card() { while (fno.fname[0]); FRESULT ff_result; FIL file; UINT total_bytes_written = 0; ff_result = f_open(&file, FILE_NAME, FA_READ | FA_WRITE | FA_OPEN_APPEND); if(ff_result != FR_OK) { NRF_LOG_INFO("Unable to open or create file: " FILE_NAME "."); SD_CARD_PRESENT = 0; return; } NRF_LOG_RAW_INFO(""); NRF_LOG_INFO("Writing to file as a block " FILE_NAME "..."); SD_CARD_PRESENT = 1; for(int i = 0; i < MAXIMUM_BUFFER_STORAGE; i++) { // 提前过滤无效数据 if((SD_CARD_T1[i] == 0) || (SD_CARD_T2[i] == 0) || (SD_CARD_T3[i] == 0) || (SD_CARD_T4[i] == 0)) { ID++; continue; } char buffer[50]; char stT1 [MAX_BUFFER_SIZE]; char stT2 [MAX_BUFFER_SIZE]; char stT3 [MAX_BUFFER_SIZE]; char stT4 [MAX_BUFFER_SIZE]; char stPd [MAX_BUFFER_SIZE]; char stUT [MAX_BUFFER_SIZE]; sprintf(stT1, "%d", SD_CARD_T1[i]); sprintf(stT2, "%d", SD_CARD_T2[i]); sprintf(stT3, "%d", SD_CARD_T3[i]); sprintf(stT4, "%d", SD_CARD_T4[i]); sprintf(stPd, "%.2f", SD_CARD_Pdiff[i]); sprintf(stUT, "%d", SD_CARD_UNIX_TIME[i]); int line_len = snprintf(buffer, sizeof(buffer), "%s,%s,%s,%s,%s,%s\r\n",stUT,stT1,stT2,stT3,stT4,stPd); if (line_len > 0 && line_len < sizeof(buffer)) { UINT bytes_written; ff_result = f_write(&file, buffer, line_len, &bytes_written); if (ff_result != FR_OK) { NRF_LOG_INFO("Write failed for entry %d\r\n.", i); } else { total_bytes_written += bytes_written; } } else { NRF_LOG_INFO("Buffer overflow or invalid line for entry %d\r\n.", i); } ID = ID + 1; } NRF_LOG_INFO("Write new entry %d bytes written.", total_bytes_written); (void) f_close(&file); Time_stamp_id = ID; return; }
为什么之前会出现间歇性空行?
当栈上的buffer未被完全覆盖时,残留的垃圾数据里恰好包含了\r\n,就会被写入成空行;而后续循环中如果生成的CSV行内容较长,把buffer的剩余空间也覆盖了,垃圾数据被清空,空行就不再出现——这就是你看到"运行一段时间后恢复正常"的原因。
内容的提问来源于stack exchange,提问作者Thomas Morris
相关产品推荐
相关产品推荐

