diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8f98b62 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,49 @@ +# STM_ATEM_F405 项目说明 + +## 项目概述 +STM32F405 航空电磁接收机固件,基于 STM32CubeIDE + HAL 库开发。 + +**核心功能:** ADC 采集(LTC2508)→ 数据校正 → SD 卡存储 / UART 输出 + +## 目录结构 +- `Core/` — CubeMX 生成代码(main.c、外设初始化),**不要手动修改生成区域** +- `User/` — 业务逻辑(驱动、数据包、存储、GPS、配置管理等) +- `Drivers/` — HAL 驱动库(不修改) +- `Middlewares/` — FatFs、USB(不修改) + +## 关键模块 +| 文件 | 职责 | +|------|------| +| `ltc2508_driver` | SPI ADC 采集驱动 | +| `data_packet` | 数据包格式定义与封包 | +| `data_storage` | FatFs SD 卡文件写入 | +| `correction` | ADC 校正参数应用 | +| `app_config` | 编译期配置(UART 输出、SD 存储开关)| +| `config_manager` | 配置运行时访问层,默认值来自 `app_config.h` | +| `gps_driver` | GPS NMEA 解析(UART)| +| `rs485_driver` | RS485 通信 | +| `optic_mag_driver` | 光学磁传感器 | +| `system_monitor` | 系统状态统计与诊断 | + +## 通信接口 +| 接口 | 用途 | 波特率 | 备注 | +|------|------|--------|------| +| USART1 (PA9/PA10) | RS485 数据输出 | 2 000 000 | TX DMA;DE/RE 控制脚 PC7 | +| USART2 (PA3) | 光泵磁力仪接收 | 115 200 | 仅 RX,DMA 循环;PA2 已改为普通 GPIO | +| USART3 (PB10/PB11) | GPS NMEA 接收 | 115 200 | TX/RX,单字节中断接收 | +| SPI1/2/3 | LTC2508 ADC 采集 | — | 三路 SPI 同时驱动三片 ADC | +| SDIO | SD 卡(FatFs) | — | 与 USB MSC 共用,采样期间独占 | +| USB | MSC 大容量存储 | — | 供 PC 直接访问 SD 卡 | + +## 开发规范 +- 每次修改代码后,判断是否影响模块职责、接口用途、配置项或注意事项,若有则同步更新 CLAUDE.md 对应部分 + +- 业务代码只放 `User/` 目录;CubeMX 生成区域(`USER CODE BEGIN/END`)内写用户代码 +- 函数命名:`模块名_功能()` 如 `DataStorage_StartRecording()` +- 全局变量加 `g_` 前缀,静态变量加 `s_` 前缀 +- 不添加无意义注释;注释用中文说明 **为什么**,不说明是什么 + +## 注意事项 +- 修改 `.ioc` 文件后重新生成代码会覆盖 `Core/` 文件,需手动恢复用户代码 +- USB MSC 与 FatFs 共享 SD 卡,采样期间需卸载再挂载 +- 配置在 `User/app_config.h` 中以 `#define` 编译期确定;若需恢复 CONFIG.TXT 运行时加载,在 `main.c` 挂载 SD 后重新调用 `Config_Load()` 即可 diff --git a/Core/Inc/main.h b/Core/Inc/main.h index b084dec..e51e0df 100644 --- a/Core/Inc/main.h +++ b/Core/Inc/main.h @@ -60,8 +60,6 @@ void Error_Handler(void); #define ADC_DRY_Pin GPIO_PIN_1 #define ADC_DRY_GPIO_Port GPIOA #define ADC_DRY_EXTI_IRQn EXTI1_IRQn -#define ADC_SYNC_Pin GPIO_PIN_2 -#define ADC_SYNC_GPIO_Port GPIOA #define RS485_CTL_Pin GPIO_PIN_7 #define RS485_CTL_GPIO_Port GPIOC diff --git a/Core/Inc/stm32f4xx_it.h b/Core/Inc/stm32f4xx_it.h index 107969d..a3de15d 100644 --- a/Core/Inc/stm32f4xx_it.h +++ b/Core/Inc/stm32f4xx_it.h @@ -60,6 +60,7 @@ void DMA1_Stream0_IRQHandler(void); void DMA1_Stream3_IRQHandler(void); void TIM2_IRQHandler(void); void USART1_IRQHandler(void); +void USART2_IRQHandler(void); void USART3_IRQHandler(void); void SDIO_IRQHandler(void); void DMA2_Stream0_IRQHandler(void); diff --git a/Core/Inc/usart.h b/Core/Inc/usart.h index dd17ca7..3741c11 100644 --- a/Core/Inc/usart.h +++ b/Core/Inc/usart.h @@ -34,6 +34,8 @@ extern "C" { extern UART_HandleTypeDef huart1; +extern UART_HandleTypeDef huart2; + extern UART_HandleTypeDef huart3; /* USER CODE BEGIN Private defines */ @@ -41,6 +43,7 @@ extern UART_HandleTypeDef huart3; /* USER CODE END Private defines */ void MX_USART1_UART_Init(void); +void MX_USART2_UART_Init(void); void MX_USART3_UART_Init(void); /* USER CODE BEGIN Prototypes */ diff --git a/Core/Src/gpio.c b/Core/Src/gpio.c index 30a01fd..c8604a1 100644 --- a/Core/Src/gpio.c +++ b/Core/Src/gpio.c @@ -51,9 +51,6 @@ void MX_GPIO_Init(void) __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); - /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(ADC_SYNC_GPIO_Port, ADC_SYNC_Pin, GPIO_PIN_RESET); - /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(RS485_CTL_GPIO_Port, RS485_CTL_Pin, GPIO_PIN_RESET); @@ -69,13 +66,6 @@ void MX_GPIO_Init(void) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(ADC_DRY_GPIO_Port, &GPIO_InitStruct); - /*Configure GPIO pin : ADC_SYNC_Pin */ - GPIO_InitStruct.Pin = ADC_SYNC_Pin; - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - HAL_GPIO_Init(ADC_SYNC_GPIO_Port, &GPIO_InitStruct); - /*Configure GPIO pin : RS485_CTL_Pin */ GPIO_InitStruct.Pin = RS485_CTL_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; diff --git a/Core/Src/main.c b/Core/Src/main.c index 16b6ac0..798cfd5 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -37,6 +37,7 @@ #include "system_monitor.h" #include "config_manager.h" #include "gps_driver.h" +#include "optic_mag_driver.h" #include #include /* USER CODE END Includes */ @@ -48,15 +49,10 @@ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ -// 监控功能宏开关(统一控制串口输出和文件存储) -#define ENABLE_SYSTEM_MONITOR 1 // 系统监控开关 -#define DEBUG_OUTPUT_INTERVAL_MS 30000 // 调试输出间隔(毫秒) -#define MONITOR_SAVE_INTERVAL_MS 30000 // 监控状态保存间隔(毫秒) - 30秒 - -// 数据输出模式选择(运行时配置,从SD卡加载) -// 注意:DATA_OUTPUT_MODE_UART 和 DATA_OUTPUT_MODE_STORAGE 已改为运行时配置 -// 使用 Config_IsUartOutputEnabled() 和 Config_IsStorageEnabled() 来检查状态 -// 配置文件:0:/CONFIG.TXT +// 以下宏从 app_config.h 引入,此处仅做别名映射,保持内部代码不变 +#define ENABLE_SYSTEM_MONITOR CFG_ENABLE_SYSTEM_MONITOR +#define DEBUG_OUTPUT_INTERVAL_MS CFG_DEBUG_OUTPUT_INTERVAL_MS +#define MONITOR_SAVE_INTERVAL_MS CFG_MONITOR_SAVE_INTERVAL_MS /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ @@ -73,14 +69,17 @@ extern SPI_HandleTypeDef hspi2; extern SPI_HandleTypeDef hspi3; extern TIM_HandleTypeDef htim2; extern UART_HandleTypeDef huart1; +extern UART_HandleTypeDef huart2; extern UART_HandleTypeDef huart3; // 校正参数 CorrectionParams_t g_correction_params; -// 数据包 -DataPacket_t g_data_packet; -CorrectedDataPacket_t g_corrected_packet; -CorrectedDataPacketWithGPS_t g_corrected_packet_with_gps; // 带GPS信息的数据包 +// 数据包(标准包 / 扩展包各一份,ProcessAdcData按需选用) +DataPacket_t g_data_packet; +DataPacketWithOptic_t g_data_packet_with_optic; +CorrectedDataPacket_t g_corrected_packet; +CorrectedDataPacketWithGPS_t g_corrected_packet_with_gps; +CorrectedDataPacketWithOptic_t g_corrected_packet_with_optic; // 数据存储句柄 DataStorageHandle_t g_data_storage; // 系统状态 @@ -151,110 +150,99 @@ static void StopRecording(void) */ static void ProcessAdcData(void) { - // 检查ADC数据是否准备就绪 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(CorrectedDataPacketWithGPS_t); // 使用带GPS的数据包大小 - can_store_data = DataStorage_IsBufferAvailable(&g_data_storage, max_packet_size); - } + if (LTC2508_GetReadyBuffer(&ready_buffer) != LTC2508_OK || ready_buffer == NULL) return; + + // 快照光泵数据:USART2中断优先级(10) < TIM2优先级(3),此处不会被抢占,无需关中断 + uint8_t optic_fresh = g_optic_mag_fresh; + uint32_t optic_val = g_optic_mag_value; + if (optic_fresh) g_optic_mag_fresh = 0; + + // 检查存储缓冲区是否可用(取最大包长为基准) + uint8_t can_store_data = 0; + if (g_recording_enabled) { + can_store_data = DataStorage_IsBufferAvailable(&g_data_storage, + sizeof(CorrectedDataPacketWithOptic_t)); + } #if ENABLE_SYSTEM_MONITOR - SystemMonitor_IncrementSampleCount(); + SystemMonitor_IncrementSampleCount(); #endif - // 1. 从双缓冲区获取数据并合并 (高位16位在前) - int32_t raw_adc[NUM_LTC2508]; - for (uint8_t i = 0; i < NUM_LTC2508; i++) { - raw_adc[i] = (int32_t)(((uint32_t)ready_buffer->data[i][0] << 16) | ready_buffer->data[i][1]); + // 1. 合并三路ADC原始数据(高16位在前) + int32_t raw_adc[NUM_LTC2508]; + for (uint8_t i = 0; i < NUM_LTC2508; i++) { + raw_adc[i] = (int32_t)(((uint32_t)ready_buffer->data[i][0] << 16) | ready_buffer->data[i][1]); + } + + // 2. 获取GPS数据(时间戳始终使用,位置字段受ENABLE_GPS_POSITION控制) + GPS_Data_t current_gps_data; + uint8_t gps_valid = GPS_GetData(¤t_gps_data); + uint32_t gps_time = gps_valid ? (current_gps_data.time.hour * 10000u + + current_gps_data.time.minute * 100u + + current_gps_data.time.second) : 0u; + float lat = (float)current_gps_data.position.latitude; + float lon = (float)current_gps_data.position.longitude; + float alt = (float)current_gps_data.position.altitude; + + // 3. 决定发送哪种包(校正×是否有光泵 = 4种组合) + const uint8_t *send_ptr; + uint16_t send_size; + + CorrectionResult_t correction_result; + if (g_correction_params.params_valid && + Apply_Correction(raw_adc[0], raw_adc[1], raw_adc[2], + &correction_result, &g_correction_params) == HAL_OK) { + + if (optic_fresh) { + PackCorrectedDataWithOptic(&g_corrected_packet_with_optic, + correction_result.corrected_x, correction_result.corrected_y, + correction_result.corrected_z, gps_time, lat, lon, alt, optic_val); + send_ptr = (const uint8_t *)&g_corrected_packet_with_optic; + send_size = sizeof(CorrectedDataPacketWithOptic_t); + } else { + PackCorrectedDataWithGPS(&g_corrected_packet_with_gps, + correction_result.corrected_x, correction_result.corrected_y, + correction_result.corrected_z, gps_time, lat, lon, alt); + send_ptr = (const uint8_t *)&g_corrected_packet_with_gps; + send_size = sizeof(CorrectedDataPacketWithGPS_t); } - // 2. 获取当前GPS数据 - GPS_Data_t current_gps_data; - uint8_t gps_valid = GPS_GetData(¤t_gps_data); + } else { - // 3. 应用校正算法 - CorrectionResult_t correction_result; - uint8_t correction_applied = 0; - - if (g_correction_params.params_valid && - Apply_Correction(raw_adc[0], raw_adc[1], raw_adc[2], - &correction_result, &g_correction_params) == HAL_OK) { - - // 4a. 打包校正后的数据(带GPS关键信息:仅经纬度) - // float lat = gps_valid ? (float)current_gps_data.position.latitude : 0.0f; - // float lon = gps_valid ? (float)current_gps_data.position.longitude : 0.0f; - // float alt = gps_valid ? (float)current_gps_data.position.altitude : 0.0f; - float lat = (float)current_gps_data.position.latitude; - float lon = (float)current_gps_data.position.longitude; - float alt = (float)current_gps_data.position.altitude; - uint32_t gps_time = gps_valid ? (current_gps_data.time.hour * 10000 + - current_gps_data.time.minute * 100 + - current_gps_data.time.second) : 0; - - PackCorrectedDataWithGPS(&g_corrected_packet_with_gps, - correction_result.corrected_x, - correction_result.corrected_y, - correction_result.corrected_z, - gps_time, - lat, - lon, - alt); - - correction_applied = 1; - - // 发送校正后的数据包到串口(运行时配置) - if (Config_IsUartOutputEnabled()) { - RS485_SendData((uint8_t*)&g_corrected_packet_with_gps, sizeof(CorrectedDataPacketWithGPS_t)); - } - - } else { - - // 4b. 校正失败或未启用,使用原始数据 - // float lat = gps_valid ? (float)current_gps_data.position.latitude : 0.0f; - // float lon = gps_valid ? (float)current_gps_data.position.longitude : 0.0f; - // float alt = gps_valid ? (float)current_gps_data.position.altitude : 0.0f; - float lat = (float)current_gps_data.position.latitude; - float lon = (float)current_gps_data.position.longitude; - float alt = (float)current_gps_data.position.altitude; - uint32_t gps_time = gps_valid ? (current_gps_data.time.hour * 10000 + - current_gps_data.time.minute * 100 + - current_gps_data.time.second) : 0; - - PackData(&g_data_packet, raw_adc[0], raw_adc[1], raw_adc[2], gps_time, lat, lon, alt); - - // 发送原始数据包到串口(运行时配置) - if (Config_IsUartOutputEnabled()) { - RS485_SendData((uint8_t*)&g_data_packet, sizeof(DataPacket_t)); - } + if (optic_fresh) { + PackDataWithOptic(&g_data_packet_with_optic, + raw_adc[0], raw_adc[1], raw_adc[2], + gps_time, lat, lon, alt, optic_val); + send_ptr = (const uint8_t *)&g_data_packet_with_optic; + send_size = sizeof(DataPacketWithOptic_t); + } else { + PackData(&g_data_packet, + raw_adc[0], raw_adc[1], raw_adc[2], + gps_time, lat, lon, alt); + send_ptr = (const uint8_t *)&g_data_packet; + send_size = sizeof(DataPacket_t); } + } - // 6. 存储数据到SD卡 (如果启用记录且缓冲区可用,运行时配置) - if (Config_IsStorageEnabled() && g_recording_enabled) { - if (can_store_data) { - if (correction_applied) { - // 存储校正后的数据(带GPS信息) - DataStorage_WriteCorrectedData(&g_data_storage, (CorrectedDataPacket_t*)&g_corrected_packet_with_gps); - } else { - // 存储原始数据 - DataStorage_WriteData(&g_data_storage, &g_data_packet); - } - } else { - // 缓冲区满,数据被丢弃 + // 4. 发送到RS485串口 + if (Config_IsUartOutputEnabled()) { + RS485_SendData((uint8_t *)send_ptr, send_size); + } + + // 5. 存储到SD卡(方案Y:直接写原始字节流,与串口格式一致) + if (Config_IsStorageEnabled() && g_recording_enabled) { + if (can_store_data) { + DataStorage_WriteRawBytes(&g_data_storage, send_ptr, send_size); + } else { #if ENABLE_SYSTEM_MONITOR SystemMonitor_ReportDataDropped(); #endif - } } - - // 7. 释放已处理的缓冲区 - LTC2508_ReleaseBuffer(LTC2508_GetCurrentReadBuffer()); - } else { - } + + // 6. 释放已处理的缓冲区 + LTC2508_ReleaseBuffer(LTC2508_GetCurrentReadBuffer()); } /** @@ -536,15 +524,19 @@ int main(void) MX_USART3_UART_Init(); MX_TIM2_Init(); MX_TIM1_Init(); + MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ // 初始化系统监控 #if ENABLE_SYSTEM_MONITOR SystemMonitor_Init(); #endif - - // 初始化调试输出 + + // 初始化调试输出(含GPS) DebugOutput_Init(); + + // 初始化光泵磁力仪接收(USART2 RX,115200) + OpticMag_Init(&huart2); // 初始化配置管理器(设置默认值) Config_Init(); @@ -562,13 +554,9 @@ int main(void) if (!g_usb_connected) { // USB未连接,挂载文件系统用于数据采集 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 CFG_LOAD_FROM_FILE + Config_Load(); +#endif // 初始化数据存储 if (DataStorage_Init(&g_data_storage) == HAL_OK) { // 开始数据记录(如果存储功能已启用) @@ -812,8 +800,21 @@ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart3) { - // GPS数据接收回调 GPS_UART_RxCpltCallback(huart); + } else if (huart == &huart2) { + OpticMag_UART_RxCpltCallback(huart); + } +} + +/** + * @brief UART错误回调函数 + * @param huart: UART句柄指针 + * @retval None + */ +void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) +{ + if (huart == &huart2) { + OpticMag_UART_ErrorCallback(huart); } } diff --git a/Core/Src/stm32f4xx_it.c b/Core/Src/stm32f4xx_it.c index 2ff8cd9..7d28cb2 100644 --- a/Core/Src/stm32f4xx_it.c +++ b/Core/Src/stm32f4xx_it.c @@ -25,6 +25,7 @@ #include "ltc2508_driver.h" #include "rs485_driver.h" #include "gps_driver.h" +#include "optic_mag_driver.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -69,12 +70,12 @@ extern DMA_HandleTypeDef hdma_spi3_rx; extern TIM_HandleTypeDef htim2; extern DMA_HandleTypeDef hdma_usart1_tx; extern UART_HandleTypeDef huart1; +extern UART_HandleTypeDef huart2; extern UART_HandleTypeDef huart3; /* USER CODE BEGIN EV */ extern SPI_HandleTypeDef hspi1; extern SPI_HandleTypeDef hspi2; extern SPI_HandleTypeDef hspi3; -extern UART_HandleTypeDef huart1; /* USER CODE END EV */ /******************************************************************************/ @@ -285,6 +286,20 @@ void USART1_IRQHandler(void) /* USER CODE END USART1_IRQn 1 */ } +/** + * @brief This function handles USART2 global interrupt. + */ +void USART2_IRQHandler(void) +{ + /* USER CODE BEGIN USART2_IRQn 0 */ + + /* USER CODE END USART2_IRQn 0 */ + HAL_UART_IRQHandler(&huart2); + /* USER CODE BEGIN USART2_IRQn 1 */ + + /* USER CODE END USART2_IRQn 1 */ +} + /** * @brief This function handles USART3 global interrupt. */ diff --git a/Core/Src/usart.c b/Core/Src/usart.c index 4a4ca7e..2050ac9 100644 --- a/Core/Src/usart.c +++ b/Core/Src/usart.c @@ -25,6 +25,7 @@ /* USER CODE END 0 */ UART_HandleTypeDef huart1; +UART_HandleTypeDef huart2; UART_HandleTypeDef huart3; DMA_HandleTypeDef hdma_usart1_tx; @@ -56,6 +57,35 @@ void MX_USART1_UART_Init(void) /* USER CODE END USART1_Init 2 */ +} +/* USART2 init function */ + +void MX_USART2_UART_Init(void) +{ + + /* USER CODE BEGIN USART2_Init 0 */ + + /* USER CODE END USART2_Init 0 */ + + /* USER CODE BEGIN USART2_Init 1 */ + + /* USER CODE END USART2_Init 1 */ + huart2.Instance = USART2; + huart2.Init.BaudRate = 115200; + huart2.Init.WordLength = UART_WORDLENGTH_8B; + huart2.Init.StopBits = UART_STOPBITS_1; + huart2.Init.Parity = UART_PARITY_NONE; + huart2.Init.Mode = UART_MODE_RX; + huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; + huart2.Init.OverSampling = UART_OVERSAMPLING_16; + if (HAL_UART_Init(&huart2) != HAL_OK) + { + Error_Handler(); + } + /* USER CODE BEGIN USART2_Init 2 */ + + /* USER CODE END USART2_Init 2 */ + } /* USART3 init function */ @@ -137,6 +167,40 @@ void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle) /* USER CODE END USART1_MspInit 1 */ } + else if(uartHandle->Instance==USART2) + { + /* USER CODE BEGIN USART2_MspInit 0 */ + + /* USER CODE END USART2_MspInit 0 */ + /* USART2 clock enable */ + __HAL_RCC_USART2_CLK_ENABLE(); + + __HAL_RCC_GPIOA_CLK_ENABLE(); + /**USART2 GPIO Configuration + PA2 ------> USART2_TX + PA3 ------> USART2_RX + */ + GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF7_USART2; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + /* USART2 interrupt Init */ + HAL_NVIC_SetPriority(USART2_IRQn, 7, 0); + HAL_NVIC_EnableIRQ(USART2_IRQn); + /* USER CODE BEGIN USART2_MspInit 1 */ + /* 覆盖PA2为普通GPIO输出:CubeMX将其初始化为USART2_TX(AF7), + 此处重新配置以供其他功能使用,PA3(RX)保持AF7不变 */ + GPIO_InitStruct.Pin = GPIO_PIN_2; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = 0; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + /* USER CODE END USART2_MspInit 1 */ + } else if(uartHandle->Instance==USART3) { /* USER CODE BEGIN USART3_MspInit 0 */ @@ -192,6 +256,26 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle) /* USER CODE END USART1_MspDeInit 1 */ } + else if(uartHandle->Instance==USART2) + { + /* USER CODE BEGIN USART2_MspDeInit 0 */ + + /* USER CODE END USART2_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_USART2_CLK_DISABLE(); + + /**USART2 GPIO Configuration + PA2 ------> USART2_TX + PA3 ------> USART2_RX + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3); + + /* USART2 interrupt Deinit */ + HAL_NVIC_DisableIRQ(USART2_IRQn); + /* USER CODE BEGIN USART2_MspDeInit 1 */ + + /* USER CODE END USART2_MspDeInit 1 */ + } else if(uartHandle->Instance==USART3) { /* USER CODE BEGIN USART3_MspDeInit 0 */ diff --git a/Doc/注意事项.jpg b/Doc/注意事项.jpg new file mode 100644 index 0000000..ad31eed Binary files /dev/null and b/Doc/注意事项.jpg differ diff --git a/STM_ATEM_F405.ioc b/STM_ATEM_F405.ioc index 361e52d..6f0e180 100644 --- a/STM_ATEM_F405.ioc +++ b/STM_ATEM_F405.ioc @@ -111,9 +111,10 @@ Mcu.IP0=DMA Mcu.IP1=FATFS Mcu.IP10=TIM2 Mcu.IP11=USART1 -Mcu.IP12=USART3 -Mcu.IP13=USB_DEVICE -Mcu.IP14=USB_OTG_FS +Mcu.IP12=USART2 +Mcu.IP13=USART3 +Mcu.IP14=USB_DEVICE +Mcu.IP15=USB_OTG_FS Mcu.IP2=NVIC Mcu.IP3=RCC Mcu.IP4=SDIO @@ -122,42 +123,43 @@ Mcu.IP6=SPI2 Mcu.IP7=SPI3 Mcu.IP8=SYS Mcu.IP9=TIM1 -Mcu.IPNb=15 +Mcu.IPNb=16 Mcu.Name=STM32F405RGTx Mcu.Package=LQFP64 Mcu.Pin0=PH0-OSC_IN Mcu.Pin1=PH1-OSC_OUT -Mcu.Pin10=PB13 -Mcu.Pin11=PB15 -Mcu.Pin12=PC7 -Mcu.Pin13=PC8 -Mcu.Pin14=PC9 -Mcu.Pin15=PA8 -Mcu.Pin16=PA9 -Mcu.Pin17=PA10 -Mcu.Pin18=PA11 -Mcu.Pin19=PA12 +Mcu.Pin10=PB11 +Mcu.Pin11=PB13 +Mcu.Pin12=PB15 +Mcu.Pin13=PC7 +Mcu.Pin14=PC8 +Mcu.Pin15=PC9 +Mcu.Pin16=PA8 +Mcu.Pin17=PA9 +Mcu.Pin18=PA10 +Mcu.Pin19=PA11 Mcu.Pin2=PC3 -Mcu.Pin20=PA13 -Mcu.Pin21=PA14 -Mcu.Pin22=PC10 -Mcu.Pin23=PC11 -Mcu.Pin24=PC12 -Mcu.Pin25=PD2 -Mcu.Pin26=PB3 -Mcu.Pin27=PB5 -Mcu.Pin28=VP_FATFS_VS_SDIO -Mcu.Pin29=VP_SYS_VS_Systick +Mcu.Pin20=PA12 +Mcu.Pin21=PA13 +Mcu.Pin22=PA14 +Mcu.Pin23=PC10 +Mcu.Pin24=PC11 +Mcu.Pin25=PC12 +Mcu.Pin26=PD2 +Mcu.Pin27=PB3 +Mcu.Pin28=PB5 +Mcu.Pin29=VP_FATFS_VS_SDIO Mcu.Pin3=PA1 -Mcu.Pin30=VP_TIM2_VS_ClockSourceINT -Mcu.Pin31=VP_USB_DEVICE_VS_USB_DEVICE_MSC_FS +Mcu.Pin30=VP_SYS_VS_Systick +Mcu.Pin31=VP_TIM2_VS_ClockSourceINT +Mcu.Pin32=VP_USB_DEVICE_VS_USB_DEVICE_MSC_FS Mcu.Pin4=PA2 -Mcu.Pin5=PA5 -Mcu.Pin6=PA6 -Mcu.Pin7=PA7 -Mcu.Pin8=PB10 -Mcu.Pin9=PB11 -Mcu.PinsNb=32 +Mcu.Pin5=PA3 +Mcu.Pin6=PA5 +Mcu.Pin7=PA6 +Mcu.Pin8=PA7 +Mcu.Pin9=PB10 +Mcu.PinsNb=33 Mcu.ThirdPartyNb=0 Mcu.UserConstants= Mcu.UserName=STM32F405RGTx @@ -185,6 +187,7 @@ 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\:11\:0\:true\:false\:true\:true\:true\:true +NVIC.USART2_IRQn=true\:7\: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 PA1.GPIOParameters=GPIO_Label @@ -201,10 +204,10 @@ PA13.Mode=Serial_Wire PA13.Signal=SYS_JTMS-SWDIO PA14.Mode=Serial_Wire PA14.Signal=SYS_JTCK-SWCLK -PA2.GPIOParameters=GPIO_Label -PA2.GPIO_Label=ADC_SYNC -PA2.Locked=true -PA2.Signal=GPIO_Output +PA2.Mode=Asynchronous +PA2.Signal=USART2_TX +PA3.Mode=Asynchronous +PA3.Signal=USART2_RX PA5.Mode=Full_Duplex_Master PA5.Signal=SPI1_SCK PA6.Mode=Full_Duplex_Master @@ -299,7 +302,7 @@ ProjectManager.ToolChainLocation= ProjectManager.UAScriptAfterPath= ProjectManager.UAScriptBeforePath= ProjectManager.UnderRoot=true -ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_SDIO_SD_Init-SDIO-false-HAL-true,5-MX_SPI1_Init-SPI1-false-HAL-true,6-MX_SPI2_Init-SPI2-false-HAL-true,7-MX_SPI3_Init-SPI3-false-HAL-true,8-MX_USART1_UART_Init-USART1-false-HAL-true,9-MX_FATFS_Init-FATFS-false-HAL-false,10-MX_USB_DEVICE_Init-USB_DEVICE-false-HAL-false,11-MX_USART3_UART_Init-USART3-false-HAL-true,12-MX_TIM2_Init-TIM2-false-HAL-true +ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_SDIO_SD_Init-SDIO-false-HAL-true,5-MX_SPI1_Init-SPI1-false-HAL-true,6-MX_SPI2_Init-SPI2-false-HAL-true,7-MX_SPI3_Init-SPI3-false-HAL-true,8-MX_USART1_UART_Init-USART1-false-HAL-true,9-MX_FATFS_Init-FATFS-false-HAL-false,10-MX_USB_DEVICE_Init-USB_DEVICE-false-HAL-false,11-MX_USART3_UART_Init-USART3-false-HAL-true,12-MX_TIM2_Init-TIM2-false-HAL-true,13-MX_TIM1_Init-TIM1-false-HAL-true,14-MX_USART2_UART_Init-USART2-false-HAL-true RCC.48MHZClocksFreq_Value=48000000 RCC.AHBFreq_Value=168000000 RCC.APB1CLKDivider=RCC_HCLK_DIV4 @@ -371,6 +374,9 @@ TIM2.Prescaler=83 USART1.BaudRate=2000000 USART1.IPParameters=VirtualMode,BaudRate USART1.VirtualMode=VM_ASYNC +USART2.IPParameters=VirtualMode,Mode +USART2.Mode=MODE_RX +USART2.VirtualMode=VM_ASYNC USART3.BaudRate=115200 USART3.IPParameters=VirtualMode,BaudRate USART3.VirtualMode=VM_ASYNC diff --git a/User/app_config.h b/User/app_config.h new file mode 100644 index 0000000..3d5b502 --- /dev/null +++ b/User/app_config.h @@ -0,0 +1,84 @@ +#ifndef APP_CONFIG_H +#define APP_CONFIG_H + +// ============================================================ +// 系统编译期配置总入口 +// 所有可调参数集中在此文件,修改后重新编译生效。 +// 若需恢复 SD 卡运行时加载,将 CFG_LOAD_FROM_FILE 置 1, +// 其余编译期值将作为加载失败时的 fallback 默认值。 +// ============================================================ + + +// ------------------------------------------------------------ +// 配置加载方式 +// ------------------------------------------------------------ + +// 是否在启动时从 SD 卡 CONFIG.TXT 加载配置 +// 1 = 运行时加载(文件内容覆盖以下编译期值) +// 0 = 仅使用以下编译期值,不读取文件(安全,可重复) +#define CFG_LOAD_FROM_FILE 0 + + +// ------------------------------------------------------------ +// 数据输出控制 +// ------------------------------------------------------------ + +// RS485 串口输出开关 +// 1 = 每帧 ADC 数据通过 USART1 以 2Mbps 发送 +// 0 = 禁用串口输出,降低功耗和 CPU 占用 +#define CFG_UART_OUTPUT_ENABLED 1 + +// SD 卡数据存储开关 +// 1 = 每帧数据写入 SD 卡 DATA/SESSION_xxx/ 目录下的文件 +// 0 = 禁用存储,SD 卡仅用于 USB MSC 访问 +#define CFG_STORAGE_ENABLED 1 + + +// ------------------------------------------------------------ +// 功能模块开关 +// ------------------------------------------------------------ + +// 系统监控统计模块 +// 1 = 启用:统计采样次数、丢帧数、SD 写入量、串口发送量, +// 并定期输出到调试串口 / 写入 LOG.TXT +// 0 = 禁用:相关代码被条件编译剔除,节省 Flash 和运行时开销 +#define CFG_ENABLE_SYSTEM_MONITOR 0 + +// 数据包中包含 GPS 经纬度和海拔字段 +// 1 = 每帧包含 latitude(float) + longitude(float) + altitude(float),包体增大 12 字节 +// 0 = 仅保留 GPS 时间戳(gps_time),包体更小,适合不需要位置信息的场景 +#define CFG_ENABLE_GPS_POSITION 0 + + +// ------------------------------------------------------------ +// 调试与监控定时参数 +// ------------------------------------------------------------ + +// 调试信息通过 RS485 输出的时间间隔(毫秒) +// 控制 DebugOutput_PrintSystemStats() 的调用频率 +// 建议范围:5000(5秒)~ 60000(1分钟) +#define CFG_DEBUG_OUTPUT_INTERVAL_MS 30000 + +// 监控统计数据写入 SD 卡 LOG.TXT 的时间间隔(毫秒) +// 间隔越短日志越详细,但 SD 卡写入操作越频繁 +// 建议范围:10000(10秒)~ 300000(5分钟) +#define CFG_MONITOR_SAVE_INTERVAL_MS 30000 + + +// ------------------------------------------------------------ +// SD 卡存储参数 +// ------------------------------------------------------------ + +// SD 卡写入双缓冲区单个缓冲区大小(字节) +// 更大的缓冲区可减少写入次数,提升吞吐量,但占用更多 RAM +// STM32F405 RAM 为 192KB,建议不超过 65536(64KB) +// 典型值:32768(32KB) / 65536(64KB) +#define CFG_STORAGE_BUFFER_SIZE 32768 + +// 单个数据文件的最大大小(字节),超过后自动创建新文件 +// 较小的文件便于传输和分析,较大的文件减少文件切换开销 +// 典型值:100MB / 500MB / 1024MB +#define CFG_STORAGE_FILE_MAX_SIZE (100 * 1024 * 1024) + + +#endif // APP_CONFIG_H diff --git a/User/config_manager.h b/User/config_manager.h index 23232bc..38cb143 100644 --- a/User/config_manager.h +++ b/User/config_manager.h @@ -2,6 +2,7 @@ #define CONFIG_MANAGER_H #include "main.h" +#include "app_config.h" #include // 配置文件路径 @@ -17,8 +18,8 @@ typedef struct { } SystemConfig_t; // 默认配置值 -#define DEFAULT_UART_OUTPUT_ENABLED 1 -#define DEFAULT_STORAGE_ENABLED 0 +#define DEFAULT_UART_OUTPUT_ENABLED CFG_UART_OUTPUT_ENABLED +#define DEFAULT_STORAGE_ENABLED CFG_STORAGE_ENABLED #define CONFIG_VERSION 0x00010000 // 版本 1.0.0 // 函数声明 diff --git a/User/data_packet.c b/User/data_packet.c index 985348b..75e6c2f 100644 --- a/User/data_packet.c +++ b/User/data_packet.c @@ -2,8 +2,8 @@ #include "stddef.h" #include "stm32f4xx_hal.h" -// CRC16-MODBUS算法实现 -uint16_t Calculate_CRC16(const uint8_t *data, uint16_t len) { +uint16_t Calculate_CRC16(const uint8_t *data, uint16_t len) +{ uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < len; i++) { crc ^= data[i]; @@ -18,107 +18,91 @@ uint16_t Calculate_CRC16(const uint8_t *data, uint16_t len) { return crc; } -void PackData(DataPacket_t *packet, int32_t adc1, int32_t adc2, int32_t adc3, +// ─── 内部辅助宏:填充GPS位置字段(仅在宏开启时) ───────────────────────────── +#ifdef ENABLE_GPS_POSITION +#define FILL_GPS_POSITION(pkt, lat, lon, alt) \ + do { (pkt)->gps_latitude = (lat); (pkt)->gps_longitude = (lon); (pkt)->gps_altitude = (alt); } while(0) +#else +#define FILL_GPS_POSITION(pkt, lat, lon, alt) // 空操作 +#endif + +// ─── 原始ADC数据包 ──────────────────────────────────────────────────────────── + +void PackData(DataPacket_t *packet, + int32_t adc1, int32_t adc2, int32_t adc3, uint32_t gps_time, float latitude, float longitude, float altitude) { if (packet == NULL) return; - - // 设置包头 - packet->start_byte = PACKET_START_BYTE; - - // 设置时间戳 -// packet->timestamp = HAL_GetTick(); - - // 设置ADC数据 - packet->adc_data1 = adc1; - packet->adc_data2 = adc2; - packet->adc_data3 = adc3; - - // 设置GPS数据 - packet->gps_time = gps_time; - packet->gps_latitude = latitude; - packet->gps_longitude = longitude; - packet->gps_altitude = altitude; + packet->start_byte = PACKET_TYPE_STANDARD; + packet->adc_data1 = adc1; + packet->adc_data2 = adc2; + packet->adc_data3 = adc3; + packet->gps_time = gps_time; + FILL_GPS_POSITION(packet, latitude, longitude, altitude); } +void PackDataWithOptic(DataPacketWithOptic_t *packet, + int32_t adc1, int32_t adc2, int32_t adc3, + uint32_t gps_time, float latitude, float longitude, float altitude, + uint32_t optical_mag) +{ + if (packet == NULL) return; + packet->start_byte = PACKET_TYPE_WITH_OPTIC; + packet->adc_data1 = adc1; + packet->adc_data2 = adc2; + packet->adc_data3 = adc3; + packet->gps_time = gps_time; + FILL_GPS_POSITION(packet, latitude, longitude, altitude); + packet->optical_mag = optical_mag; +} + +// ─── 校正数据包 ─────────────────────────────────────────────────────────────── + +void PackCorrectedData(CorrectedDataPacket_t *packet, + float x, float y, float z, + uint32_t gps_time, float latitude, float longitude, float altitude) +{ + PackCorrectedDataWithGPS(packet, x, y, z, gps_time, latitude, longitude, altitude); +} + +void PackCorrectedDataWithGPS(CorrectedDataPacketWithGPS_t *packet, + float x, float y, float z, + uint32_t gps_time, float latitude, float longitude, float altitude) +{ + if (packet == NULL) return; + packet->start_byte = PACKET_TYPE_STANDARD; + packet->corrected_x = x; + packet->corrected_y = y; + packet->corrected_z = z; + packet->gps_time = gps_time; + FILL_GPS_POSITION(packet, latitude, longitude, altitude); +} + +void PackCorrectedDataWithOptic(CorrectedDataPacketWithOptic_t *packet, + float x, float y, float z, + uint32_t gps_time, float latitude, float longitude, float altitude, + uint32_t optical_mag) +{ + if (packet == NULL) return; + packet->start_byte = PACKET_TYPE_WITH_OPTIC; + packet->corrected_x = x; + packet->corrected_y = y; + packet->corrected_z = z; + packet->gps_time = gps_time; + FILL_GPS_POSITION(packet, latitude, longitude, altitude); + packet->optical_mag = optical_mag; +} + +// ─── 验证函数 ───────────────────────────────────────────────────────────────── + uint8_t ValidatePacket(const DataPacket_t *packet) { if (packet == NULL) return 0; - - // 检查包头 - if (packet->start_byte != PACKET_START_BYTE) return 0; - - // 精简版数据包无校验和,仅检查包头 - return 1; // 包头正确,认为有效 + return (packet->start_byte == PACKET_TYPE_STANDARD) ? 1 : 0; } uint8_t ValidateCorrectedPacket(const CorrectedDataPacket_t *packet) { if (packet == NULL) return 0; - - // 检查包头 - if (packet->start_byte != PACKET_START_BYTE) return 0; - - // 精简版数据包无校验和,仅检查包头 - return 1; // 包头正确,认为有效 -} - -void PackCorrectedData(CorrectedDataPacket_t *packet, float x, float y, float z, - uint32_t gps_time, float latitude, float longitude, float altitude) -{ - if (packet == NULL) return; - - // 设置包头 - packet->start_byte = PACKET_START_BYTE; - - // 设置时间戳 - // packet->timestamp = HAL_GetTick(); - - // 设置校正后数据 - packet->corrected_x = x; - packet->corrected_y = y; - packet->corrected_z = z; - - // 设置GPS数据 - packet->gps_time = gps_time; - packet->gps_latitude = latitude; - packet->gps_longitude = longitude; - packet->gps_altitude = altitude; -} - -void PackCorrectedDataWithGPS(CorrectedDataPacketWithGPS_t *packet, float x, float y, float z, - uint32_t gps_time, float latitude, float longitude, float altitude) -{ - if (packet == NULL) return; - - // 设置包头 - packet->start_byte = PACKET_START_BYTE; - - // 设置时间戳 - // packet->timestamp = HAL_GetTick(); - - // 设置校正后数据 - packet->corrected_x = x; - packet->corrected_y = y; - packet->corrected_z = z; - - // 设置GPS数据 - packet->gps_time = gps_time; - packet->gps_latitude = latitude; - packet->gps_longitude = longitude; - packet->gps_altitude = altitude; -} - -uint8_t ValidateCorrectedPacketWithGPS(const CorrectedDataPacketWithGPS_t *packet) -{ - if (packet == NULL) return 0; - - // 检查包头 - if (packet->start_byte != PACKET_START_BYTE) return 0; - - // 精简版数据包无校验和,仅检查包头 - // 可以添加简单的数据合理性检查 - // 例如:检查GPS坐标是否在有效范围内等 - - return 1; // 包头正确,认为有效 + return (packet->start_byte == PACKET_TYPE_STANDARD) ? 1 : 0; } diff --git a/User/data_packet.h b/User/data_packet.h index 250f1fc..317ed8f 100644 --- a/User/data_packet.h +++ b/User/data_packet.h @@ -2,59 +2,109 @@ #define DATA_PACKET_H #include +#include "app_config.h" -#define PACKET_START_BYTE 0xFFFFFFFF -#define PACKET_END_BYTE 0x0000 +// 帧同步魔数(高3字节固定),低字节为类型标志 +#define PACKET_TYPE_STANDARD 0xFFFFFFFFUL // 标准包(无光泵) +#define PACKET_TYPE_WITH_OPTIC 0xFFFFFFFEUL // 扩展包(含光泵,末尾追加 optical_mag) +#define PACKET_SYNC_MASK 0xFFFFFF00UL // 同步检测掩码 + +// 兼容旧代码 +#define PACKET_START_BYTE PACKET_TYPE_STANDARD + +// GPS位置字段由 app_config.h 中 CFG_ENABLE_GPS_POSITION 控制 +#if CFG_ENABLE_GPS_POSITION +#define ENABLE_GPS_POSITION +#endif + +// ─── 原始ADC数据包 ──────────────────────────────────────────────────────────── -// 数据包结构(精简版 - 有包头无校验和,含GPS) typedef struct __attribute__((packed)) { - uint32_t start_byte; // 包头 (4字节) = 0xFFFFFFFF - // uint32_t timestamp; // 系统时间戳 (4字节) - int32_t adc_data1; // ADC1 数据 (4字节) - int32_t adc_data2; // ADC2 数据 (4字节) - int32_t adc_data3; // ADC3 数据 (4字节) - uint32_t gps_time; // GPS时间戳 (4字节) HHMMSS格式 - float gps_latitude; // GPS纬度 (4字节) - float gps_longitude; // GPS经度 (4字节) - float gps_altitude; // GPS海拔 (4字节) + uint32_t start_byte; // PACKET_TYPE_STANDARD + int32_t adc_data1; + int32_t adc_data2; + int32_t adc_data3; + uint32_t gps_time; // HHMMSS格式,始终存在 +#ifdef ENABLE_GPS_POSITION + float gps_latitude; + float gps_longitude; + float gps_altitude; +#endif } DataPacket_t; -// 校正后数据包结构(精简版 - 有包头无校验和,含GPS) typedef struct __attribute__((packed)) { - uint32_t start_byte; // 包头 (4字节) = 0xFFFFFFFF - // uint32_t timestamp; // 系统时间戳 (4字节) - float corrected_x; // 校正后X轴数据 (4字节) - float corrected_y; // 校正后Y轴数据 (4字节) - float corrected_z; // 校正后Z轴数据 (4字节) - uint32_t gps_time; // GPS时间戳 (4字节) HHMMSS格式 - float gps_latitude; // GPS纬度 (4字节) - float gps_longitude; // GPS经度 (4字节) - float gps_altitude; // GPS海拔 (4字节) + uint32_t start_byte; // PACKET_TYPE_WITH_OPTIC + int32_t adc_data1; + int32_t adc_data2; + int32_t adc_data3; + uint32_t gps_time; +#ifdef ENABLE_GPS_POSITION + float gps_latitude; + float gps_longitude; + float gps_altitude; +#endif + uint32_t optical_mag; // 单位 0.0001 nT,例如 123456789 = 12345.6789 nT +} DataPacketWithOptic_t; + +// ─── 校正数据包 ─────────────────────────────────────────────────────────────── + +typedef struct __attribute__((packed)) { + uint32_t start_byte; // PACKET_TYPE_STANDARD + float corrected_x; + float corrected_y; + float corrected_z; + uint32_t gps_time; +#ifdef ENABLE_GPS_POSITION + float gps_latitude; + float gps_longitude; + float gps_altitude; +#endif } CorrectedDataPacket_t; -// 带GPS信息的校正数据包结构(精简版 - 有包头无校验和) -typedef struct __attribute__((packed)) { - uint32_t start_byte; // 包头 (4字节) = 0xFFFFFFFF - // uint32_t timestamp; // 系统时间戳 (4字节) - float corrected_x; // 校正后X轴数据 (4字节) - float corrected_y; // 校正后Y轴数据 (4字节) - float corrected_z; // 校正后Z轴数据 (4字节) - uint32_t gps_time; // GPS时间戳 (4字节) HHMMSS格式 - float gps_latitude; // GPS纬度 (4字节) - float gps_longitude; // GPS经度 (4字节) - float gps_altitude; // GPS海拔 (4字节) -} CorrectedDataPacketWithGPS_t; +// 与 CorrectedDataPacket_t 相同,保留别名兼容旧代码 +typedef CorrectedDataPacket_t CorrectedDataPacketWithGPS_t; + +typedef struct __attribute__((packed)) { + uint32_t start_byte; // PACKET_TYPE_WITH_OPTIC + float corrected_x; + float corrected_y; + float corrected_z; + uint32_t gps_time; +#ifdef ENABLE_GPS_POSITION + float gps_latitude; + float gps_longitude; + float gps_altitude; +#endif + uint32_t optical_mag; // 单位 0.0001 nT +} CorrectedDataPacketWithOptic_t; + +// ─── 函数声明 ───────────────────────────────────────────────────────────────── -// 函数声明 uint16_t Calculate_CRC16(const uint8_t *data, uint16_t len); -void PackData(DataPacket_t *packet, int32_t adc1, int32_t adc2, int32_t adc3, + +void PackData(DataPacket_t *packet, + int32_t adc1, int32_t adc2, int32_t adc3, uint32_t gps_time, float latitude, float longitude, float altitude); -void PackCorrectedData(CorrectedDataPacket_t *packet, float x, float y, float z, + +void PackDataWithOptic(DataPacketWithOptic_t *packet, + int32_t adc1, int32_t adc2, int32_t adc3, + uint32_t gps_time, float latitude, float longitude, float altitude, + uint32_t optical_mag); + +void PackCorrectedData(CorrectedDataPacket_t *packet, + float x, float y, float z, uint32_t gps_time, float latitude, float longitude, float altitude); -void PackCorrectedDataWithGPS(CorrectedDataPacketWithGPS_t *packet, float x, float y, float z, - uint32_t gps_time, float latitude, float , float altitude); + +void PackCorrectedDataWithGPS(CorrectedDataPacketWithGPS_t *packet, + float x, float y, float z, + uint32_t gps_time, float latitude, float longitude, float altitude); + +void PackCorrectedDataWithOptic(CorrectedDataPacketWithOptic_t *packet, + float x, float y, float z, + uint32_t gps_time, float latitude, float longitude, float altitude, + uint32_t optical_mag); + uint8_t ValidatePacket(const DataPacket_t *packet); uint8_t ValidateCorrectedPacket(const CorrectedDataPacket_t *packet); -uint8_t ValidateCorrectedPacketWithGPS(const CorrectedDataPacketWithGPS_t *packet); #endif // DATA_PACKET_H diff --git a/User/data_storage.c b/User/data_storage.c index a9bbdf7..bb281a7 100644 --- a/User/data_storage.c +++ b/User/data_storage.c @@ -113,6 +113,41 @@ HAL_StatusTypeDef DataStorage_WriteData(DataStorageHandle_t *handle, const DataP return HAL_OK; } +/** + * @brief 通用字节流写入,支持任意包长度(方案Y混流存储) + * @param handle: 数据存储句柄指针 + * @param data: 数据缓冲区指针 + * @param size: 写入字节数 + * @retval HAL_StatusTypeDef + */ +HAL_StatusTypeDef DataStorage_WriteRawBytes(DataStorageHandle_t *handle, const uint8_t *data, uint16_t size) +{ + if (handle == NULL || data == NULL || !handle->initialized || size == 0) { + return HAL_ERROR; + } + + if (handle->stats.state != DATA_STORAGE_RECORDING) { + return HAL_ERROR; + } + + DataBuffer_t *active_buf = &handle->buffers[handle->active_buffer]; + + if (active_buf->index + size > DATA_STORAGE_BUFFER_SIZE) { + if (DataStorage_SwitchBuffer(handle) != HAL_OK) { + handle->stats.error_count++; + return HAL_ERROR; + } + active_buf = &handle->buffers[handle->active_buffer]; + } + + memcpy(&active_buf->data[active_buf->index], data, size); + active_buf->index += size; + active_buf->state = BUFFER_WRITING; + handle->stats.total_samples++; + + return HAL_OK; +} + /** * @brief 写入校正后的数据包到存储 * @param handle: 数据存储句柄指针 diff --git a/User/data_storage.h b/User/data_storage.h index c4c95f1..f1b723a 100644 --- a/User/data_storage.h +++ b/User/data_storage.h @@ -2,15 +2,16 @@ #define DATA_STORAGE_H #include "main.h" +#include "app_config.h" #include "fatfs.h" #include "ff.h" #include "data_packet.h" #include "correction.h" #include -// 数据存储配置 -#define DATA_STORAGE_BUFFER_SIZE 32768 // 缓冲区大小(字节) -#define DATA_STORAGE_FILE_MAX_SIZE (100*1024*1024) // 单个文件最大100MB +// 数据存储配置(数值由 app_config.h 中 CFG_* 统一管理) +#define DATA_STORAGE_BUFFER_SIZE CFG_STORAGE_BUFFER_SIZE +#define DATA_STORAGE_FILE_MAX_SIZE CFG_STORAGE_FILE_MAX_SIZE #define DATA_STORAGE_BASE_PATH "0:/DATA" // 数据存储基础路径 #define DATA_STORAGE_FILE_PREFIX "/ADC_DATA_" // 文件名前缀 #define DATA_STORAGE_FOLDER_PREFIX "SESSION_" // 文件夹名前缀 @@ -87,4 +88,7 @@ void DataStorage_ProcessBackgroundTasks(DataStorageHandle_t *handle); // 缓冲区可用性检查函数 uint8_t DataStorage_IsBufferAvailable(DataStorageHandle_t *handle, uint32_t required_size); +// 通用字节流写入(支持任意包类型,方案Y混流存储) +HAL_StatusTypeDef DataStorage_WriteRawBytes(DataStorageHandle_t *handle, const uint8_t *data, uint16_t size); + #endif // DATA_STORAGE_H diff --git a/User/optic_mag_driver.c b/User/optic_mag_driver.c new file mode 100644 index 0000000..de3587e --- /dev/null +++ b/User/optic_mag_driver.c @@ -0,0 +1,70 @@ +#include "optic_mag_driver.h" + +volatile uint32_t g_optic_mag_value = 0; +volatile uint8_t g_optic_mag_fresh = 0; + +typedef enum { + OPTIC_STATE_IDLE = 0, + OPTIC_STATE_COLLECTING, + OPTIC_STATE_WAIT_END, +} OpticState_t; + +static UART_HandleTypeDef *s_huart; +static uint8_t s_rx_byte; +static uint8_t s_bcd_buf[OPTIC_MAG_DATA_BYTES]; +static uint8_t s_data_count; +static OpticState_t s_state; + +void OpticMag_Init(UART_HandleTypeDef *huart) +{ + s_huart = huart; + s_state = OPTIC_STATE_IDLE; + s_data_count = 0; + HAL_UART_Receive_IT(s_huart, &s_rx_byte, 1); +} + +void OpticMag_UART_RxCpltCallback(UART_HandleTypeDef *huart) +{ + if (huart != s_huart) return; + + uint8_t byte = s_rx_byte; + + switch (s_state) { + case OPTIC_STATE_IDLE: + if (byte == OPTIC_MAG_FRAME_START) { + s_data_count = 0; + s_state = OPTIC_STATE_COLLECTING; + } + break; + + case OPTIC_STATE_COLLECTING: + s_bcd_buf[s_data_count++] = byte; + if (s_data_count == OPTIC_MAG_DATA_BYTES) { + s_state = OPTIC_STATE_WAIT_END; + } + break; + + case OPTIC_STATE_WAIT_END: + if (byte == OPTIC_MAG_FRAME_END) { + // 取前9位BCD数字(忽略第10位),每字节低nibble为有效数字 + uint32_t value = 0; + for (uint8_t i = 0; i < 9; i++) { + value = value * 10u + (s_bcd_buf[i] & 0x0Fu); + } + g_optic_mag_value = value; + g_optic_mag_fresh = 1; + } + // 无论结束字节是否合法,均回到IDLE(非法帧直接丢弃) + s_state = OPTIC_STATE_IDLE; + break; + } + + HAL_UART_Receive_IT(s_huart, &s_rx_byte, 1); +} + +void OpticMag_UART_ErrorCallback(UART_HandleTypeDef *huart) +{ + if (huart != s_huart) return; + s_state = OPTIC_STATE_IDLE; + HAL_UART_Receive_IT(s_huart, &s_rx_byte, 1); +} diff --git a/User/optic_mag_driver.h b/User/optic_mag_driver.h new file mode 100644 index 0000000..ae968fa --- /dev/null +++ b/User/optic_mag_driver.h @@ -0,0 +1,20 @@ +#ifndef OPTIC_MAG_DRIVER_H +#define OPTIC_MAG_DRIVER_H + +#include "main.h" +#include + +#define OPTIC_MAG_FRAME_START 0x5A +#define OPTIC_MAG_FRAME_END 0xA5 +#define OPTIC_MAG_DATA_BYTES 10 + +// 单位 0.0001 nT,忽略末位BCD数字,取前9位 +// 例如 123456789 表示 12345.6789 nT +extern volatile uint32_t g_optic_mag_value; +extern volatile uint8_t g_optic_mag_fresh; + +void OpticMag_Init(UART_HandleTypeDef *huart); +void OpticMag_UART_RxCpltCallback(UART_HandleTypeDef *huart); +void OpticMag_UART_ErrorCallback(UART_HandleTypeDef *huart); + +#endif // OPTIC_MAG_DRIVER_H