feat(optic_mag): 集成光泵磁力仪驱动并重构数据包架构

- 新增光泵磁力仪驱动模块,通过 USART2 中断接收 BCD 编码数据,采样率 115200bps
- 重构数据包架构:引入标准包与扩展包(含光泵数据)两种类型,通过帧头魔数区分
- 新增 DataPacketWithOptic_t、CorrectedDataPacketWithOptic_t 两种扩展数据包类型
- 数据存储改为通用字节流写入(方案Y),支持任意包类型混流存储
- 将编译期配置集中到 app_config.h,包括 UART 输出、SD 存储、GPS 位置等开关
- 移除 ADC_SYNC GPIO 引脚配置,释放 PA2 用于 USART2_TX
- 主循环 ProcessAdcData 改为按需选择数据包类型,光泵数据快照在 ADC 中断前完成
- 新增 USART2 错误回调处理,支持接收异常时自动恢复
This commit is contained in:
zhoujie 2026-06-07 22:50:54 +08:00
parent 4f8feccc06
commit bc37e14fba
18 changed files with 694 additions and 299 deletions

49
CLAUDE.md Normal file
View File

@ -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 DMADE/RE 控制脚 PC7 |
| USART2 (PA3) | 光泵磁力仪接收 | 115 200 | 仅 RXDMA 循环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()` 即可

View File

@ -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

View File

@ -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);

View File

@ -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 */

View File

@ -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;

View File

@ -37,6 +37,7 @@
#include "system_monitor.h"
#include "config_manager.h"
#include "gps_driver.h"
#include "optic_mag_driver.h"
#include <stdio.h>
#include <string.h>
/* 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(&current_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(&current_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,6 +524,7 @@ int main(void)
MX_USART3_UART_Init();
MX_TIM2_Init();
MX_TIM1_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
// 初始化系统监控
@ -543,9 +532,12 @@ int main(void)
SystemMonitor_Init();
#endif
// 初始化调试输出
// 初始化调试输出含GPS
DebugOutput_Init();
// 初始化光泵磁力仪接收USART2 RX115200
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);
}
}

View File

@ -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.
*/

View File

@ -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 */

BIN
Doc/注意事项.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -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

84
User/app_config.h Normal file
View File

@ -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() 的调用频率
// 建议范围50005秒~ 600001分钟
#define CFG_DEBUG_OUTPUT_INTERVAL_MS 30000
// 监控统计数据写入 SD 卡 LOG.TXT 的时间间隔(毫秒)
// 间隔越短日志越详细,但 SD 卡写入操作越频繁
// 建议范围1000010秒~ 3000005分钟
#define CFG_MONITOR_SAVE_INTERVAL_MS 30000
// ------------------------------------------------------------
// SD 卡存储参数
// ------------------------------------------------------------
// SD 卡写入双缓冲区单个缓冲区大小(字节)
// 更大的缓冲区可减少写入次数,提升吞吐量,但占用更多 RAM
// STM32F405 RAM 为 192KB建议不超过 6553664KB
// 典型值3276832KB / 6553664KB
#define CFG_STORAGE_BUFFER_SIZE 32768
// 单个数据文件的最大大小(字节),超过后自动创建新文件
// 较小的文件便于传输和分析,较大的文件减少文件切换开销
// 典型值100MB / 500MB / 1024MB
#define CFG_STORAGE_FILE_MAX_SIZE (100 * 1024 * 1024)
#endif // APP_CONFIG_H

View File

@ -2,6 +2,7 @@
#define CONFIG_MANAGER_H
#include "main.h"
#include "app_config.h"
#include <stdint.h>
// 配置文件路径
@ -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
// 函数声明

View File

@ -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;
}

View File

@ -2,59 +2,109 @@
#define DATA_PACKET_H
#include <stdint.h>
#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

View File

@ -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:

View File

@ -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 <stdint.h>
// 数据存储配置
#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

70
User/optic_mag_driver.c Normal file
View File

@ -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);
}

20
User/optic_mag_driver.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef OPTIC_MAG_DRIVER_H
#define OPTIC_MAG_DRIVER_H
#include "main.h"
#include <stdint.h>
#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