STM_ATEM/User/SD_Performance_Analysis.md
zhoujie 8a928032dd feat(storage): 新增SD卡性能分析文档并优化写入策略
- 新增SD卡性能分析文档,详细分析写入瓶颈并提供优化建议
- 优化DataStorage_FlushBuffer函数,减少f_sync调用频率以提高写入性能
- 在停止录制时强制同步所有数据到SD卡,确保数据完整性
- 使用静态变量记录刷新次数,每10次刷新或文件即将达到最大大小时才同步

🐛 fix(adc): 修复中断回调中的条件判断逻辑

- 注释掉cnt % 2条件判断,确保中断回调中的处理逻辑能正常执行
- 保持原有的GPIO引脚检测和处理流程不变
2026-02-07 00:02:50 +08:00

252 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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. 瓶颈分析
#### 可能的原因:
1. **SD卡性能不足**
- 使用了低速SD卡Class 4或更低
- SD卡碎片化严重
- SD卡老化或质量问题
2. **写入策略问题**
- 使用同步写入(`f_write` + `f_sync`)阻塞时间长
- 每次写入后立即调用 `f_sync()` 强制同步
3. **缓冲区配置不当**
- 32KB缓冲区可能不够大
- 双缓冲机制在高速写入时仍然不够
4. **SDIO时钟配置**
- 当前 `ClockDiv = 1`,可能未达到最高速度
## 优化建议
### 优先级1立即优化软件层面
#### 1.1 减少 f_sync() 调用频率
**当前代码**[`data_storage.c:278`](User/data_storage.c:278)
```c
// 同步到存储设备
f_sync(&handle->file);
```
**问题**:每次写入都调用 `f_sync()` 会严重降低性能。
**优化方案**
```c
// 只在特定条件下同步
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`](User/data_storage.h:12)
```c
#define DATA_STORAGE_BUFFER_SIZE 32768 // 32KB
```
**优化方案**
```c
#define DATA_STORAGE_BUFFER_SIZE 65536 // 64KB
```
或者更激进:
```c
#define DATA_STORAGE_BUFFER_SIZE 131072 // 128KB
```
**注意**需要确保MCU有足够的RAMSTM32F405有128KB SRAM
#### 1.3 优化SDIO时钟分频
**当前配置**[`sdio.c:49`](Core/Src/sdio.c:49)
```c
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`](User/data_storage.c:278) 中的 `DataStorage_FlushBuffer()` 函数:
```c
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`](User/data_storage.h:12)
```c
// 从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硬件速度而在
1. **频繁的 f_sync() 调用**(每次写入都同步)
2. **文件系统开销**FAT32的元数据更新
3. **SD卡随机写入性能**(可能碎片化)
## 预期改进效果
实施上述优化后:
- `sd_buffer_full_count` 应降低 **80-90%**
- `sd_write_error_count` 应降为 **0**
- 系统稳定性显著提升
## 调试建议
### 1. 添加性能计时
在 [`data_storage.c`](User/data_storage.c:252) 中添加:
```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. 监控写入速度
计算实际写入速度:
```c
// 在统计信息中添加
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卡或降低数据采样率。