feat(storage): 新增SD卡性能分析文档并优化写入策略

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

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

- 注释掉cnt % 2条件判断,确保中断回调中的处理逻辑能正常执行
- 保持原有的GPIO引脚检测和处理流程不变
This commit is contained in:
zhoujie 2026-02-07 00:02:50 +08:00
parent 9a2d543fcb
commit 8a928032dd
3 changed files with 266 additions and 3 deletions

View File

@ -617,7 +617,7 @@ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
if(LTC2508_IsInited() == 0) return; if(LTC2508_IsInited() == 0) return;
cnt ++; cnt ++;
if(cnt % 2 == 0) // if(cnt % 2 == 0)
{ {
// HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); // HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
if (GPIO_Pin == ADC_DRY_Pin) { if (GPIO_Pin == ADC_DRY_Pin) {

View File

@ -0,0 +1,251 @@
# 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卡或降低数据采样率。

View File

@ -64,6 +64,9 @@ HAL_StatusTypeDef DataStorage_StopRecording(DataStorageHandle_t *handle)
} }
} }
// 强制同步所有数据到SD卡
f_sync(&handle->file);
// 关闭文件 // 关闭文件
f_close(&handle->file); f_close(&handle->file);
@ -278,8 +281,17 @@ HAL_StatusTypeDef DataStorage_FlushBuffer(DataStorageHandle_t *handle, uint8_t b
return HAL_ERROR; return HAL_ERROR;
} }
// 同步到存储设备 // 优化:减少同步频率以提高性能
f_sync(&handle->file); // 使用静态变量记录刷新次数
static uint32_t flush_count = 0;
flush_count++;
// 每10次刷新才同步一次或者文件即将达到最大大小时同步
// 这样可以显著减少SD卡写入延迟提高吞吐量
if (flush_count % 10 == 0 ||
handle->stats.current_file_size + bytes_written >= DATA_STORAGE_FILE_MAX_SIZE) {
f_sync(&handle->file);
}
// 更新统计信息 // 更新统计信息
handle->stats.current_file_size += bytes_written; handle->stats.current_file_size += bytes_written;