feat(config): 新增运行时配置管理功能

- 新增配置管理器模块,支持从SD卡加载运行时配置
- 将数据输出模式从编译时宏改为运行时配置,提高灵活性
- 新增配置文件 `CONFIG.TXT`,支持串口输出和存储功能的动态开关
- 集成配置管理器到主程序,在系统启动时自动加载配置
- 更新数据存储模块,使用配置管理器管理会话序号

🐛 fix(usart): 修复USART3中断配置并优化波特率

- 添加USART3中断处理函数声明和实现
- 将USART3波特率从921600提升至2000000以提高通信速率
- 调整USART1中断优先级从2改为11,优化中断响应
- 在USART3 MSP初始化和反初始化中添加中断配置

📝 docs(user): 新增功能说明文档

- 新增《配置管理功能说明》文档,详细说明运行时配置的使用方法
- 新增《串口发送监控功能说明》文档,说明新增的串口统计功能
- 文档包含配置项说明、文件格式、使用示例和注意事项

 feat(monitor): 增强系统监控功能

- 在系统监控统计中添加串口发送监控字段(发送次数、字节数、错误数)
- 新增串口发送统计报告函数,集成到RS485驱动中
- 优化监控状态保存格式,采用精简格式减少文件写入阻塞时间
- 监控数据现在包含串口统计信息,便于性能分析和故障排查

♻️ refactor(main): 重构主程序配置处理逻辑

- 移除编译时宏 `DATA_OUTPUT_MODE_UART` 和 `DATA_OUTPUT_MODE_STORAGE`
- 使用 `Config_IsUartOutputEnabled()` 和 `Config_IsStorageEnabled()` 函数替代
- 优化数据存储启动逻辑,仅在存储功能启用时开始记录
- 添加配置加载成功/失败的调试输出信息
This commit is contained in:
zhoujie 2026-02-07 14:28:14 +08:00
parent 28b4dc6af1
commit 3c0acaa176
13 changed files with 713 additions and 43 deletions

View File

@ -60,6 +60,7 @@ void DMA1_Stream0_IRQHandler(void);
void DMA1_Stream3_IRQHandler(void); void DMA1_Stream3_IRQHandler(void);
void TIM2_IRQHandler(void); void TIM2_IRQHandler(void);
void USART1_IRQHandler(void); void USART1_IRQHandler(void);
void USART3_IRQHandler(void);
void SDIO_IRQHandler(void); void SDIO_IRQHandler(void);
void DMA2_Stream0_IRQHandler(void); void DMA2_Stream0_IRQHandler(void);
void DMA2_Stream3_IRQHandler(void); void DMA2_Stream3_IRQHandler(void);

View File

