✨ 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
+84
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() 的调用频率
// 建议范围: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
+3 -2
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
// 函数声明
+76 -92
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;
}
+89 -39
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信息的校正数据包结构(精简版 - 有包头无校验和)
// 与 CorrectedDataPacket_t 相同,保留别名兼容旧代码
typedef CorrectedDataPacket_t CorrectedDataPacketWithGPS_t;
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;
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
+35
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: 数据存储句柄指针
+7 -3
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
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
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