✨ 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:
2026-06-07 22:50:54 +08:00
parent 4f8feccc06
commit bc37e14fba
18 changed files with 693 additions and 298 deletions
-10
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;
+113 -112
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,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);
}
}
+16 -1
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.
*/
+84
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 */