@ -35,6 +35,7 @@
#include "correction.h" #include "correction.h"
#include "data_storage.h" #include "data_storage.h"
#include "system_monitor.h" #include "system_monitor.h"
#include "config_manager.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
/* USER CODE END Includes */ /* USER CODE END Includes */
@ -51,10 +52,10 @@
#define DEBUG_OUTPUT_INTERVAL_MS 1000 // 调试输出间隔(毫秒) #define DEBUG_OUTPUT_INTERVAL_MS 1000 // 调试输出间隔(毫秒)
#define MONITOR_SAVE_INTERVAL_MS 10000 // 监控状态保存间隔(毫秒) - 10秒 #define MONITOR_SAVE_INTERVAL_MS 10000 // 监控状态保存间隔(毫秒) - 10秒
// 数据输出模式选择(可以同时启用 // 数据输出模式选择(运行时配置从SD卡加载
#define DATA_OUTPUT_MODE_UART 1 // 0=禁用, 1=启用串口输出数据 // 注意DATA_OUTPUT_MODE_UART 和 DATA_OUTPUT_MODE_STORAGE 已改为运行时配置
#define DATA_OUTPUT_MODE_STORAGE 0 // 0=禁用, 1=启用存储到卡 // 使用 Config_IsUartOutputEnabled() 和 Config_IsStorageEnabled() 来检查状态
// 注意:两个模式可以同时启用,但会增加系统负载 // 配置文件0:/CONFIG.TXT
/* USER CODE END PD */ /* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/
@ -186,25 +187,24 @@ static void ProcessAdcData(void)
correction_applied = 1; correction_applied = 1;
#if DATA_OUTPUT_MODE_UART // 发送校正后的数据包到串口(运行时配置)
// 发送校正后的数据包到串口 if (Config_IsUartOutputEnabled()) {
RS485_SendData((uint8_t*)&g_corrected_packet, sizeof(CorrectedDataPacket_t)); RS485_SendData((uint8_t*)&g_corrected_packet, sizeof(CorrectedDataPacket_t));
#endif }
} else { } else {
// 4b. 校正失败或未启用,使用原始数据 // 4b. 校正失败或未启用,使用原始数据
PackData(&g_data_packet, raw_adc[0], raw_adc[1], raw_adc[2]); PackData(&g_data_packet, raw_adc[0], raw_adc[1], raw_adc[2]);
#if DATA_OUTPUT_MODE_UART // 发送原始数据包到串口(运行时配置)
// 发送原始数据包到串口 if (Config_IsUartOutputEnabled()) {
RS485_SendData((uint8_t*)&g_data_packet, sizeof(DataPacket_t)); RS485_SendData((uint8_t*)&g_data_packet, sizeof(DataPacket_t));
#endif }
} }
// 6. 存储数据到SD卡 (如果启用记录且缓冲区可用) // 6. 存储数据到SD卡 (如果启用记录且缓冲区可用,运行时配置)
#if DATA_OUTPUT_MODE_STORAGE if (Config_IsStorageEnabled() && g_recording_enabled) {
if (g_recording_enabled) {
if (can_store_data) { if (can_store_data) {
if (correction_applied) { if (correction_applied) {
// 存储校正后的数据 // 存储校正后的数据
@ -220,7 +220,6 @@ static void ProcessAdcData(void)
#endif #endif
} }
} }
#endif
// 7. 释放已处理的缓冲区 // 7. 释放已处理的缓冲区
LTC2508_ReleaseBuffer(LTC2508_GetCurrentReadBuffer()); LTC2508_ReleaseBuffer(LTC2508_GetCurrentReadBuffer());
@ -500,6 +499,9 @@ int main(void)
// 初始化调试输出 // 初始化调试输出
DebugOutput_Init(); DebugOutput_Init();
// 初始化配置管理器(设置默认值)
Config_Init();
// 初始化RS485通信 // 初始化RS485通信
RS485_Init(&huart1, RS485_DE_RE_PORT, RS485_DE_RE_PIN); RS485_Init(&huart1, RS485_DE_RE_PORT, RS485_DE_RE_PIN);
@ -513,10 +515,19 @@ int main(void)
if (!g_usb_connected) { if (!g_usb_connected) {
// USB未连接挂载文件系统用于数据采集 // USB未连接挂载文件系统用于数据采集
if (MountFileSystemForSampling() == HAL_OK) { if (MountFileSystemForSampling() == HAL_OK) {
// 从SD卡加载配置
if (Config_Load() == HAL_OK) {
DebugOutput_SendString("Config loaded from SD card\r\n");
} else {
DebugOutput_SendString("Using default config\r\n");
}
// 初始化数据存储 // 初始化数据存储
if (DataStorage_Init(&g_data_storage) == HAL_OK) { if (DataStorage_Init(&g_data_storage) == HAL_OK) {
// 开始数据记录 // 开始数据记录(如果存储功能已启用)
StartRecording(); if (Config_IsStorageEnabled()) {
StartRecording();
}
} }
} }
} else { } else {

View File

@ -68,6 +68,7 @@ extern DMA_HandleTypeDef hdma_spi3_rx;
extern TIM_HandleTypeDef htim2; extern TIM_HandleTypeDef htim2;
extern DMA_HandleTypeDef hdma_usart1_tx; extern DMA_HandleTypeDef hdma_usart1_tx;
extern UART_HandleTypeDef huart1; extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart3;
/* USER CODE BEGIN EV */ /* USER CODE BEGIN EV */
extern SPI_HandleTypeDef hspi1; extern SPI_HandleTypeDef hspi1;
extern SPI_HandleTypeDef hspi2; extern SPI_HandleTypeDef hspi2;
@ -283,6 +284,20 @@ void USART1_IRQHandler(void)
/* USER CODE END USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */
} }
/**
* @brief This function handles USART3 global interrupt.
*/
void USART3_IRQHandler(void)
{
/* USER CODE BEGIN USART3_IRQn 0 */
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3);
/* USER CODE BEGIN USART3_IRQn 1 */
/* USER CODE END USART3_IRQn 1 */
}
/** /**
* @brief This function handles SDIO global interrupt. * @brief This function handles SDIO global interrupt.
*/ */

View File

