From 5419e33397d86deefe8c0455624db57363e2a06f Mon Sep 17 00:00:00 2001 From: zhoujie <929834232@qq.com> Date: Sat, 7 Feb 2026 13:02:59 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(monitor):=20=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E7=9B=91=E6=8E=A7=E4=B8=8E=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增数据存储缓冲区可用性检查,防止缓冲区满时数据丢失 - 新增会话文件夹管理功能,每次上电自动创建新的数据存储文件夹 - 新增监控状态定期保存功能,将系统统计信息写入MONITOR.TXT文件 - 新增数据丢弃统计,记录因缓冲区满而未存储的数据包数量 - 优化数据输出模式配置,支持串口输出和存储到卡的独立控制 - 优化USB连接处理逻辑,增加系统稳定性检查 🐛 fix(interrupt): 调整中断优先级配置 - 提高USART1中断优先级(从6调整为2),确保串口通信及时响应 - 调整DMA2_Stream5中断优先级(从0调整为5),优化数据传输调度 - 修复RS485驱动中的忙标志逻辑,改为阻塞式传输以提高可靠性 ♻️ refactor(config): 优化系统配置和存储设置 - 重构宏定义配置,统一系统监控开关,分离数据输出模式控制 - 将SD卡最大扇区大小从512调整为4096,优化大文件存储性能 - 增加堆栈大小配置(从0x800调整为0x1000),提高系统稳定性 - 优化USB存储读写超时设置,使用最大超时值确保操作完成 📝 docs(comments): 更新代码注释和文档 - 更新数据存储模块的注释,说明新的会话文件夹管理机制 - 在main.c中添加数据输出模式选择的详细说明注释 - 更新系统监控统计输出格式,包含新增的数据丢弃统计项 --- Core/Src/dma.c | 2 +- Core/Src/main.c | 124 +++++++---- Core/Src/usart.c | 2 +- FATFS/Target/ffconf.h | 2 +- STM_ATEM_F405.ioc | 8 +- USB_DEVICE/App/usbd_storage_if.c | 6 +- User/data_storage.c | 171 ++++++++++++++- User/data_storage.h | 20 +- User/rs485_driver.c | 20 +- User/sd_test.c | 349 ++++++++++++++++++------------- User/system_monitor.c | 81 +++++++ User/system_monitor.h | 9 + 12 files changed, 577 insertions(+), 217 deletions(-) diff --git a/Core/Src/dma.c b/Core/Src/dma.c index 6e0ede8..7f31549 100644 --- a/Core/Src/dma.c +++ b/Core/Src/dma.c @@ -57,7 +57,7 @@ void MX_DMA_Init(void) HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 10, 0); HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); /* DMA2_Stream5_IRQn interrupt configuration */ - HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 0, 0); + HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 5, 0); HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn); /* DMA2_Stream6_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 10, 0); diff --git a/Core/Src/main.c b/Core/Src/main.c index 22197a3..67e45a3 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -46,12 +46,15 @@ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ -// 串口输出控制开关 -#define ENABLE_UART_DEBUG_OUTPUT 1 -#define DEBUG_OUTPUT_INTERVAL_MS 1000 // 调试输出间隔(毫秒) - -// 监控功能宏开关 +// 监控功能宏开关(统一控制串口输出和文件存储) #define ENABLE_SYSTEM_MONITOR 1 // 系统监控开关 +#define DEBUG_OUTPUT_INTERVAL_MS 1000 // 调试输出间隔(毫秒) +#define MONITOR_SAVE_INTERVAL_MS 10000 // 监控状态保存间隔(毫秒) - 10秒 + +// 数据输出模式选择(可以同时启用) +#define DATA_OUTPUT_MODE_UART 0 // 0=禁用, 1=启用串口输出数据 +#define DATA_OUTPUT_MODE_STORAGE 1 // 0=禁用, 1=启用存储到卡 +// 注意:两个模式可以同时启用,但会增加系统负载 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ @@ -87,7 +90,9 @@ static uint32_t g_last_usb_check = 0; // 性能监控和调试输出 static uint32_t g_last_debug_output = 0; -static uint8_t g_debug_output_enabled = ENABLE_UART_DEBUG_OUTPUT; + +// 监控状态保存 +static uint32_t g_last_monitor_save = 0; /* USER CODE END PV */ @@ -147,6 +152,13 @@ static void ProcessAdcData(void) LTC2508_BufferTypeDef *ready_buffer = NULL; if (LTC2508_GetReadyBuffer(&ready_buffer) == LTC2508_OK && ready_buffer != NULL) { + // 检查存储缓冲区是否可用(用于决定是否存储数据) + uint8_t can_store_data = 0; + if (g_recording_enabled) { + uint32_t max_packet_size = sizeof(CorrectedDataPacket_t); + can_store_data = DataStorage_IsBufferAvailable(&g_data_storage, max_packet_size); + } + #if ENABLE_SYSTEM_MONITOR SystemMonitor_IncrementSampleCount(); #endif @@ -174,28 +186,41 @@ static void ProcessAdcData(void) correction_applied = 1; - // 发送校正后的数据包 -// RS485_SendData((uint8_t*)&g_corrected_packet, sizeof(CorrectedDataPacket_t)); +#if DATA_OUTPUT_MODE_UART + // 发送校正后的数据包到串口 + RS485_SendData((uint8_t*)&g_corrected_packet, sizeof(CorrectedDataPacket_t)); +#endif } else { // 4b. 校正失败或未启用,使用原始数据 PackData(&g_data_packet, raw_adc[0], raw_adc[1], raw_adc[2]); - // 发送原始数据包 +#if DATA_OUTPUT_MODE_UART + // 发送原始数据包到串口 RS485_SendData((uint8_t*)&g_data_packet, sizeof(DataPacket_t)); +#endif } - // 6. 存储数据到SD卡 (如果启用记录) + // 6. 存储数据到SD卡 (如果启用记录且缓冲区可用) +#if DATA_OUTPUT_MODE_STORAGE if (g_recording_enabled) { - if (correction_applied) { - // 存储校正后的数据 - DataStorage_WriteCorrectedData(&g_data_storage, &g_corrected_packet); + if (can_store_data) { + if (correction_applied) { + // 存储校正后的数据 + DataStorage_WriteCorrectedData(&g_data_storage, &g_corrected_packet); + } else { + // 存储原始数据 + DataStorage_WriteData(&g_data_storage, &g_data_packet); + } } else { - // 存储原始数据 - DataStorage_WriteData(&g_data_storage, &g_data_packet); + // 缓冲区满,数据被丢弃 +#if ENABLE_SYSTEM_MONITOR + SystemMonitor_ReportDataDropped(); +#endif } } +#endif // 7. 释放已处理的缓冲区 LTC2508_ReleaseBuffer(LTC2508_GetCurrentReadBuffer()); @@ -211,9 +236,9 @@ static void ProcessAdcData(void) static void DebugOutput_Init(void) { // USART3已在MX_USART3_UART_Init()中初始化 - if (g_debug_output_enabled) { - DebugOutput_SendString("\r\n=== System Debug Output Initialized ===\r\n"); - } +#if ENABLE_SYSTEM_MONITOR + DebugOutput_SendString("\r\n=== System Debug Output Initialized ===\r\n"); +#endif } /** @@ -223,11 +248,13 @@ static void DebugOutput_Init(void) */ static void DebugOutput_SendString(const char* str) { - if (!g_debug_output_enabled || str == NULL) { +#if ENABLE_SYSTEM_MONITOR + if (str == NULL) { return; } HAL_UART_Transmit(&huart3, (uint8_t*)str, strlen(str), 100); +#endif } /** @@ -237,10 +264,6 @@ static void DebugOutput_SendString(const char* str) static void DebugOutput_PrintSystemStats(void) { #if ENABLE_SYSTEM_MONITOR - if (!g_debug_output_enabled) { - return; - } - char buffer[256]; SystemMonitorStats_t sys_stats; SystemMonitor_GetStats(&sys_stats); @@ -261,12 +284,14 @@ static void DebugOutput_PrintSystemStats(void) "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 File Count: %lu\r\n" + "SD Data Dropped: %lu\r\n", sys_stats.sd_write_count, sys_stats.sd_write_error_count, sys_stats.sd_buffer_full_count, sys_stats.sd_total_bytes_written, - sys_stats.sd_file_count); + sys_stats.sd_file_count, + sys_stats.sd_data_dropped_count); DebugOutput_SendString(buffer); // 计算并打印统计指标 @@ -328,6 +353,12 @@ static void HandleUSBConnectionChange(void) // 卸载用于采样的文件系统 UnmountFileSystemForSampling(); + + while(1) + { + ; + } + } else { // USB断开:重新挂载文件系统,开始数据采集 DebugOutput_SendString("USB Disconnected: Starting data acquisition\r\n"); @@ -461,10 +492,6 @@ int main(void) MX_TIM2_Init(); /* USER CODE BEGIN 2 */ -// SD_NAND_Init_And_Mount(); -// SD_Test_Write(); -// SD_Test_Read(); - // 初始化系统监控 #if ENABLE_SYSTEM_MONITOR SystemMonitor_Init(); @@ -489,7 +516,7 @@ int main(void) // 初始化数据存储 if (DataStorage_Init(&g_data_storage) == HAL_OK) { // 开始数据记录 - StartRecording(); + StartRecording(); } } } else { @@ -502,6 +529,16 @@ int main(void) } } +#ifdef NEED_FORMAT_SD +// Raw_Hardware_Test(); +// SDNAND_ForceFormat_and_Mount(); + Run_SDNAND_SpeedTest_V2(); + while(1) + { + ; + } +#endif + // 启动TIM2定时器用于1ms周期的ADC数据处理 if (HAL_TIM_Base_Start_IT(&htim2) != HAL_OK) { Error_Handler(); @@ -533,21 +570,34 @@ int main(void) uint32_t current_tick = HAL_GetTick(); // USB连接状态检测 (每500ms检测一次) -// if (current_tick - g_last_usb_check >= 500) { -// HandleUSBConnectionChange(); -// g_last_usb_check = current_tick; -// } + if (current_tick - g_last_usb_check >= 500) { + HandleUSBConnectionChange(); + g_last_usb_check = current_tick; + } // 处理数据存储后台任务 (轮询方式) + // 优化:连续处理3次,加快缓冲区刷新速度 if (g_recording_enabled) { - DataStorage_ProcessBackgroundTasks(&g_data_storage); + // for (int i = 0; i < 3; i++) { + DataStorage_ProcessBackgroundTasks(&g_data_storage); + // } } // 定期输出调试信息 (每1秒输出一次) - if (g_debug_output_enabled && (current_tick - g_last_debug_output >= DEBUG_OUTPUT_INTERVAL_MS)) { +#if ENABLE_SYSTEM_MONITOR + if (current_tick - g_last_debug_output >= DEBUG_OUTPUT_INTERVAL_MS) { DebugOutput_PrintSystemStats(); g_last_debug_output = current_tick; } +#endif + + // 定期保存监控状态 (每1分钟保存一次) +#if ENABLE_SYSTEM_MONITOR + if (current_tick - g_last_monitor_save >= MONITOR_SAVE_INTERVAL_MS) { + SystemMonitor_SaveStatus(); + g_last_monitor_save = current_tick; + } +#endif // ADC采样由PA1外部中断触发,不在主循环中触发 // 可以在这里添加其他低优先级任务 @@ -617,7 +667,7 @@ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) if(LTC2508_IsInited() == 0) return; cnt ++; - // if(cnt % 2 == 0) + // if(cnt % 5 == 0) { // HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); if (GPIO_Pin == ADC_DRY_Pin) { diff --git a/Core/Src/usart.c b/Core/Src/usart.c index 7cc2534..1cd8aa9 100644 --- a/Core/Src/usart.c +++ b/Core/Src/usart.c @@ -131,7 +131,7 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx); /* USART1 interrupt Init */ - HAL_NVIC_SetPriority(USART1_IRQn, 6, 0); + HAL_NVIC_SetPriority(USART1_IRQn, 2, 0); HAL_NVIC_EnableIRQ(USART1_IRQn); /* USER CODE BEGIN USART1_MspInit 1 */ diff --git a/FATFS/Target/ffconf.h b/FATFS/Target/ffconf.h index 78333a4..3289dd1 100644 --- a/FATFS/Target/ffconf.h +++ b/FATFS/Target/ffconf.h @@ -175,7 +175,7 @@ / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() / function will be available. */ #define _MIN_SS 512 /* 512, 1024, 2048 or 4096 */ -#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +#define _MAX_SS 4096 /* 512, 1024, 2048 or 4096 */ /* These options configure the range of sector size to be supported. (512, 1024, / 2048 or 4096) Always set both 512 for most systems, all type of memory cards and / harddisk. But a larger value may be required for on-board flash memory and some diff --git a/STM_ATEM_F405.ioc b/STM_ATEM_F405.ioc index 7921126..de6e975 100644 --- a/STM_ATEM_F405.ioc +++ b/STM_ATEM_F405.ioc @@ -89,7 +89,7 @@ Dma.USART1_TX.5.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphData FATFS.BSP.number=1 FATFS.IPParameters=_USE_LFN,USE_DMA_CODE_SD,_MAX_SS,_MIN_SS FATFS.USE_DMA_CODE_SD=1 -FATFS._MAX_SS=512 +FATFS._MAX_SS=4096 FATFS._MIN_SS=512 FATFS._USE_LFN=2 FATFS0.BSP.STBoard=false @@ -167,7 +167,7 @@ NVIC.DMA1_Stream0_IRQn=true\:5\:0\:true\:false\:true\:false\:true\:true NVIC.DMA1_Stream3_IRQn=true\:5\:0\:true\:false\:true\:false\:true\:true NVIC.DMA2_Stream0_IRQn=true\:5\:0\:true\:false\:true\:false\:true\:true NVIC.DMA2_Stream3_IRQn=true\:10\:0\:true\:false\:true\:false\:true\:true -NVIC.DMA2_Stream5_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true +NVIC.DMA2_Stream5_IRQn=true\:5\:0\:true\:false\:true\:false\:true\:true NVIC.DMA2_Stream6_IRQn=true\:10\:0\:true\:false\:true\:false\:true\:true NVIC.DMA2_Stream7_IRQn=true\:12\:0\:true\:false\:true\:false\:true\:true NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false @@ -183,7 +183,7 @@ 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.SysTick_IRQn=true\:0\:0\:true\:false\:true\:false\:true\:false NVIC.TIM2_IRQn=true\:3\:0\:true\:false\:true\:true\:true\:true -NVIC.USART1_IRQn=true\:6\:0\:true\:false\:true\:true\:true\:true +NVIC.USART1_IRQn=true\:2\:0\:true\:false\:true\:true\:true\:true NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false PA1.GPIOParameters=GPIO_Label PA1.GPIO_Label=ADC_DRY @@ -291,7 +291,7 @@ ProjectManager.ProjectFileName=STM_ATEM_F405.ioc ProjectManager.ProjectName=STM_ATEM_F405 ProjectManager.ProjectStructure= ProjectManager.RegisterCallBack= -ProjectManager.StackSize=0x800 +ProjectManager.StackSize=0x1000 ProjectManager.TargetToolchain=STM32CubeIDE ProjectManager.ToolChainLocation= ProjectManager.UAScriptAfterPath= diff --git a/USB_DEVICE/App/usbd_storage_if.c b/USB_DEVICE/App/usbd_storage_if.c index d0cddcc..2a874d4 100644 --- a/USB_DEVICE/App/usbd_storage_if.c +++ b/USB_DEVICE/App/usbd_storage_if.c @@ -290,7 +290,7 @@ int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t bl // 使用较长的超时时间 (SD NAND 读写较慢) uint32_t timeout = 2000; - if (HAL_SD_ReadBlocks(&hsd, buf, blk_addr, blk_len, timeout) != HAL_OK) { + if (HAL_SD_ReadBlocks(&hsd, buf, blk_addr, blk_len, 0xffff) != HAL_OK) { return (USBD_FAIL); } @@ -321,10 +321,10 @@ int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t b // 修正 3: 增加超时时间,并等待 Busy 结束 uint32_t timeout = 5000; // 给 5秒,SD NAND 写入由于要擦除/编程,很慢 - if (HAL_SD_WriteBlocks(&hsd, buf, blk_addr, blk_len, timeout) != HAL_OK) { + if (HAL_SD_WriteBlocks(&hsd, buf, blk_addr, blk_len, 0xffff) != HAL_OK) { return (USBD_FAIL); } - + // 【最关键的一步】等待 SD NAND 内部编程结束 // 如果没有这一步,文件就会丢失! uint32_t tickstart = HAL_GetTick(); diff --git a/User/data_storage.c b/User/data_storage.c index 1f9cde2..68e445d 100644 --- a/User/data_storage.c +++ b/User/data_storage.c @@ -2,6 +2,7 @@ #include "system_monitor.h" #include #include +#include /** * @brief 初始化数据存储模块 @@ -29,9 +30,8 @@ HAL_StatusTypeDef DataStorage_Init(DataStorageHandle_t *handle) handle->flush_buffer = 1; handle->flush_in_progress = 0; - // 创建数据存储目录 - FRESULT res = f_mkdir(DATA_STORAGE_PATH); - if (res != FR_OK && res != FR_EXIST) { + // 创建新的会话文件夹(每次上电创建新文件夹) + if (DataStorage_CreateSessionFolder(handle) != HAL_OK) { return HAL_ERROR; } @@ -177,10 +177,10 @@ HAL_StatusTypeDef DataStorage_CreateNewFile(DataStorageHandle_t *handle) return HAL_ERROR; } - // 生成文件名 (基于时间戳) + // 生成文件名 (基于时间戳),文件存储在当前会话文件夹中 uint32_t timestamp = HAL_GetTick(); snprintf(handle->stats.current_filename, sizeof(handle->stats.current_filename), - "%s%s%08lX.dat", DATA_STORAGE_PATH, DATA_STORAGE_FILE_PREFIX, timestamp); + "%s%s%08lX.dat", handle->current_session_path, DATA_STORAGE_FILE_PREFIX, timestamp); // 创建并打开文件 FRESULT res = f_open(&handle->file, handle->stats.current_filename, @@ -287,8 +287,7 @@ HAL_StatusTypeDef DataStorage_FlushBuffer(DataStorageHandle_t *handle, uint8_t b flush_count++; // 每10次刷新才同步一次,或者文件即将达到最大大小时同步 - // 这样可以显著减少SD卡写入延迟,提高吞吐量 - if (flush_count % 10 == 0 || + if (flush_count % 10 == 0 || handle->stats.current_file_size + bytes_written >= DATA_STORAGE_FILE_MAX_SIZE) { f_sync(&handle->file); } @@ -368,3 +367,161 @@ HAL_StatusTypeDef DataStorage_SwitchBuffer(DataStorageHandle_t *handle) return HAL_OK; } + +/** + * @brief 检查缓冲区是否有足够空间可用 + * @param handle: 数据存储句柄指针 + * @param required_size: 需要的空间大小(字节) + * @retval 1: 缓冲区可用, 0: 缓冲区不可用 + */ +uint8_t DataStorage_IsBufferAvailable(DataStorageHandle_t *handle, uint32_t required_size) +{ + if (handle == NULL || !handle->initialized) { + return 0; + } + + // 如果未在记录状态,返回不可用 + if (handle->stats.state != DATA_STORAGE_RECORDING) { + return 0; + } + + DataBuffer_t *active_buf = &handle->buffers[handle->active_buffer]; + + // 检查当前活动缓冲区是否有足够空间 + if (active_buf->index + required_size <= DATA_STORAGE_BUFFER_SIZE) { + return 1; // 当前缓冲区有足够空间 + } + + // 当前缓冲区空间不足,检查是否可以切换到另一个缓冲区 + uint8_t next_buffer = (handle->active_buffer == 0) ? 1 : 0; + DataBuffer_t *next_buf = &handle->buffers[next_buffer]; + + // 如果另一个缓冲区正在刷新或准备刷新,则不可用 + if (next_buf->state == BUFFER_FLUSHING || next_buf->state == BUFFER_READY_TO_FLUSH) { + return 0; // 无法切换,缓冲区不可用 + } + + // 另一个缓冲区可用 + return 1; +} + +/** + * @brief 创建新的会话文件夹 + * @param handle: 数据存储句柄指针 + * @retval HAL_StatusTypeDef + */ +HAL_StatusTypeDef DataStorage_CreateSessionFolder(DataStorageHandle_t *handle) +{ + if (handle == NULL) { + return HAL_ERROR; + } + + // 从PARAM.TXT加载当前序号 + uint32_t session_number = 0; + DataStorage_LoadSessionNumber(&session_number); + + // 递增序号 + session_number++; + + // 生成会话文件夹名(基于序号) + snprintf(handle->current_session_path, sizeof(handle->current_session_path), + "%s/%s%06lu", DATA_STORAGE_BASE_PATH, DATA_STORAGE_FOLDER_PREFIX, session_number); + + // 创建基础数据目录(如果不存在) + FRESULT res = f_mkdir(DATA_STORAGE_BASE_PATH); + if (res != FR_OK && res != FR_EXIST) { + return HAL_ERROR; + } + + // 创建会话文件夹 + res = f_mkdir(handle->current_session_path); + if (res != FR_OK && res != FR_EXIST) { + return HAL_ERROR; + } + + // 保存更新后的序号到PARAM.TXT + if (DataStorage_SaveSessionNumber(session_number) != HAL_OK) { + // 即使保存失败,也继续使用该文件夹 + // 这不是致命错误 + } + + return HAL_OK; +} + +/** + * @brief 从文件加载会话序号 + * @param session_number: 用于存储序号的指针 + * @retval HAL_StatusTypeDef + */ +HAL_StatusTypeDef DataStorage_LoadSessionNumber(uint32_t *session_number) +{ + if (session_number == NULL) { + return HAL_ERROR; + } + + FIL file; + FRESULT res; + UINT bytes_read; + char buffer[16]; + + // 打开PARAM.TXT文件 + res = f_open(&file, DATA_STORAGE_PARAM_FILE, FA_READ); + if (res != FR_OK) { + // 文件不存在,返回初始序号0 + *session_number = 0; + return HAL_OK; + } + + // 读取序号 + res = f_read(&file, buffer, sizeof(buffer) - 1, &bytes_read); + if (res != FR_OK) { + f_close(&file); + *session_number = 0; + return HAL_OK; + } + + // 添加字符串结束符 + buffer[bytes_read] = '\0'; + + // 关闭文件 + f_close(&file); + + // 转换为数字 + *session_number = (uint32_t)atoi(buffer); + + return HAL_OK; +} + +/** + * @brief 保存会话序号到文件 + * @param session_number: 要保存的序号 + * @retval HAL_StatusTypeDef + */ +HAL_StatusTypeDef DataStorage_SaveSessionNumber(uint32_t session_number) +{ + FIL file; + FRESULT res; + UINT bytes_written; + char buffer[16]; + + // 创建或覆盖PARAM.TXT文件 + res = f_open(&file, DATA_STORAGE_PARAM_FILE, FA_CREATE_ALWAYS | FA_WRITE); + if (res != FR_OK) { + return HAL_ERROR; + } + + // 将序号转换为字符串 + snprintf(buffer, sizeof(buffer), "%lu", session_number); + + // 写入序号 + res = f_write(&file, buffer, strlen(buffer), &bytes_written); + if (res != FR_OK || bytes_written != strlen(buffer)) { + f_close(&file); + return HAL_ERROR; + } + + // 关闭文件 + f_close(&file); + + return HAL_OK; +} diff --git a/User/data_storage.h b/User/data_storage.h index d452560..a1419dd 100644 --- a/User/data_storage.h +++ b/User/data_storage.h @@ -11,8 +11,11 @@ // 数据存储配置 #define DATA_STORAGE_BUFFER_SIZE 32768 // 缓冲区大小(字节) #define DATA_STORAGE_FILE_MAX_SIZE (20*1024*1024) // 单个文件最大20MB -#define DATA_STORAGE_PATH "0:/DATA2" // 数据存储路径 -#define DATA_STORAGE_FILE_PREFIX "/ADC_DATA_" // 文件名前缀 +#define DATA_STORAGE_BASE_PATH "0:/DATA" // 数据存储基础路径 +#define DATA_STORAGE_FILE_PREFIX "/ADC_DATA_" // 文件名前缀 +#define DATA_STORAGE_FOLDER_PREFIX "SESSION_" // 文件夹名前缀 +#define DATA_STORAGE_PARAM_FILE "0:/PARAM.TXT" // 记录会话序号的文件 +#define DATA_STORAGE_MAX_PATH_LEN 128 // 最大路径长度 // 缓冲区状态 typedef enum { @@ -37,7 +40,7 @@ typedef struct { uint32_t file_count; uint32_t error_count; DataStorageState_t state; - char current_filename[64]; + char current_filename[256]; } DataStorageStats_t; // 双缓冲区结构 @@ -56,6 +59,7 @@ typedef struct { DataStorageStats_t stats; uint8_t initialized; uint8_t flush_in_progress; // 刷新进行中标志 + char current_session_path[DATA_STORAGE_MAX_PATH_LEN]; // 当前会话文件夹路径 } DataStorageHandle_t; // 函数声明 @@ -68,9 +72,19 @@ HAL_StatusTypeDef DataStorage_Flush(DataStorageHandle_t *handle); void DataStorage_GetStats(DataStorageHandle_t *handle, DataStorageStats_t *stats); HAL_StatusTypeDef DataStorage_CreateNewFile(DataStorageHandle_t *handle); +// 文件夹管理函数 +HAL_StatusTypeDef DataStorage_CreateSessionFolder(DataStorageHandle_t *handle); + +// 序号管理函数 +HAL_StatusTypeDef DataStorage_LoadSessionNumber(uint32_t *session_number); +HAL_StatusTypeDef DataStorage_SaveSessionNumber(uint32_t session_number); + // 双缓冲区管理函数 HAL_StatusTypeDef DataStorage_SwitchBuffer(DataStorageHandle_t *handle); HAL_StatusTypeDef DataStorage_FlushBuffer(DataStorageHandle_t *handle, uint8_t buffer_index); void DataStorage_ProcessBackgroundTasks(DataStorageHandle_t *handle); +// 缓冲区可用性检查函数 +uint8_t DataStorage_IsBufferAvailable(DataStorageHandle_t *handle, uint32_t required_size); + #endif // DATA_STORAGE_H diff --git a/User/rs485_driver.c b/User/rs485_driver.c index 4b8cb38..6689645 100644 --- a/User/rs485_driver.c +++ b/User/rs485_driver.c @@ -19,14 +19,14 @@ HAL_StatusTypeDef RS485_SendData(uint8_t *pData, uint16_t Size) { HAL_StatusTypeDef ret; - if (g_rs485_tx_busy) - { - return HAL_BUSY; - } +// if (g_rs485_tx_busy) +// { +// return HAL_BUSY; +// } - g_rs485_tx_busy = 1; +// g_rs485_tx_busy = 1; HAL_GPIO_WritePin(g_de_re_port, g_de_re_pin, GPIO_PIN_SET); // 设置为发送模式 - ret = HAL_UART_Transmit_IT(g_huart_485, pData, Size); + ret = HAL_UART_Transmit(g_huart_485, pData, Size, 0xffff); // 注意:不能在这里立即切换回接收模式! // DMA传输是非阻塞的,需要在传输完成回调中切换 @@ -34,11 +34,11 @@ HAL_StatusTypeDef RS485_SendData(uint8_t *pData, uint16_t Size) { // 如果启动DMA失败,需要清除忙标志并切换回接收模式 HAL_GPIO_WritePin(g_de_re_port, g_de_re_pin, GPIO_PIN_RESET); - g_rs485_tx_busy = 0; +// g_rs485_tx_busy = 0; } // 等待数据传输完成 - while(g_rs485_tx_busy) +// while(g_rs485_tx_busy) { ; } @@ -52,7 +52,7 @@ void RS485_TxCpltCallback(UART_HandleTypeDef *huart) if (huart == g_huart_485) { // DMA传输完成后切换回接收模式 - HAL_GPIO_WritePin(g_de_re_port, g_de_re_pin, GPIO_PIN_RESET); - g_rs485_tx_busy = 0; +// HAL_GPIO_WritePin(g_de_re_port, g_de_re_pin, GPIO_PIN_RESET); +// g_rs485_tx_busy = 0; } } diff --git a/User/sd_test.c b/User/sd_test.c index 911f589..66cf0dc 100644 --- a/User/sd_test.c +++ b/User/sd_test.c @@ -1,168 +1,217 @@ -/* USER CODE BEGIN Includes */ -#include // 用于 printf 打印调试信息 - -#include "fatfs.h" -#include "main.h" -#include "dma.h" -#include "fatfs.h" -#include "sdio.h" -#include "spi.h" -#include "tim.h" -#include "usart.h" -#include "usb_device.h" -#include "gpio.h" -#include "main.h" -#include "fatfs.h" #include "ff.h" -#include "data_packet.h" -#include "correction.h" -#include +#include "stdio.h" +#include "string.h" +#include "main.h" // Ensure huart3 is defined here -/* USER CODE END Includes */ +//#define NEED_FORMAT_SD +#ifdef NEED_FORMAT_SD -/* USER CODE BEGIN 4 */ +extern UART_HandleTypeDef huart3; -extern SD_HandleTypeDef hsd; -//DMA_HandleTypeDef hdma_sdio_rx; -//DMA_HandleTypeDef hdma_sdio_tx; +// Configuration +#define TEST_FILE_NAME "ST_DATA.BIN" +#define TEST_BUF_SIZE (64 * 1024) // 64KB Buffer (Good for SDIO DMA) +#define TOTAL_TEST_SIZE (16 * 1024 * 1024) // 16MB Total Data -/** - * @brief 针对 SD NAND 的专用初始化与格式化函数 - * @return 0: 成功, -1: 失败 - */ -int SD_NAND_Init_And_Mount(void) -{ +// Align buffer to 4 bytes for DMA compatibility +uint8_t g_test_buffer[TEST_BUF_SIZE] __attribute__((aligned(4))); +char uart_buf[128]; // Buffer for formatting UART messages + +// Helper function to print via UART3 +void UART3_Print(const char* str) { + HAL_UART_Transmit(&huart3, (uint8_t*)str, strlen(str), 100); +} + +void Run_SDNAND_SpeedTest(void) { + FIL file; FRESULT res; - BYTE workBuffer[4096]; // f_mkfs 需要的工作缓存 + UINT bw, br; + uint32_t startTime, endTime; + float speed; + UART3_Print("\r\n=== SD NAND Speed Test Start ===\r\n"); + + // Initialize buffer with dummy data + for (uint32_t i = 0; i < TEST_BUF_SIZE; i++) g_test_buffer[i] = (uint8_t)(i % 256); - // 1. 物理层延时:SD NAND 内部初始化比普通卡慢 - HAL_Delay(500); - - // 2. 检查底层 SDIO 是否通了 - // 如果这里检测不到,说明硬件接线或 IOC 时钟配置有问题 - HAL_SD_CardStateTypeDef cardState = HAL_SD_GetCardState(&hsd); // 确认句柄是 hsd1 还是 hsd - if (cardState == HAL_SD_CARD_ERROR) { - printf("Error: SD Card Hardware Not Ready!\r\n"); - return -1; - } - - // 3. 尝试挂载 (Force Mount = 1) - printf("Attempting to mount SD card...\r\n"); - res = f_mount(&SDFatFS, SDPath, 1); - - // 4. 分析挂载结果 - if (res == FR_OK) { - // 挂载成功,打印容量信息 - DWORD free_clusters; - FATFS *fs; - f_getfree(SDPath, &free_clusters, &fs); - uint32_t total_blocks = (fs->n_fatent - 2) * fs->csize; - printf("Mount Success! Total: %lu sectors\r\n", total_blocks); - return 0; - } - - // 5. 如果没有文件系统 (新芯片或损坏),执行格式化 - else if (res == FR_NO_FILESYSTEM) - { - printf("No Filesystem found. Starting formatting (Legacy Mode)...\r\n"); - - /* 针对你的 FatFs 版本 (5个参数) 的修正 - 参数说明: - 1. path: 路径 - 2. opt: 格式化选项 (FM_FAT32, FM_SFD 等) - 3. au: 簇大小 (Allocation Unit),设为 4096 适配 SD NAND - 4. work: 工作缓存指针 - 5. len: 工作缓存大小 - */ - - // 确保你的 ff.h 中定义了 FM_FAT32。如果没有,尝试用 0x02 代替 - BYTE format_opt = FM_FAT32; // 或者 FM_ANY | FM_SFD - - // 【关键】针对 SD NAND,强制使用 4096 字节的簇大小 - DWORD au_size = 512; - - // 修正后的函数调用:传入 5 个参数,而不是结构体 - res = f_mkfs(SDPath, format_opt, au_size, workBuffer, sizeof(workBuffer)); - - if (res == FR_OK) { - printf("Format Successful! Remounting...\r\n"); - // 格式化后建议取消挂载再重新挂载 - f_mount(NULL, SDPath, 0); - res = f_mount(&SDFatFS, SDPath, 1); - if (res == FR_OK) { - printf("Remount Success!\r\n"); - return 0; - } - } else { - printf("Format Failed! Error: %d\r\n", res); - } - } - - return -1; -} - -/* 简单的测试写文件函数 */ -void SD_Test_Write(void) { - FIL fil; - UINT bw; - if (f_open(&fil, "SDTest.txt", FA_WRITE | FA_CREATE_ALWAYS) == FR_OK) { - f_write(&fil, "Hello SD NAND!", 14, &bw); - f_close(&fil); - printf("Write Test: OK\r\n"); - } else { - printf("Write Test: Failed\r\n"); - } -} - -/** - * @brief 读取测试函数 - * 打开 SDTest.txt,读取内容并通过 printf 打印 - */ -void SD_Test_Read(void) -{ - FIL fil; // 文件对象 - FRESULT res; // FatFs 返回结果 - UINT br; // Bytes Read (实际读取到的字节数) - BYTE readBuf[128]; // 读取缓存 (根据需要调整大小) - - printf("\r\n--- Starting Read Test ---\r\n"); - - // 1. 打开文件 (使用 FA_READ 模式) - // 注意:文件名必须与写入时完全一致 - res = f_open(&fil, "SDTest.txt", FA_READ); - + // --- WRITE TEST --- + res = f_open(&file, TEST_FILE_NAME, FA_CREATE_ALWAYS | FA_WRITE); if (res != FR_OK) { - printf("Read Failed: f_open error code %d\r\n", res); - if (res == FR_NO_FILE) { - printf("Error: File does not exist. Did Write Test run successfully?\r\n"); - } + sprintf(uart_buf, "Open Fail (Write). Error: %d\r\n", res); + UART3_Print(uart_buf); return; } - // 2. 读取文件 - // 为了安全,读取长度设为 buffer大小 - 1,留一个字节给字符串结束符 - res = f_read(&fil, readBuf, sizeof(readBuf) - 1, &br); + UART3_Print("Testing Write Speed... Please wait.\r\n"); + startTime = HAL_GetTick(); + for (uint32_t i = 0; i < TOTAL_TEST_SIZE / TEST_BUF_SIZE; i++) { + res = f_write(&file, g_test_buffer, TEST_BUF_SIZE, &bw); + if (res != FR_OK) break; + } + f_sync(&file); // Flush to physical NAND + endTime = HAL_GetTick(); - if (res == FR_OK) { - // 3. 处理读取到的数据 - readBuf[br] = '\0'; // 手动添加字符串结束符,方便 printf 打印 + speed = ((float)TOTAL_TEST_SIZE / 1024.0 / 1024.0) / ((float)(endTime - startTime) / 1000.0); + sprintf(uart_buf, "WRITE: %.2f MB/s (%lu ms)\r\n", speed, endTime - startTime); + UART3_Print(uart_buf); + f_close(&file); - printf("Read Success!\r\n"); - printf("Bytes Read: %d\r\n", br); - - if (br > 0) { - printf("File Content: \r\n%s\r\n", readBuf); - } else { - printf("Warning: File is empty (0 bytes).\r\n"); - } - } else { - printf("Read Failed: f_read error code %d\r\n", res); + // --- READ TEST --- + res = f_open(&file, TEST_FILE_NAME, FA_READ); + if (res != FR_OK) { + UART3_Print("Open Fail (Read)\r\n"); + return; } - // 4. 关闭文件 (读取完成后也必须关闭,释放句柄) - f_close(&fil); + UART3_Print("Testing Read Speed... Please wait.\r\n"); + startTime = HAL_GetTick(); + for (uint32_t i = 0; i < TOTAL_TEST_SIZE / TEST_BUF_SIZE; i++) { + res = f_read(&file, g_test_buffer, TEST_BUF_SIZE, &br); + if (res != FR_OK) break; + } + endTime = HAL_GetTick(); - printf("--- Read Test Finished ---\r\n"); + speed = ((float)TOTAL_TEST_SIZE / 1024.0 / 1024.0) / ((float)(endTime - startTime) / 1000.0); + sprintf(uart_buf, "READ: %.2f MB/s (%lu ms)\r\n", speed, endTime - startTime); + UART3_Print(uart_buf); + + f_close(&file); + f_unlink(TEST_FILE_NAME); // Clean up + UART3_Print("=== Test Finished ===\r\n"); } -/* USER CODE END 4 */ + +extern SD_HandleTypeDef hsd; +extern DMA_HandleTypeDef hdma_sdio_rx; +extern DMA_HandleTypeDef hdma_sdio_tx; + +void Raw_Hardware_Test(void) { + uint32_t start, end; + HAL_StatusTypeDef status; + + UART3_Print("\r\n--- Starting Raw Hardware Test (No File System) ---\r\n"); + + // Test Raw DMA Write + start = HAL_GetTick(); + status = HAL_SD_WriteBlocks_DMA(&hsd, g_test_buffer, 1000, TEST_BUF_SIZE / 512); + if (status == HAL_OK) { + // Wait for DMA to finish + while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) {} + end = HAL_GetTick(); + sprintf(uart_buf, "Raw DMA Write: %lu ms\r\n", end - start); + UART3_Print(uart_buf); + } else { + UART3_Print("Raw Write Failed!\r\n"); + } +} + +void Run_SDNAND_SpeedTest_V2(void) { + FIL file; + FRESULT res; + UINT bw, br; + uint32_t startTime, endTime, duration; + float speed; + + UART3_Print("\r\n=== SD NAND Speed Test V2 ===\r\n"); + + // 预填充数据 + for (uint32_t i = 0; i < TEST_BUF_SIZE; i++) g_test_buffer[i] = (uint8_t)(i % 256); + + // --- WRITE TEST --- + res = f_open(&file, TEST_FILE_NAME, FA_CREATE_ALWAYS | FA_WRITE); + if (res != FR_OK) { + sprintf(uart_buf, "Open Fail (Write). Error: %d\r\n", res); + UART3_Print(uart_buf); + return; + } + + UART3_Print("Writing 16MB...\r\n"); + startTime = HAL_GetTick(); + + for (uint32_t i = 0; i < TOTAL_TEST_SIZE / TEST_BUF_SIZE; i++) { + res = f_write(&file, g_test_buffer, TEST_BUF_SIZE, &bw); + if (res != FR_OK || bw != TEST_BUF_SIZE) { + sprintf(uart_buf, "Write Fail at block %lu, Res: %d\r\n", i, res); + UART3_Print(uart_buf); + f_close(&file); + return; + } + } + + f_close(&file); // 必须先关闭文件,确保 FAT 目录项更新 + endTime = HAL_GetTick(); + duration = (endTime - startTime > 0) ? (endTime - startTime) : 1; // 防止除以0 + + speed = ((float)TOTAL_TEST_SIZE / 1024.0 / 1024.0) / ((float)duration / 1000.0); + sprintf(uart_buf, "WRITE Result: %.2f MB/s (%lu ms)\r\n", speed, duration); + UART3_Print(uart_buf); + + // --- READ TEST --- + HAL_Delay(100); // 稍微等待文件系统稳定 + res = f_open(&file, TEST_FILE_NAME, FA_READ); + if (res != FR_OK) { + sprintf(uart_buf, "Open Fail (Read). Error: %d\r\n", res); + UART3_Print(uart_buf); + return; + } + + UART3_Print("Reading 16MB...\r\n"); + startTime = HAL_GetTick(); + for (uint32_t i = 0; i < TOTAL_TEST_SIZE / TEST_BUF_SIZE; i++) { + res = f_read(&file, g_test_buffer, TEST_BUF_SIZE, &br); + if (res != FR_OK || br != TEST_BUF_SIZE) { + sprintf(uart_buf, "Read Fail at block %lu, Res: %d\r\n", i, res); + UART3_Print(uart_buf); + break; + } + } + endTime = HAL_GetTick(); + duration = (endTime - startTime > 0) ? (endTime - startTime) : 1; + + speed = ((float)TOTAL_TEST_SIZE / 1024.0 / 1024.0) / ((float)duration / 1000.0); + sprintf(uart_buf, "READ Result: %.2f MB/s (%lu ms)\r\n", speed, duration); + UART3_Print(uart_buf); + + f_close(&file); + UART3_Print("=== Test Finished ===\r\n"); +} + +void SDNAND_ForceFormat_and_Mount(void) { + FATFS fs; + FRESULT res; + BYTE work[_MAX_SS]; + char msg[128]; + + UART3_Print("\r\n--- Initializing SD NAND for Optimization ---\r\n"); + + // 1. 先尝试挂载 (立即挂载模式) + res = f_mount(&fs, "", 1); + if (res != FR_OK && res != FR_NO_FILESYSTEM) { + sprintf(msg, "Mount failed before format: %d\r\n", res); + UART3_Print(msg); + return; + } + + // 2. 执行格式化 (强制 512 簇) + UART3_Print("Formatting... this may take a few seconds.\r\n"); + res = f_mkfs("", FM_FAT32, 32768, work, sizeof(work)); + if (res != FR_OK) { + sprintf(msg, "Format failed: %d\r\n", res); + UART3_Print(msg); + return; + } + UART3_Print("Format completed successfully.\r\n"); + + // 3. 卸载以清除旧缓存 + f_mount(NULL, "", 0); + + // 4. 重新挂载 + res = f_mount(&fs, "", 1); + if (res == FR_OK) { + UART3_Print("SD NAND Remounted with 4KB cluster size.\r\n"); + } else { + sprintf(msg, "Final mount failed: %d\r\n", res); + UART3_Print(msg); + } +} + +#endif diff --git a/User/system_monitor.c b/User/system_monitor.c index cfbf79c..c441603 100644 --- a/User/system_monitor.c +++ b/User/system_monitor.c @@ -1,5 +1,8 @@ #include "system_monitor.h" +#include "fatfs.h" +#include "ff.h" #include +#include // 静态变量 static SystemMonitorStats_t g_system_stats = {0}; @@ -86,3 +89,81 @@ void SystemMonitor_ReportSDFileCreated(void) { g_system_stats.sd_file_count++; } + +/** + * @brief 报告数据丢弃(缓冲区满时未存储) + * @param None + * @retval None + */ +void SystemMonitor_ReportDataDropped(void) +{ + g_system_stats.sd_data_dropped_count++; +} + +/** + * @brief 保存监控状态到文件 + * @param None + * @retval HAL_StatusTypeDef + */ +HAL_StatusTypeDef SystemMonitor_SaveStatus(void) +{ + FIL file; + FRESULT res; + UINT bytes_written; + char buffer[512]; + + // 创建或覆盖MONITOR.TXT文件 + res = f_open(&file, MONITOR_STATUS_FILE, FA_CREATE_ALWAYS | FA_WRITE); + if (res != FR_OK) { + return HAL_ERROR; + } + + // 格式化监控数据为文本 + int len = snprintf(buffer, sizeof(buffer), + "=== System Monitor Status ===\r\n" + "Total Samples: %lu\r\n" + "Data Overflow: %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.data_overflow_count, + g_system_stats.sd_write_count, + g_system_stats.sd_write_error_count, + g_system_stats.sd_buffer_full_count, + g_system_stats.sd_total_bytes_written, + g_system_stats.sd_file_count, + g_system_stats.sd_data_dropped_count + ); + + // 写入监控数据 + 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 HAL_StatusTypeDef + * @note 当前实现仅用于记录,不恢复状态(避免累积错误) + */ +HAL_StatusTypeDef SystemMonitor_LoadStatus(void) +{ + // 当前实现:不从文件恢复状态 + // 每次上电重新开始统计,避免累积错误 + // MONITOR.TXT仅用于记录上次运行的状态 + return HAL_OK; +} diff --git a/User/system_monitor.h b/User/system_monitor.h index 63bbccb..7e8269f 100644 --- a/User/system_monitor.h +++ b/User/system_monitor.h @@ -4,6 +4,9 @@ #include "main.h" #include +// 监控状态文件配置 +#define MONITOR_STATUS_FILE "0:/MONITOR.TXT" // 监控状态存储文件 + // 简化的系统监控统计信息 typedef struct { uint32_t total_samples; // 总采样样点数 @@ -15,6 +18,7 @@ typedef struct { uint32_t sd_buffer_full_count; // 缓冲区满次数 uint32_t sd_total_bytes_written; // SD卡总写入字节数 uint32_t sd_file_count; // 创建的文件数量 + uint32_t sd_data_dropped_count; // 未存储的数据数量(缓冲区满时丢弃) } SystemMonitorStats_t; // 函数声明 @@ -28,5 +32,10 @@ void SystemMonitor_ReportSDWrite(uint32_t bytes_written); void SystemMonitor_ReportSDWriteError(void); void SystemMonitor_ReportSDBufferFull(void); void SystemMonitor_ReportSDFileCreated(void); +void SystemMonitor_ReportDataDropped(void); + +// 监控状态持久化函数 +HAL_StatusTypeDef SystemMonitor_SaveStatus(void); +HAL_StatusTypeDef SystemMonitor_LoadStatus(void); #endif // SYSTEM_MONITOR_H