✨ feat(storage): 新增SD卡性能分析文档并优化写入策略
- 新增SD卡性能分析文档,详细分析写入瓶颈并提供优化建议
- 优化DataStorage_FlushBuffer函数,减少f_sync调用频率以提高写入性能
- 在停止录制时强制同步所有数据到SD卡,确保数据完整性
- 使用静态变量记录刷新次数,每10次刷新或文件即将达到最大大小时才同步
🐛 fix(adc): 修复中断回调中的条件判断逻辑
- 注释掉cnt % 2条件判断,确保中断回调中的处理逻辑能正常执行
- 保持原有的GPIO引脚检测和处理流程不变
This commit is contained in:
parent
9a2d543fcb
commit
8a928032dd
@ -617,7 +617,7 @@ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
|
||||
if(LTC2508_IsInited() == 0) return;
|
||||
|
||||
cnt ++;
|
||||
if(cnt % 2 == 0)
|
||||
// if(cnt % 2 == 0)
|
||||
{
|
||||
// HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
|
||||
if (GPIO_Pin == ADC_DRY_Pin) {
|
||||
|
||||
251
User/SD_Performance_Analysis.md
Normal file
251
User/SD_Performance_Analysis.md
Normal 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有足够的RAM(STM32F405有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卡或降低数据采样率。
|
||||
@ -64,6 +64,9 @@ HAL_StatusTypeDef DataStorage_StopRecording(DataStorageHandle_t *handle)
|
||||
}
|
||||
}
|
||||
|
||||
// 强制同步所有数据到SD卡
|
||||
f_sync(&handle->file);
|
||||
|
||||
// 关闭文件
|
||||
f_close(&handle->file);
|
||||
|
||||
@ -278,8 +281,17 @@ HAL_StatusTypeDef DataStorage_FlushBuffer(DataStorageHandle_t *handle, uint8_t b
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
// 同步到存储设备
|
||||
// 优化:减少同步频率以提高性能
|
||||
// 使用静态变量记录刷新次数
|
||||
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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user