@ -70,7 +70,7 @@ void MX_USART3_UART_Init(void)
/* USER CODE END USART3_Init 1 */ /* USER CODE END USART3_Init 1 */
huart3.Instance = USART3; huart3.Instance = USART3;
huart3.Init.BaudRate = 921600; huart3.Init.BaudRate = 2000000;
huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1; huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Parity = UART_PARITY_NONE;
@ -131,7 +131,7 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
__HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx); __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);
/* USART1 interrupt Init */ /* USART1 interrupt Init */
HAL_NVIC_SetPriority(USART1_IRQn, 2, 0); HAL_NVIC_SetPriority(USART1_IRQn, 11, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn); HAL_NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspInit 1 */ /* USER CODE BEGIN USART1_MspInit 1 */
@ -157,6 +157,9 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
GPIO_InitStruct.Alternate = GPIO_AF7_USART3; GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USART3 interrupt Init */
HAL_NVIC_SetPriority(USART3_IRQn, 15, 0);
HAL_NVIC_EnableIRQ(USART3_IRQn);
/* USER CODE BEGIN USART3_MspInit 1 */ /* USER CODE BEGIN USART3_MspInit 1 */
/* USER CODE END USART3_MspInit 1 */ /* USER CODE END USART3_MspInit 1 */
@ -203,6 +206,8 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
*/ */
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11); HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);
/* USART3 interrupt Deinit */
HAL_NVIC_DisableIRQ(USART3_IRQn);
/* USER CODE BEGIN USART3_MspDeInit 1 */ /* USER CODE BEGIN USART3_MspDeInit 1 */
/* USER CODE END USART3_MspDeInit 1 */ /* USER CODE END USART3_MspDeInit 1 */

View File

@ -183,7 +183,8 @@ NVIC.SDIO_IRQn=true\:9\:0\:true\:false\:true\:true\:true\:true
NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.SysTick_IRQn=true\:0\:0\:true\:false\:true\:false\:true\:false NVIC.SysTick_IRQn=true\:0\:0\:true\:false\:true\:false\:true\:false
NVIC.TIM2_IRQn=true\:3\:0\:true\:false\:true\:true\:true\:true NVIC.TIM2_IRQn=true\:3\:0\:true\:false\:true\:true\:true\:true
NVIC.USART1_IRQn=true\:2\:0\:true\:false\:true\:true\:true\:true NVIC.USART1_IRQn=true\:11\:0\:true\:false\:true\:true\:true\:true
NVIC.USART3_IRQn=true\:15\:0\:true\:false\:true\:true\:true\:true
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
PA1.GPIOParameters=GPIO_Label PA1.GPIOParameters=GPIO_Label
PA1.GPIO_Label=ADC_DRY PA1.GPIO_Label=ADC_DRY
@ -359,7 +360,7 @@ TIM2.Prescaler=83
USART1.BaudRate=2000000 USART1.BaudRate=2000000
USART1.IPParameters=VirtualMode,BaudRate USART1.IPParameters=VirtualMode,BaudRate
USART1.VirtualMode=VM_ASYNC USART1.VirtualMode=VM_ASYNC
USART3.BaudRate=921600 USART3.BaudRate=2000000
USART3.IPParameters=VirtualMode,BaudRate USART3.IPParameters=VirtualMode,BaudRate
USART3.VirtualMode=VM_ASYNC USART3.VirtualMode=VM_ASYNC
USB_DEVICE.CLASS_NAME_FS=MSC USB_DEVICE.CLASS_NAME_FS=MSC

View File

