- 新增SD卡性能分析文档,详细分析写入瓶颈并提供优化建议
- 优化DataStorage_FlushBuffer函数,减少f_sync调用频率以提高写入性能
- 在停止录制时强制同步所有数据到SD卡,确保数据完整性
- 使用静态变量记录刷新次数,每10次刷新或文件即将达到最大大小时才同步
🐛 fix(adc): 修复中断回调中的条件判断逻辑
- 注释掉cnt % 2条件判断,确保中断回调中的处理逻辑能正常执行
- 保持原有的GPIO引脚检测和处理流程不变
6.4 KiB
6.4 KiB
SD卡写入性能问题分析与优化建议
问题现象
根据监控数据显示:
- SD Write Errors: 3 - 出现了3次写入错误
- SD Buffer Full: 1489 - 缓冲区满发生了1489次
这表明SD卡写入速度严重跟不上数据产生速度,导致双缓冲区频繁处于忙碌状态。
问题分析
1. 数据产生速度
根据代码分析:
- ADC采样率:4 KHz(每秒4000个样本)
- 每个样本包含3个通道的数据
- 数据包大小:
- 原始数据包
DataPacket_t:约20-30字节 - 校正数据包
CorrectedDataPacket_t:约30-40字节
- 原始数据包
- 估算数据速率:4000 × 30 = 120 KB/s
2. SD卡写入速度
当前配置:
- 缓冲区大小:32768字节(32 KB)
- 缓冲区满1489次意味着频繁切换
- 如果缓冲区满次数高,说明后台刷新速度慢
3. 瓶颈分析
可能的原因:
-
SD卡性能不足
- 使用了低速SD卡(Class 4或更低)
- SD卡碎片化严重
- SD卡老化或质量问题
-
写入策略问题
- 使用同步写入(
f_write+f_sync)阻塞时间长 - 每次写入后立即调用
f_sync()强制同步
- 使用同步写入(
-
缓冲区配置不当
- 32KB缓冲区可能不够大
- 双缓冲机制在高速写入时仍然不够
-
SDIO时钟配置
- 当前
ClockDiv = 1,可能未达到最高速度
- 当前
优化建议
优先级1:立即优化(软件层面)
1.1 减少 f_sync() 调用频率
当前代码(data_storage.c:278):
// 同步到存储设备
f_sync(&handle->file);
问题:每次写入都调用 f_sync() 会严重降低性能。
优化方案:
// 只在特定条件下同步
static uint32_t write_count = 0;
write_count++;
// 每10次写入或文件即将关闭时才同步
if (write_count % 10 == 0 || buffer->index >= DATA_STORAGE_BUFFER_SIZE) {
f_sync(&handle->file);
}
1.2 增大缓冲区大小
当前配置(data_storage.h:12):
#define DATA_STORAGE_BUFFER_SIZE 32768 // 32KB
优化方案:
#define DATA_STORAGE_BUFFER_SIZE 65536 // 64KB
或者更激进:
#define DATA_STORAGE_BUFFER_SIZE 131072 // 128KB
注意:需要确保MCU有足够的RAM(STM32F405有128KB SRAM)。
1.3 优化SDIO时钟分频
当前配置(sdio.c:49):
hsd.Init.ClockDiv = 1;
这会产生:168MHz / (1+1) = 84 MHz 的SDIO时钟(已经很高)。
建议:保持当前配置,或尝试 ClockDiv = 0(如果SD卡支持)。
1.4 使用DMA写入(如果未启用)
检查是否使用了DMA传输。当前代码中已配置DMA,但需要确认是否实际使用。
优先级2:中期优化
2.1 实现三缓冲或更多缓冲
将双缓冲扩展为三缓冲或四缓冲,提供更多的写入时间窗口。
2.2 降低数据采样率
如果应用允许,考虑:
- 降低采样率(从4KHz降到2KHz或1KHz)
- 或者实现数据压缩/抽取
2.3 优化数据包结构
减小数据包大小,例如:
- 使用更紧凑的数据格式
- 移除不必要的字段
- 使用数据压缩
优先级3:硬件优化
3.1 更换高速SD卡
推荐使用:
- Class 10 或更高
- UHS-I (Ultra High Speed) 卡
- A1/A2 等级(针对随机写入优化)
3.2 SD卡格式化
使用合适的分配单元大小格式化SD卡:
- 推荐使用 32KB 或 64KB 分配单元
- 与缓冲区大小对齐可提高性能
推荐的优化步骤
第一步:修改 f_sync() 策略
修改 data_storage.c 中的 DataStorage_FlushBuffer() 函数:
HAL_StatusTypeDef DataStorage_FlushBuffer(DataStorageHandle_t *handle, uint8_t buffer_index)
{
// ... 前面的代码保持不变 ...
UINT bytes_written;
FRESULT res = f_write(&handle->file, buffer->data, buffer->index, &bytes_written);
if (res != FR_OK || bytes_written != buffer->index) {
handle->stats.error_count++;
buffer->state = BUFFER_READY_TO_FLUSH;
SystemMonitor_ReportSDWriteError();
return HAL_ERROR;
}
// 优化:减少同步频率
static uint32_t flush_count = 0;
flush_count++;
// 每5次刷新才同步一次,或者文件即将达到最大大小时同步
if (flush_count % 5 == 0 ||
handle->stats.current_file_size + bytes_written >= DATA_STORAGE_FILE_MAX_SIZE) {
f_sync(&handle->file);
}
// ... 后面的代码保持不变 ...
}
第二步:增大缓冲区
修改 data_storage.h:
// 从32KB增加到64KB
#define DATA_STORAGE_BUFFER_SIZE 65536
第三步:监控改进效果
运行系统并观察:
sd_buffer_full_count是否显著减少sd_write_error_count是否降为0- 平均写入大小是否增加
性能计算
理论最大写入速度
SDIO 4线模式,84MHz时钟:
- 理论带宽:84 MHz × 4 bits / 8 = 42 MB/s
- 实际速度(考虑协议开销):约 20-25 MB/s
当前需求
- 数据速率:120 KB/s
- 理论上SD卡速度足够(20 MB/s >> 120 KB/s)
问题根源
瓶颈不在SDIO硬件速度,而在:
- 频繁的 f_sync() 调用(每次写入都同步)
- 文件系统开销(FAT32的元数据更新)
- SD卡随机写入性能(可能碎片化)
预期改进效果
实施上述优化后:
sd_buffer_full_count应降低 80-90%sd_write_error_count应降为 0- 系统稳定性显著提升
调试建议
1. 添加性能计时
在 data_storage.c 中添加:
uint32_t start_tick = HAL_GetTick();
FRESULT res = f_write(&handle->file, buffer->data, buffer->index, &bytes_written);
uint32_t write_time = HAL_GetTick() - start_tick;
// 如果写入时间过长,记录警告
if (write_time > 100) { // 超过100ms
// 记录或输出警告
}
2. 监控写入速度
计算实际写入速度:
// 在统计信息中添加
float write_speed_kbps = (float)sys_stats.sd_total_bytes_written /
(HAL_GetTick() / 1000.0f) / 1024.0f;
printf("Write Speed: %.2f KB/s\n", write_speed_kbps);
总结
当前问题的主要原因是频繁的 f_sync() 调用导致写入性能下降。通过减少同步频率和增大缓冲区,可以显著改善性能。如果问题仍然存在,考虑更换高速SD卡或降低数据采样率。