@ -0,0 +1,224 @@
# 配置管理功能说明
## 概述
将数据输出模式配置从编译时的 `#define` 改为运行时可配置配置存储在SD卡文件中系统启动时自动加载。
## 配置项
### 1. UART输出使能 (uart_output_enabled)
- **功能**: 控制是否通过串口(RS485)发送数据
- **默认值**: 1 (启用)
- **取值**: 0=禁用, 1=启用
### 2. 存储使能 (storage_enabled)
- **功能**: 控制是否将数据存储到SD卡
- **默认值**: 0 (禁用)
- **取值**: 0=禁用, 1=启用
## 配置文件
### 文件路径
```
0:/CONFIG.TXT
```
### 文件格式
```
UART=1
STORAGE=0
SESSION=0
VERSION=65536
```
**说明**:
- `UART`: 串口输出使能状态 (0或1)
- `STORAGE`: SD卡存储使能状态 (0或1)
- `SESSION`: 会话序号 (用于数据存储文件夹命名)
- `VERSION`: 配置版本号 (用于验证配置兼容性)
## 使用方法
### 1. 初始化配置管理器
```c
// 在main()函数中初始化
Config_Init(); // 设置默认配置
```
### 2. 加载配置
```c
// 在文件系统挂载后加载配置
if (Config_Load() == HAL_OK) {
// 配置加载成功
} else {
// 配置加载失败,使用默认值
}
```
### 3. 读取配置
```c
// 检查串口输出是否启用
if (Config_IsUartOutputEnabled()) {
RS485_SendData(data, size);
}
// 检查存储是否启用
if (Config_IsStorageEnabled()) {
DataStorage_WriteData(&storage, &packet);
}
```
### 4. 修改配置
```c
// 修改串口输出设置
Config_SetUartOutput(1); // 启用
Config_SetUartOutput(0); // 禁用
// 修改存储设置
Config_SetStorage(1); // 启用
Config_SetStorage(0); // 禁用
// 保存配置到SD卡
Config_Save();
```
## 配置文件管理
### 创建配置文件
如果SD卡中不存在配置文件系统会在首次启动时自动创建使用默认配置值。
### 修改配置文件
可以通过以下方式修改配置:
1. **通过USB连接修改**:
- 将设备连接到PC
- 打开SD卡中的 `CONFIG.TXT` 文件
- 修改配置值
- 保存文件
- 断开USB重启设备
2. **通过代码修改**:
```c
Config_SetUartOutput(1);
Config_SetStorage(1);
Config_Save(); // 保存到SD卡
```
### 恢复默认配置
```c
Config_SetDefaults(); // 恢复默认值
Config_Save(); // 保存到SD卡
```
## 配置验证
配置管理器包含以下验证机制:
1. **版本检查**: 确保配置文件版本与当前软件兼容
2. **值范围检查**: 确保配置值在有效范围内 (0或1)
3. **校验和验证**: 检测配置数据完整性
如果验证失败,系统将自动使用默认配置。
## 与原有代码的对比
### 原有方式(编译时配置)
```c
#define DATA_OUTPUT_MODE_UART 1
#define DATA_OUTPUT_MODE_STORAGE 0
#if DATA_OUTPUT_MODE_UART
RS485_SendData(data, size);
#endif
#if DATA_OUTPUT_MODE_STORAGE
DataStorage_WriteData(&storage, &packet);
#endif
```
### 新方式(运行时配置)
```c
// 配置从SD卡加载
if (Config_IsUartOutputEnabled()) {
RS485_SendData(data, size);
}
if (Config_IsStorageEnabled()) {
DataStorage_WriteData(&storage, &packet);
}
```
## 优势
1. **灵活性**: 无需重新编译即可更改配置
2. **便捷性**: 通过修改SD卡文件即可调整系统行为
3. **可维护性**: 配置集中管理,易于维护
4. **可扩展性**: 易于添加新的配置项
## 注意事项
1. **文件系统依赖**: 配置加载需要SD卡文件系统正常工作
2. **启动顺序**: 必须在文件系统挂载后才能加载配置
3. **默认配置**: 如果配置文件不存在或损坏,系统使用默认配置
4. **性能影响**: 运行时检查比编译时宏略慢,但影响可忽略
## 文件修改清单
1. **User/config_manager.h** - 配置管理器头文件
2. **User/config_manager.c** - 配置管理器实现
3. **Core/Src/main.c** - 集成配置管理器,替换编译时宏
## 配置示例
### 示例1: 仅串口输出
```
UART=1
STORAGE=0
SESSION=0
VERSION=65536
```
### 示例2: 仅SD卡存储
```
UART=0
STORAGE=1
SESSION=5
VERSION=65536
```
### 示例3: 同时启用
```
UART=1
STORAGE=1
SESSION=10
VERSION=65536
```
### 示例4: 全部禁用
```
UART=0
STORAGE=0
SESSION=0
VERSION=65536
```
## 会话序号管理
### 会话序号的作用
会话序号用于为每次数据采集创建唯一的文件夹。每次系统启动并开始数据存储时,会话序号会自动递增,生成类似 `SESSION_000001``SESSION_000002` 的文件夹名称。
### 会话序号函数
```c
// 获取当前会话序号
uint32_t session_num = Config_GetSessionNumber();
// 设置会话序号
Config_SetSessionNumber(100);
// 递增会话序号并返回新值
uint32_t new_session = Config_IncrementSessionNumber();
```
### 注意事项
- 会话序号在 [`DataStorage_CreateSessionFolder()`](User/data_storage.c:413) 中自动递增
- 配置文件统一管理,替代了原来的 `PARAM.TXT` 文件
- 会话序号会随配置一起保存到 `CONFIG.TXT`

View File

@ -0,0 +1,107 @@
# 串口发送监控功能说明
## 概述
在系统监控模块中增加了串口UART/RS485发送数据的统计功能用于跟踪通过串口发出的数据量和错误情况。
## 新增功能
### 1. 监控统计字段
`SystemMonitorStats_t` 结构体中新增了三个字段:
```c
// 串口发送监控信息
uint32_t uart_tx_count; // 串口发送次数
uint32_t uart_tx_bytes; // 串口发送总字节数
uint32_t uart_tx_error_count; // 串口发送错误次数
```
### 2. 监控函数
#### SystemMonitor_ReportUARTTx()
```c
void SystemMonitor_ReportUARTTx(uint32_t bytes_sent);
```
- **功能**: 报告成功的串口发送操作
- **参数**: `bytes_sent` - 本次发送的字节数
- **调用时机**: 在 `RS485_SendData()` 中DMA启动成功后调用
#### SystemMonitor_ReportUARTTxError()
```c
void SystemMonitor_ReportUARTTxError(void);
```
- **功能**: 报告串口发送错误
- **调用时机**: 在 `RS485_SendData()` 中DMA启动失败时调用
### 3. 集成位置
#### rs485_driver.c
`RS485_SendData()` 函数中集成了监控调用:
- DMA启动成功时调用 `SystemMonitor_ReportUARTTx(Size)` 记录发送字节数
- DMA启动失败时调用 `SystemMonitor_ReportUARTTxError()` 记录错误
### 4. 监控数据保存
监控数据会定期保存到SD卡的 `LOG.TXT` 文件中,采用精简格式以减少阻塞时间:
```
Samples:[样本数] Ovf:[溢出数]
SD:Wr=[写入次数] Err=[错误数] Full=[满次数] Bytes=[字节数] Files=[文件数] Drop=[丢弃数]
UART:Tx=[发送次数] Bytes=[字节数] Err=[错误数]
```
**示例输出**
```
Samples:1000000 Ovf:5
SD:Wr=500 Err=2 Full=3 Bytes=32000000 Files=10 Drop=15
UART:Tx=250 Bytes=12500 Err=1
```
## 使用示例
### 获取串口统计信息
```c
SystemMonitorStats_t stats;
SystemMonitor_GetStats(&stats);
// 访问串口统计数据
uint32_t tx_count = stats.uart_tx_count;
uint32_t tx_bytes = stats.uart_tx_bytes;
uint32_t tx_errors = stats.uart_tx_error_count;
```
### 计算发送速率
```c
// 假设运行时间为 runtime_seconds 秒
float bytes_per_second = (float)stats.uart_tx_bytes / runtime_seconds;
float packets_per_second = (float)stats.uart_tx_count / runtime_seconds;
```
## 文件修改清单
1. **User/system_monitor.h**
- 在 `SystemMonitorStats_t` 结构体中添加串口统计字段
- 声明 `SystemMonitor_ReportUARTTx()``SystemMonitor_ReportUARTTxError()` 函数
2. **User/system_monitor.c**
- 实现 `SystemMonitor_ReportUARTTx()` 函数
- 实现 `SystemMonitor_ReportUARTTxError()` 函数
- 更新 `SystemMonitor_SaveStatus()` 函数,在保存的日志中包含串口统计信息
3. **User/rs485_driver.c**
- 包含 `system_monitor.h` 头文件
- 在 `RS485_SendData()` 函数中添加监控调用
## 注意事项
1. **性能影响**: 监控函数调用非常轻量(仅增加计数器),对系统性能影响可忽略不计
2. **线程安全**: 当前实现未使用互斥锁,如果在多线程环境中使用,建议添加适当的保护机制
3. **计数器溢出**: 使用 `uint32_t` 类型,在高频发送场景下可能溢出,建议定期保存和重置统计数据
4. **错误统计**: 仅统计DMA启动失败的情况不包括传输过程中的错误如需要可在DMA错误回调中添加
## 扩展建议
如需更详细的监控,可以考虑添加:
- 平均包大小统计
- 发送延迟统计
- 忙状态拒绝次数(`HAL_BUSY` 返回次数)
- 按时间段的发送速率统计

234
User/config_manager.c Normal file
View File

@ -0,0 +1,234 @@
#include "config_manager.h"
#include "fatfs.h"
#include "ff.h"
#include <string.h>
#include <stdio.h>
// 全局配置变量
static SystemConfig_t g_system_config = {0};
static uint8_t g_config_initialized = 0;
// 计算校验和
static uint32_t Calculate_Checksum(const SystemConfig_t *config)
{
uint32_t checksum = 0;
checksum += config->uart_output_enabled;
checksum += config->storage_enabled;
checksum += config->session_number;
checksum += config->config_version;
return checksum;
}
/**
* @brief
* @param None
* @retval None
*/
void Config_Init(void)
{
if (!g_config_initialized) {
Config_SetDefaults();
g_config_initialized = 1;
}
}
/**
* @brief
* @param None
* @retval None
*/
void Config_SetDefaults(void)
{
g_system_config.uart_output_enabled = DEFAULT_UART_OUTPUT_ENABLED;
g_system_config.storage_enabled = DEFAULT_STORAGE_ENABLED;
g_system_config.session_number = 0; // 初始会话序号为0
g_system_config.config_version = CONFIG_VERSION;
g_system_config.checksum = Calculate_Checksum(&g_system_config);
}
/**
* @brief SD卡加载配置
* @param None
* @retval HAL_OK: , HAL_ERROR: 使
*/
HAL_StatusTypeDef Config_Load(void)
{
FIL file;
FRESULT res;
UINT bytes_read;
char buffer[128];
// 尝试打开配置文件
res = f_open(&file, CONFIG_FILE_PATH, FA_READ);
if (res != FR_OK) {
// 文件不存在,使用默认配置并保存
Config_SetDefaults();
Config_Save();
return HAL_ERROR;
}
// 读取配置文件
res = f_read(&file, buffer, sizeof(buffer), &bytes_read);
f_close(&file);
if (res != FR_OK || bytes_read == 0) {
Config_SetDefaults();
return HAL_ERROR;
}
// 解析配置(简单的文本格式)
SystemConfig_t temp_config;
int uart_enabled, storage_enabled;
unsigned int version, session_num;
int parsed = sscanf(buffer,
"UART=%d\nSTORAGE=%d\nSESSION=%u\nVERSION=%u\n",
&uart_enabled,
&storage_enabled,
&session_num,
&version);
if (parsed == 4 && version == CONFIG_VERSION) {
temp_config.uart_output_enabled = (uint8_t)uart_enabled;
temp_config.storage_enabled = (uint8_t)storage_enabled;
temp_config.session_number = session_num;
temp_config.config_version = version;
temp_config.checksum = Calculate_Checksum(&temp_config);
// 验证配置值的合法性
if (temp_config.uart_output_enabled <= 1 &&
temp_config.storage_enabled <= 1) {
memcpy(&g_system_config, &temp_config, sizeof(SystemConfig_t));
return HAL_OK;
}
}
// 解析失败,使用默认配置
Config_SetDefaults();
return HAL_ERROR;
}
/**
* @brief SD卡
* @param None
* @retval HAL_OK: , HAL_ERROR:
*/
HAL_StatusTypeDef Config_Save(void)
{
FIL file;
FRESULT res;
UINT bytes_written;
char buffer[128];
// 更新校验和
g_system_config.checksum = Calculate_Checksum(&g_system_config);
// 创建或覆盖配置文件
res = f_open(&file, CONFIG_FILE_PATH, FA_CREATE_ALWAYS | FA_WRITE);
if (res != FR_OK) {
return HAL_ERROR;
}
// 格式化配置数据为文本(精简格式)
int len = snprintf(buffer, sizeof(buffer),
"UART=%d\nSTORAGE=%d\nSESSION=%lu\nVERSION=%lu\n",
g_system_config.uart_output_enabled,
g_system_config.storage_enabled,
g_system_config.session_number,
g_system_config.config_version);
// 写入配置数据
res = f_write(&file, buffer, len, &bytes_written);
if (res != FR_OK || bytes_written != (UINT)len) {
f_close(&file);
return HAL_ERROR;
}
// 关闭文件
f_close(&file);
return HAL_OK;
}
/**
* @brief 使
* @param None
* @retval 1: , 0:
*/
uint8_t Config_IsUartOutputEnabled(void)
{
return g_system_config.uart_output_enabled;
}
/**
* @brief 使
* @param None
* @retval 1: , 0:
*/
uint8_t Config_IsStorageEnabled(void)
{
return g_system_config.storage_enabled;
}
/**
* @brief 使
* @param enabled: 1=, 0=
* @retval None
*/
void Config_SetUartOutput(uint8_t enabled)
{
g_system_config.uart_output_enabled = (enabled != 0) ? 1 : 0;
}
/**
* @brief 使
* @param enabled: 1=, 0=
* @retval None
*/
void Config_SetStorage(uint8_t enabled)
{
g_system_config.storage_enabled = (enabled != 0) ? 1 : 0;
}
/**
* @brief
* @param config:
* @retval None
*/
void Config_GetConfig(SystemConfig_t *config)
{
if (config != NULL) {
memcpy(config, &g_system_config, sizeof(SystemConfig_t));
}
}
/**
* @brief
* @param None
* @retval
*/
uint32_t Config_GetSessionNumber(void)
{
return g_system_config.session_number;
}
/**
* @brief
* @param session_number:
* @retval None
*/
void Config_SetSessionNumber(uint32_t session_number)
{
g_system_config.session_number = session_number;
}
/**
* @brief
* @param None
* @retval
*/
uint32_t Config_IncrementSessionNumber(void)
{
g_system_config.session_number++;
return g_system_config.session_number;
}

42
User/config_manager.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef CONFIG_MANAGER_H
#define CONFIG_MANAGER_H
#include "main.h"
#include <stdint.h>
// 配置文件路径
#define CONFIG_FILE_PATH "0:/CONFIG.TXT"
// 系统配置结构体
typedef struct {
uint8_t uart_output_enabled; // 串口输出使能: 0=禁用, 1=启用
uint8_t storage_enabled; // SD卡存储使能: 0=禁用, 1=启用
uint32_t session_number; // 会话序号(用于数据存储文件夹命名)
uint32_t config_version; // 配置版本号(用于验证)
uint32_t checksum; // 校验和(用于验证配置完整性)
} SystemConfig_t;
// 默认配置值
#define DEFAULT_UART_OUTPUT_ENABLED 1
#define DEFAULT_STORAGE_ENABLED 0
#define CONFIG_VERSION 0x00010000 // 版本 1.0.0
// 函数声明
void Config_Init(void);
HAL_StatusTypeDef Config_Load(void);
HAL_StatusTypeDef Config_Save(void);
void Config_SetDefaults(void);
// 配置访问函数
uint8_t Config_IsUartOutputEnabled(void);
uint8_t Config_IsStorageEnabled(void);
void Config_SetUartOutput(uint8_t enabled);
void Config_SetStorage(uint8_t enabled);
void Config_GetConfig(SystemConfig_t *config);
// 会话序号管理函数
uint32_t Config_GetSessionNumber(void);
void Config_SetSessionNumber(uint32_t session_number);
uint32_t Config_IncrementSessionNumber(void);
#endif // CONFIG_MANAGER_H

View File

@ -1,5 +1,6 @@
#include "data_storage.h" #include "data_storage.h"
#include "system_monitor.h" #include "system_monitor.h"
#include "config_manager.h"
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -416,12 +417,8 @@ HAL_StatusTypeDef DataStorage_CreateSessionFolder(DataStorageHandle_t *handle)
return HAL_ERROR; return HAL_ERROR;
} }
// 从PARAM.TXT加载当前序号 // 从配置管理器获取并递增会话序号
uint32_t session_number = 0; uint32_t session_number = Config_IncrementSessionNumber();
DataStorage_LoadSessionNumber(&session_number);
// 递增序号
session_number++;
// 生成会话文件夹名(基于序号) // 生成会话文件夹名(基于序号)
snprintf(handle->current_session_path, sizeof(handle->current_session_path), snprintf(handle->current_session_path, sizeof(handle->current_session_path),
@ -439,8 +436,8 @@ HAL_StatusTypeDef DataStorage_CreateSessionFolder(DataStorageHandle_t *handle)
return HAL_ERROR; return HAL_ERROR;
} }
// 保存更新后的序号到PARAM.TXT // 保存更新后的配置(包含新的会话序号)
if (DataStorage_SaveSessionNumber(session_number) != HAL_OK) { if (Config_Save() != HAL_OK) {
// 即使保存失败,也继续使用该文件夹 // 即使保存失败,也继续使用该文件夹
// 这不是致命错误 // 这不是致命错误
} }

View File

@ -1,4 +1,5 @@
#include "rs485_driver.h" #include "rs485_driver.h"
#include "system_monitor.h"
#include <string.h> #include <string.h>
static UART_HandleTypeDef *g_huart_485 = NULL; static UART_HandleTypeDef *g_huart_485 = NULL;
@ -36,6 +37,13 @@ HAL_StatusTypeDef RS485_SendData(uint8_t *pData, uint16_t Size)
// 如果启动DMA失败需要清除忙标志并切换回接收模式 // 如果启动DMA失败需要清除忙标志并切换回接收模式
HAL_GPIO_WritePin(g_de_re_port, g_de_re_pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(g_de_re_port, g_de_re_pin, GPIO_PIN_RESET);
g_rs485_tx_busy = 0; g_rs485_tx_busy = 0;
// 报告串口发送错误
SystemMonitor_ReportUARTTxError();
}
else
{
// 报告串口发送成功(记录字节数)
SystemMonitor_ReportUARTTx(Size);
} }
return ret; return ret;

View File

@ -100,6 +100,27 @@ void SystemMonitor_ReportDataDropped(void)
g_system_stats.sd_data_dropped_count++; g_system_stats.sd_data_dropped_count++;
} }
/**
* @brief
* @param bytes_sent:
* @retval None
*/
void SystemMonitor_ReportUARTTx(uint32_t bytes_sent)
{
g_system_stats.uart_tx_count++;
g_system_stats.uart_tx_bytes += bytes_sent;
}
/**
* @brief
* @param None
* @retval None
*/
void SystemMonitor_ReportUARTTxError(void)
{
g_system_stats.uart_tx_error_count++;
}
/** /**
* @brief * @brief
* @param None * @param None
@ -118,19 +139,11 @@ HAL_StatusTypeDef SystemMonitor_SaveStatus(void)
return HAL_ERROR; return HAL_ERROR;
} }
// 格式化监控数据为文本 // 格式化监控数据为文本(精简格式,减少阻塞时间)
int len = snprintf(buffer, sizeof(buffer), int len = snprintf(buffer, sizeof(buffer),
"=== System Monitor Status ===\r\n" "Samples:%lu Ovf:%lu\r\n"
"Total Samples: %lu\r\n" "SD:Wr=%lu Err=%lu Full=%lu Bytes=%lu Files=%lu Drop=%lu\r\n"
"Data Overflow: %lu\r\n" "UART:Tx=%lu Bytes=%lu Err=%lu\r\n",
"\r\n"
"=== SD Card Stats ===\r\n"
"SD Write Count: %lu\r\n"
"SD Write Errors: %lu\r\n"
"SD Buffer Full: %lu\r\n"
"SD Total Bytes: %lu\r\n"
"SD File Count: %lu\r\n"
"SD Data Dropped: %lu\r\n",
g_system_stats.total_samples, g_system_stats.total_samples,
g_system_stats.data_overflow_count, g_system_stats.data_overflow_count,
g_system_stats.sd_write_count, g_system_stats.sd_write_count,
@ -138,7 +151,10 @@ HAL_StatusTypeDef SystemMonitor_SaveStatus(void)
g_system_stats.sd_buffer_full_count, g_system_stats.sd_buffer_full_count,
g_system_stats.sd_total_bytes_written, g_system_stats.sd_total_bytes_written,
g_system_stats.sd_file_count, g_system_stats.sd_file_count,
g_system_stats.sd_data_dropped_count g_system_stats.sd_data_dropped_count,
g_system_stats.uart_tx_count,
g_system_stats.uart_tx_bytes,
g_system_stats.uart_tx_error_count
); );
// 写入监控数据 // 写入监控数据

View File

@ -19,6 +19,11 @@ typedef struct {
uint32_t sd_total_bytes_written; // SD卡总写入字节数 uint32_t sd_total_bytes_written; // SD卡总写入字节数
uint32_t sd_file_count; // 创建的文件数量 uint32_t sd_file_count; // 创建的文件数量
uint32_t sd_data_dropped_count; // 未存储的数据数量(缓冲区满时丢弃) uint32_t sd_data_dropped_count; // 未存储的数据数量(缓冲区满时丢弃)
// 串口发送监控信息
uint32_t uart_tx_count; // 串口发送次数
uint32_t uart_tx_bytes; // 串口发送总字节数
uint32_t uart_tx_error_count; // 串口发送错误次数
} SystemMonitorStats_t; } SystemMonitorStats_t;
// 函数声明 // 函数声明
@ -34,6 +39,10 @@ void SystemMonitor_ReportSDBufferFull(void);
void SystemMonitor_ReportSDFileCreated(void); void SystemMonitor_ReportSDFileCreated(void);
void SystemMonitor_ReportDataDropped(void); void SystemMonitor_ReportDataDropped(void);
// 串口发送监控函数
void SystemMonitor_ReportUARTTx(uint32_t bytes_sent);
void SystemMonitor_ReportUARTTxError(void);
// 监控状态持久化函数 // 监控状态持久化函数
HAL_StatusTypeDef SystemMonitor_SaveStatus(void); HAL_StatusTypeDef SystemMonitor_SaveStatus(void);
HAL_StatusTypeDef SystemMonitor_LoadStatus(void); HAL_StatusTypeDef SystemMonitor_LoadStatus(void);