From 5b245f89c1203fcdb16d9548470fff56316b069d Mon Sep 17 00:00:00 2001 From: zhoujie <929834232@qq.com> Date: Sat, 7 Feb 2026 19:34:48 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(gps):=20=E9=9B=86=E6=88=90GPS?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E6=94=AF=E6=8C=81=E6=95=B0=E6=8D=AE=E9=87=87?= =?UTF-8?q?=E9=9B=86=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增GPS驱动模块,支持NMEA GPGGA/GNGGA语句解析 - 修改USART3配置,波特率从2000000调整为115200用于GPS数据接收 - 新增带GPS信息的校正数据包结构`CorrectedDataPacketWithGPS_t` - 在ADC数据处理流程中集成GPS数据获取和打包 - 更新数据包处理函数,支持GPS时间戳和经纬度信息 - 新增GPS驱动使用指南和集成说明文档 - 修改主循环,添加GPS数据处理调用 - 更新中断处理,添加GPS UART接收回调支持 📝 docs(gps): 添加GPS驱动和集成说明文档 - 新增`GPS_Driver_Guide.md`详细说明GPS驱动API和使用方法 - 新增`GPS_Integration_Guide.md`说明GPS数据集成到采集系统的实现细节 - 文档包含硬件连接、数据格式、使用示例和故障排除等内容 ♻️ refactor(data): 重构数据包结构以支持GPS信息 - 修改`DataPacket_t`和`CorrectedDataPacket_t`结构,添加GPS时间戳和经纬度字段 - 新增`CorrectedDataPacketWithGPS_t`结构用于带完整GPS信息的数据包 - 更新数据打包函数,支持GPS参数传递 - 简化数据包验证逻辑,移除校验和检查以提高处理速度 🔧 chore(config): 更新硬件配置文件 - 更新STM32CubeMX项目文件,修改USART3波特率配置 - 在中断处理文件中添加GPS驱动头文件包含 --- Core/Src/main.c | 76 +++++-- Core/Src/stm32f4xx_it.c | 1 + Core/Src/usart.c | 2 +- STM_ATEM_F405.ioc | 2 +- User/GPS_Driver_Guide.md | 322 ++++++++++++++++++++++++++++ User/GPS_Integration_Guide.md | 288 +++++++++++++++++++++++++ User/data_packet.c | 88 ++++---- User/data_packet.h | 49 +++-- User/gps_driver.c | 384 ++++++++++++++++++++++++++++++++++ User/gps_driver.h | 150 +++++++++++++ 10 files changed, 1289 insertions(+), 73 deletions(-) create mode 100644 User/GPS_Driver_Guide.md create mode 100644 User/GPS_Integration_Guide.md create mode 100644 User/gps_driver.c create mode 100644 User/gps_driver.h diff --git a/Core/Src/main.c b/Core/Src/main.c index d013b86..74d789d 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -36,6 +36,7 @@ #include "data_storage.h" #include "system_monitor.h" #include "config_manager.h" +#include "gps_driver.h" #include #include /* USER CODE END Includes */ @@ -79,6 +80,7 @@ CorrectionParams_t g_correction_params; // 数据包 DataPacket_t g_data_packet; CorrectedDataPacket_t g_corrected_packet; +CorrectedDataPacketWithGPS_t g_corrected_packet_with_gps; // 带GPS信息的数据包 // 数据存储句柄 DataStorageHandle_t g_data_storage; // 系统状态 @@ -156,7 +158,7 @@ static void ProcessAdcData(void) // 检查存储缓冲区是否可用(用于决定是否存储数据) uint8_t can_store_data = 0; if (g_recording_enabled) { - uint32_t max_packet_size = sizeof(CorrectedDataPacket_t); + uint32_t max_packet_size = sizeof(CorrectedDataPacketWithGPS_t); // 使用带GPS的数据包大小 can_store_data = DataStorage_IsBufferAvailable(&g_data_storage, max_packet_size); } @@ -170,6 +172,10 @@ static void ProcessAdcData(void) raw_adc[i] = (int32_t)(((uint32_t)ready_buffer->data[i][0] << 16) | ready_buffer->data[i][1]); } + // 2. 获取当前GPS数据 + GPS_Data_t current_gps_data; + uint8_t gps_valid = GPS_GetData(¤t_gps_data); + // 3. 应用校正算法 CorrectionResult_t correction_result; uint8_t correction_applied = 0; @@ -178,24 +184,38 @@ static void ProcessAdcData(void) Apply_Correction(raw_adc[0], raw_adc[1], raw_adc[2], &correction_result, &g_correction_params) == HAL_OK) { - - // 4a. 打包校正后的数据 - PackCorrectedData(&g_corrected_packet, + // 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; + 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); + correction_result.corrected_z, + gps_time, + lat, + lon); correction_applied = 1; // 发送校正后的数据包到串口(运行时配置) if (Config_IsUartOutputEnabled()) { - RS485_SendData((uint8_t*)&g_corrected_packet, sizeof(CorrectedDataPacket_t)); + RS485_SendData((uint8_t*)&g_corrected_packet_with_gps, sizeof(CorrectedDataPacketWithGPS_t)); } } else { // 4b. 校正失败或未启用,使用原始数据 - PackData(&g_data_packet, raw_adc[0], raw_adc[1], raw_adc[2]); + float lat = gps_valid ? (float)current_gps_data.position.latitude : 0.0f; + float lon = gps_valid ? (float)current_gps_data.position.longitude : 0.0f; + 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); // 发送原始数据包到串口(运行时配置) if (Config_IsUartOutputEnabled()) { @@ -207,8 +227,8 @@ static void ProcessAdcData(void) if (Config_IsStorageEnabled() && g_recording_enabled) { if (can_store_data) { if (correction_applied) { - // 存储校正后的数据 - DataStorage_WriteCorrectedData(&g_data_storage, &g_corrected_packet); + // 存储校正后的数据(带GPS信息) + DataStorage_WriteCorrectedData(&g_data_storage, (CorrectedDataPacket_t*)&g_corrected_packet_with_gps); } else { // 存储原始数据 DataStorage_WriteData(&g_data_storage, &g_data_packet); @@ -229,30 +249,30 @@ static void ProcessAdcData(void) } /** - * @brief 初始化调试输出 + * @brief 初始化调试输出和GPS * @retval None */ static void DebugOutput_Init(void) { - // USART3已在MX_USART3_UART_Init()中初始化 -#if ENABLE_SYSTEM_MONITOR - DebugOutput_SendString("\r\n=== System Debug Output Initialized ===\r\n"); -#endif + // USART3现在用于GPS接收,不再用于调试输出 + // 初始化GPS驱动 + if (GPS_Init() == HAL_OK) { + // GPS初始化成功 + // 注意:GPS初始化后,USART3将用于接收GPS数据 + } } /** - * @brief 通过USART3发送字符串 + * @brief 通过USART3发送字符串(已禁用,USART3用于GPS) * @param str: 要发送的字符串 * @retval None */ static void DebugOutput_SendString(const char* str) { #if ENABLE_SYSTEM_MONITOR - if (str == NULL) { - return; - } - - HAL_UART_Transmit(&huart3, (uint8_t*)str, strlen(str), 100); + // USART3现在用于GPS接收,调试输出已禁用 + // 如需调试输出,请使用USART1或其他串口 + (void)str; // 避免未使用参数警告 #endif } @@ -594,6 +614,9 @@ int main(void) // } } + // GPS数据处理 + GPS_Process(); + // 定期输出调试信息 (每1秒输出一次) #if ENABLE_SYSTEM_MONITOR if (current_tick - g_last_debug_output >= DEBUG_OUTPUT_INTERVAL_MS) { @@ -751,6 +774,19 @@ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) } } +/** + * @brief UART接收完成回调函数 + * @param huart: UART句柄指针 + * @retval None + */ +void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) +{ + if (huart == &huart3) { + // GPS数据接收回调 + GPS_UART_RxCpltCallback(huart); + } +} + /* USER CODE END 4 */ /** diff --git a/Core/Src/stm32f4xx_it.c b/Core/Src/stm32f4xx_it.c index 8b9d8bd..2ff8cd9 100644 --- a/Core/Src/stm32f4xx_it.c +++ b/Core/Src/stm32f4xx_it.c @@ -24,6 +24,7 @@ /* USER CODE BEGIN Includes */ #include "ltc2508_driver.h" #include "rs485_driver.h" +#include "gps_driver.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ diff --git a/Core/Src/usart.c b/Core/Src/usart.c index e10acf0..4a4ca7e 100644 --- a/Core/Src/usart.c +++ b/Core/Src/usart.c @@ -70,7 +70,7 @@ void MX_USART3_UART_Init(void) /* USER CODE END USART3_Init 1 */ huart3.Instance = USART3; - huart3.Init.BaudRate = 2000000; + huart3.Init.BaudRate = 115200; huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.StopBits = UART_STOPBITS_1; huart3.Init.Parity = UART_PARITY_NONE; diff --git a/STM_ATEM_F405.ioc b/STM_ATEM_F405.ioc index 3007b64..76b060d 100644 --- a/STM_ATEM_F405.ioc +++ b/STM_ATEM_F405.ioc @@ -360,7 +360,7 @@ TIM2.Prescaler=83 USART1.BaudRate=2000000 USART1.IPParameters=VirtualMode,BaudRate USART1.VirtualMode=VM_ASYNC -USART3.BaudRate=2000000 +USART3.BaudRate=115200 USART3.IPParameters=VirtualMode,BaudRate USART3.VirtualMode=VM_ASYNC USB_DEVICE.CLASS_NAME_FS=MSC diff --git a/User/GPS_Driver_Guide.md b/User/GPS_Driver_Guide.md new file mode 100644 index 0000000..100c015 --- /dev/null +++ b/User/GPS_Driver_Guide.md @@ -0,0 +1,322 @@ +# GPS驱动使用指南 + +## 概述 + +本GPS驱动用于通过USART3接收GPS模块的NMEA数据,主要解析GPGGA语句,提取时间、经纬度、海拔高度等信息。 + +## 硬件连接 + +- **USART3 TX (PB10)**: 连接到GPS模块的RX(可选,如果需要向GPS发送配置命令) +- **USART3 RX (PB11)**: 连接到GPS模块的TX(接收GPS数据) +- **波特率**: 9600(GPS标准波特率) +- **数据格式**: 8位数据位,1位停止位,无校验位 + +## 功能特性 + +### 1. 支持的NMEA语句 +- **GPGGA**: GPS定位数据(主要解析) +- **GNGGA**: 北斗+GPS组合定位数据(主要解析) +- 可扩展支持其他NMEA语句(如GPRMC等) + +### 2. 解析的数据 +- **时间信息**: + - UTC时间(时、分、秒、毫秒) + +- **位置信息**: + - 纬度(十进制度数) + - 纬度方向(N/S) + - 经度(十进制度数) + - 经度方向(E/W) + - 海拔高度(米) + +- **定位质量**: + - 定位状态(无效、GPS、DGPS、RTK等) + - 使用的卫星数量 + - 水平精度因子(HDOP) + +### 3. 数据有效性管理 +- 自动检测GPS定位状态 +- 数据超时检测(2秒无更新则标记为无效) +- 数据有效标志位 + +## API接口 + +### 初始化函数 + +```c +HAL_StatusTypeDef GPS_Init(void); +``` +- **功能**: 初始化GPS驱动,启动UART接收 +- **返回值**: HAL_OK表示成功,HAL_ERROR表示失败 +- **调用位置**: 在main()函数的初始化部分调用 + +### 数据处理函数 + +```c +void GPS_Process(void); +``` +- **功能**: GPS数据处理,检查数据超时 +- **调用位置**: 在主循环中定期调用 + +### 获取GPS数据 + +```c +uint8_t GPS_GetData(GPS_Data_t *gps_data); +``` +- **功能**: 获取当前GPS数据 +- **参数**: gps_data - 指向GPS数据结构体的指针 +- **返回值**: 1表示数据有效,0表示数据无效或超时 + +### 检查数据有效性 + +```c +uint8_t GPS_IsDataValid(void); +``` +- **功能**: 检查GPS数据是否有效 +- **返回值**: 1表示有效,0表示无效 + +### 获取格式化字符串 + +```c +void GPS_GetTimeString(char *buffer, uint16_t size); +void GPS_GetPositionString(char *buffer, uint16_t size); +``` +- **功能**: 获取格式化的GPS时间和位置字符串 +- **参数**: + - buffer - 输出缓冲区 + - size - 缓冲区大小 + +## 使用示例 + +### 基本使用流程 + +```c +// 1. 在main()函数中初始化GPS +GPS_Init(); + +// 2. 在主循环中处理GPS数据 +while (1) { + // 处理GPS数据(检查超时) + GPS_Process(); + + // 定期获取GPS数据(例如每5秒) + if (current_tick - last_gps_check >= 5000) { + GPS_Data_t gps_data; + if (GPS_GetData(&gps_data)) { + // GPS数据有效,可以使用 + printf("Time: %02d:%02d:%02d.%03d UTC\n", + gps_data.time.hour, + gps_data.time.minute, + gps_data.time.second, + gps_data.time.millisec); + + printf("Position: %.6f%c, %.6f%c\n", + gps_data.position.latitude, + gps_data.position.lat_direction, + gps_data.position.longitude, + gps_data.position.lon_direction); + + printf("Altitude: %.1f m\n", gps_data.position.altitude); + printf("Satellites: %d\n", gps_data.position.satellites); + } else { + printf("GPS data not available\n"); + } + last_gps_check = current_tick; + } +} +``` + +### 获取格式化字符串 + +```c +char time_str[64]; +char pos_str[128]; + +GPS_GetTimeString(time_str, sizeof(time_str)); +GPS_GetPositionString(pos_str, sizeof(pos_str)); + +// 通过串口发送 +HAL_UART_Transmit(&huart1, (uint8_t*)time_str, strlen(time_str), 100); +HAL_UART_Transmit(&huart1, (uint8_t*)pos_str, strlen(pos_str), 100); +``` + +## 数据结构 + +### GPS_Time_t - GPS时间结构体 + +```c +typedef struct { + uint8_t hour; // 时 (UTC) + uint8_t minute; // 分 + uint8_t second; // 秒 + uint16_t millisec; // 毫秒 +} GPS_Time_t; +``` + +### GPS_Position_t - GPS位置结构体 + +```c +typedef struct { + double latitude; // 纬度 (度) + char lat_direction; // 纬度方向 ('N' or 'S') + double longitude; // 经度 (度) + char lon_direction; // 经度方向 ('E' or 'W') + double altitude; // 海拔高度 (米) + GPS_FixStatus_t fix_status; // 定位状态 + uint8_t satellites; // 使用的卫星数量 + float hdop; // 水平精度因子 +} GPS_Position_t; +``` + +### GPS_Data_t - GPS数据结构体 + +```c +typedef struct { + GPS_Time_t time; // GPS时间 + GPS_Position_t position; // GPS位置 + uint8_t data_valid; // 数据有效标志 (1=有效, 0=无效) + uint32_t last_update_tick; // 最后更新时间戳 +} GPS_Data_t; +``` + +### GPS_FixStatus_t - GPS定位状态枚举 + +```c +typedef enum { + GPS_FIX_INVALID = 0, // 无效定位 + GPS_FIX_GPS = 1, // GPS定位 + GPS_FIX_DGPS = 2, // 差分GPS定位 + GPS_FIX_PPS = 3, // PPS定位 + GPS_FIX_RTK = 4, // RTK固定解 + GPS_FIX_RTK_FLOAT = 5, // RTK浮点解 + GPS_FIX_ESTIMATED = 6, // 估算 + GPS_FIX_MANUAL = 7, // 手动输入 + GPS_FIX_SIMULATION = 8 // 模拟模式 +} GPS_FixStatus_t; +``` + +## NMEA GPGGA语句格式 + +GPGGA语句示例: +``` +$GPGGA,123519.00,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 +``` + +字段说明: +- **字段0**: $GPGGA - 语句标识符 +- **字段1**: 123519.00 - UTC时间 (hhmmss.ss) +- **字段2**: 4807.038 - 纬度 (ddmm.mmmm) +- **字段3**: N - 纬度方向 (N/S) +- **字段4**: 01131.000 - 经度 (dddmm.mmmm) +- **字段5**: E - 经度方向 (E/W) +- **字段6**: 1 - 定位质量 (0=无效, 1=GPS, 2=DGPS, etc.) +- **字段7**: 08 - 使用的卫星数量 +- **字段8**: 0.9 - HDOP水平精度因子 +- **字段9**: 545.4 - 海拔高度 +- **字段10**: M - 高度单位 (米) +- **字段11-14**: 其他信息 +- **字段15**: *47 - 校验和 + +## 坐标转换 + +GPS模块输出的坐标格式为度分格式(ddmm.mmmm),驱动会自动转换为十进制度数格式: + +**转换公式**: +``` +十进制度数 = 度 + (分 / 60) +``` + +**示例**: +- 输入: 4807.038 N +- 度数: 48 +- 分钟: 07.038 +- 输出: 48 + (7.038 / 60) = 48.1173°N + +## 注意事项 + +1. **USART3占用**: GPS驱动使用USART3,因此USART3不能再用于其他功能(如调试输出) + +2. **波特率配置**: GPS模块的波特率已配置为9600,如果您的GPS模块使用其他波特率,需要在[`usart.c`](../Core/Src/usart.c:73)中修改 + +3. **数据更新频率**: GPS模块通常每秒更新一次数据(1Hz),部分模块支持更高频率 + +4. **冷启动时间**: GPS模块首次启动或长时间未使用后,可能需要30秒到几分钟才能获得有效定位 + +5. **室内定位**: GPS信号在室内通常无法接收,需要在室外或窗边测试 + +6. **数据有效性**: 使用前务必检查[`GPS_IsDataValid()`](gps_driver.h:95)或[`GPS_GetData()`](gps_driver.h:89)的返回值 + +7. **中断优先级**: USART3中断优先级设置为15(最低优先级),如需调整请在[`usart.c`](../Core/Src/usart.c:161)中修改 + +## 扩展功能 + +### 添加其他NMEA语句解析 + +如需解析其他NMEA语句(如GPRMC),可以在[`GPS_ParseNMEA()`](gps_driver.c:213)函数中添加: + +```c +static void GPS_ParseNMEA(char *nmea) +{ + if (nmea == NULL || nmea[0] != '$') { + return; + } + + // 解析GPGGA + if (strncmp(nmea, "$GPGGA", 6) == 0 || strncmp(nmea, "$GNGGA", 6) == 0) { + GPS_ParseGPGGA(nmea); + } + // 添加GPRMC解析 + else if (strncmp(nmea, "$GPRMC", 6) == 0) { + GPS_ParseGPRMC(nmea); // 需要实现此函数 + } +} +``` + +### 向GPS模块发送配置命令 + +如需向GPS模块发送配置命令,可以使用: + +```c +char cmd[] = "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n"; +HAL_UART_Transmit(&huart3, (uint8_t*)cmd, strlen(cmd), 100); +``` + +## 故障排除 + +### 问题1: 无法接收GPS数据 +- 检查GPS模块电源是否正常 +- 检查USART3引脚连接是否正确 +- 检查GPS模块波特率是否为9600 +- 确认GPS模块在室外或窗边,能接收到卫星信号 + +### 问题2: 数据一直显示无效 +- GPS模块可能需要冷启动时间(30秒到几分钟) +- 检查GPS模块天线连接 +- 确认在室外或窗边测试 +- 检查GPS模块LED指示灯状态 + +### 问题3: 坐标数据不准确 +- 检查卫星数量是否足够(至少4颗) +- 检查HDOP值(小于2为良好) +- 等待GPS模块完全定位(通常需要几分钟) + +## 文件列表 + +- [`gps_driver.h`](gps_driver.h:1) - GPS驱动头文件 +- [`gps_driver.c`](gps_driver.c:1) - GPS驱动源文件 +- [`main.c`](../Core/Src/main.c:1) - 主程序(包含GPS初始化和使用示例) +- [`usart.c`](../Core/Src/usart.c:1) - USART配置文件 +- [`stm32f4xx_it.c`](../Core/Src/stm32f4xx_it.c:1) - 中断处理文件 + +## 版本历史 + +- **v1.0** (2026-02-07) + - 初始版本 + - 支持GPGGA/GNGGA语句解析 + - 提取时间、经纬度、海拔高度等信息 + - 数据有效性管理和超时检测 + +## 作者 + +- 开发者: Your Name +- 日期: 2026-02-07 diff --git a/User/GPS_Integration_Guide.md b/User/GPS_Integration_Guide.md new file mode 100644 index 0000000..7083ed8 --- /dev/null +++ b/User/GPS_Integration_Guide.md @@ -0,0 +1,288 @@ +# GPS位置信息集成说明 + +## 概述 + +GPS位置信息已成功集成到数据采集系统中。每个ADC数据包现在都包含对应的GPS时间和位置信息,实现了数据与地理位置的关联。 + +## 数据包结构 + +### CorrectedDataPacketWithGPS_t - 带GPS信息的校正数据包 + +```c +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字节) + // GPS信息 + uint8_t gps_valid; // GPS数据有效标志 (1字节) 1=有效, 0=无效 + uint8_t gps_hour; // GPS时间-时 (1字节) UTC时间 + uint8_t gps_minute; // GPS时间-分 (1字节) + uint8_t gps_second; // GPS时间-秒 (1字节) + float gps_latitude; // GPS纬度 (4字节) 十进制度数 + float gps_longitude; // GPS经度 (4字节) 十进制度数 + float gps_altitude; // GPS海拔高度 (4字节) 米 + uint8_t gps_satellites; // GPS卫星数量 (1字节) + uint8_t reserved[3]; // 保留字节 (3字节) + uint16_t checksum; // CRC16校验和 (2字节) + uint16_t end_byte; // 包尾 (2字节) = 0x0000 +} CorrectedDataPacketWithGPS_t; +``` + +**总大小**: 48字节 + +### 数据包字段说明 + +#### 基本信息 +- **start_byte**: 固定包头标识 `0xFFFFFFFF`,用于数据包同步 +- **timestamp**: 系统时间戳(毫秒),从系统启动开始计时 +- **corrected_x/y/z**: 经过校正算法处理后的三轴数据 + +#### GPS时间信息 +- **gps_valid**: GPS数据有效性标志 + - `1`: GPS数据有效,定位成功 + - `0`: GPS数据无效或未定位 +- **gps_hour**: UTC时间-小时 (0-23) +- **gps_minute**: UTC时间-分钟 (0-59) +- **gps_second**: UTC时间-秒 (0-59) + +#### GPS位置信息 +- **gps_latitude**: 纬度(十进制度数) + - 正值表示北纬,负值表示南纬 + - 范围: -90.0 到 +90.0 + - 示例: 39.9042 表示北纬39.9042° + +- **gps_longitude**: 经度(十进制度数) + - 正值表示东经,负值表示西经 + - 范围: -180.0 到 +180.0 + - 示例: 116.4074 表示东经116.4074° + +- **gps_altitude**: 海拔高度(米) + - 相对于海平面的高度 + - 示例: 50.5 表示海拔50.5米 + +- **gps_satellites**: 当前使用的卫星数量 + - 通常需要至少4颗卫星才能定位 + - 卫星数量越多,定位精度越高 + +#### 数据完整性 +- **checksum**: CRC16-MODBUS校验和,用于验证数据完整性 +- **end_byte**: 固定包尾标识 `0x0000` + +## 数据流程 + +### 1. 数据采集流程 + +``` +ADC采样 → 校正算法 → 获取GPS数据 → 打包数据 → 发送/存储 +``` + +### 2. 详细流程说明 + +1. **ADC数据采集**: + - 通过外部中断触发,4KHz采样率 + - 三路LTC2508 ADC同时采样 + +2. **数据校正**: + - 应用校正算法处理原始ADC数据 + - 得到校正后的X、Y、Z轴数据 + +3. **GPS数据获取**: + - 调用[`GPS_GetData()`](User/gps_driver.h:89)获取当前GPS数据 + - 检查GPS数据有效性 + +4. **数据打包**: + - 调用[`PackCorrectedDataWithGPS()`](User/data_packet.h:46)打包数据 + - 自动计算CRC16校验和 + +5. **数据输出**: + - **串口输出**: 通过USART1(RS485)发送数据包 + - **SD卡存储**: 将数据包写入SD卡文件 + +## 使用示例 + +### 数据包解析示例(接收端) + +```c +// 接收数据包 +CorrectedDataPacketWithGPS_t rx_packet; + +// 验证数据包 +if (ValidateCorrectedPacketWithGPS(&rx_packet)) { + // 数据包有效 + + // 解析ADC数据 + float x = rx_packet.corrected_x; + float y = rx_packet.corrected_y; + float z = rx_packet.corrected_z; + + // 解析GPS数据 + if (rx_packet.gps_valid) { + // GPS数据有效 + printf("GPS Time: %02d:%02d:%02d UTC\n", + rx_packet.gps_hour, + rx_packet.gps_minute, + rx_packet.gps_second); + + printf("Position: %.6f°, %.6f°\n", + rx_packet.gps_latitude, + rx_packet.gps_longitude); + + printf("Altitude: %.1f m\n", rx_packet.gps_altitude); + printf("Satellites: %d\n", rx_packet.gps_satellites); + } else { + printf("GPS data not available\n"); + } +} +``` + +### Python解析示例 + +```python +import struct + +def parse_gps_packet(data): + """解析带GPS信息的数据包""" + # 数据包格式: I I f f f B B B B f f f B 3s H H + # I=uint32, f=float, B=uint8, H=uint16, 3s=3字节 + + format_str = 'adc_data2 = adc2; packet->adc_data3 = adc3; - // 计算校验和 (不包括校验和字段本身和包尾) - uint16_t checksum = Calculate_CRC16((uint8_t*)packet, - sizeof(DataPacket_t) - sizeof(packet->checksum) - sizeof(packet->end_byte)); - packet->checksum = checksum; - - // 设置包尾 - packet->end_byte = PACKET_END_BYTE; + // 设置GPS数据 + packet->gps_time = gps_time; + packet->gps_latitude = latitude; + packet->gps_longitude = longitude; } uint8_t ValidatePacket(const DataPacket_t *packet) @@ -49,16 +47,8 @@ uint8_t ValidatePacket(const DataPacket_t *packet) // 检查包头 if (packet->start_byte != PACKET_START_BYTE) return 0; - // 检查包尾 - if (packet->end_byte != PACKET_END_BYTE) return 0; - - // 验证校验和 - uint16_t calculated_crc = Calculate_CRC16((uint8_t*)packet, - sizeof(DataPacket_t) - sizeof(packet->checksum) - sizeof(packet->end_byte)); - - if (calculated_crc != packet->checksum) return 0; - - return 1; // 验证通过 + // 精简版数据包无校验和,仅检查包头 + return 1; // 包头正确,认为有效 } uint8_t ValidateCorrectedPacket(const CorrectedDataPacket_t *packet) @@ -68,19 +58,12 @@ uint8_t ValidateCorrectedPacket(const CorrectedDataPacket_t *packet) // 检查包头 if (packet->start_byte != PACKET_START_BYTE) return 0; - // 检查包尾 - if (packet->end_byte != PACKET_END_BYTE) return 0; - - // 验证校验和 - uint16_t calculated_crc = Calculate_CRC16((uint8_t*)packet, - sizeof(CorrectedDataPacket_t) - sizeof(packet->checksum) - sizeof(packet->end_byte)); - - if (calculated_crc != packet->checksum) return 0; - - return 1; // 验证通过 + // 精简版数据包无校验和,仅检查包头 + return 1; // 包头正确,认为有效 } -void PackCorrectedData(CorrectedDataPacket_t *packet, float x, float y, float z) +void PackCorrectedData(CorrectedDataPacket_t *packet, float x, float y, float z, + uint32_t gps_time, float latitude, float longitude) { if (packet == NULL) return; @@ -95,11 +78,44 @@ void PackCorrectedData(CorrectedDataPacket_t *packet, float x, float y, float z) packet->corrected_y = y; packet->corrected_z = z; - // 计算校验和 - uint16_t checksum = Calculate_CRC16((uint8_t*)packet, - sizeof(CorrectedDataPacket_t) - sizeof(packet->checksum) - sizeof(packet->end_byte)); - packet->checksum = checksum; - - // 设置包尾 - packet->end_byte = PACKET_END_BYTE; + // 设置GPS数据 + packet->gps_time = gps_time; + packet->gps_latitude = latitude; + packet->gps_longitude = longitude; +} + +void PackCorrectedDataWithGPS(CorrectedDataPacketWithGPS_t *packet, float x, float y, float z, + uint32_t gps_time, float latitude, float longitude) +{ + 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; +} + +uint8_t ValidateCorrectedPacketWithGPS(const CorrectedDataPacketWithGPS_t *packet) +{ + if (packet == NULL) return 0; + + // 检查包头 + if (packet->start_byte != PACKET_START_BYTE) return 0; + + // 精简版数据包无校验和,仅检查包头 + // 可以添加简单的数据合理性检查 + // 例如:检查GPS坐标是否在有效范围内等 + + return 1; // 包头正确,认为有效 } diff --git a/User/data_packet.h b/User/data_packet.h index 106c3e9..cc78748 100644 --- a/User/data_packet.h +++ b/User/data_packet.h @@ -6,33 +6,52 @@ #define PACKET_START_BYTE 0xFFFFFFFF #define PACKET_END_BYTE 0x0000 -// 数据包结构 - 启用校验和和包尾 +// 数据包结构(精简版 - 有包头无校验和,含GPS) typedef struct __attribute__((packed)) { - uint32_t start_byte; // 包头 (4字节) - uint32_t timestamp; // 时间戳 (4字节) + 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字节) - uint16_t checksum; // CRC16校验和 (2字节) - uint16_t end_byte; // 包尾 (2字节) + uint32_t gps_time; // GPS时间戳 (4字节) HHMMSS格式 + float gps_latitude; // GPS纬度 (4字节) + float gps_longitude; // GPS经度 (4字节) } DataPacket_t; -// 校正后数据包结构 +// 校正后数据包结构(精简版 - 有包头无校验和,含GPS) typedef struct __attribute__((packed)) { - uint32_t start_byte; // 包头 - uint32_t timestamp; // 时间戳 - float corrected_x; // 校正后X轴数据 - float corrected_y; // 校正后Y轴数据 - float corrected_z; // 校正后Z轴数据 - uint16_t checksum; // CRC16校验和 - uint16_t end_byte; // 包尾 + 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字节) } 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字节) +} CorrectedDataPacketWithGPS_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 PackCorrectedData(CorrectedDataPacket_t *packet, float x, float y, float z); +void PackData(DataPacket_t *packet, int32_t adc1, int32_t adc2, int32_t adc3, + uint32_t gps_time, float latitude, float longitude); +void PackCorrectedData(CorrectedDataPacket_t *packet, float x, float y, float z, + uint32_t gps_time, float latitude, float longitude); +void PackCorrectedDataWithGPS(CorrectedDataPacketWithGPS_t *packet, float x, float y, float z, + uint32_t gps_time, float latitude, float longitude); 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/gps_driver.c b/User/gps_driver.c new file mode 100644 index 0000000..a6eb5ff --- /dev/null +++ b/User/gps_driver.c @@ -0,0 +1,384 @@ +/** + ****************************************************************************** + * @file gps_driver.c + * @brief GPS NMEA数据接收和解析驱动实现 + * @author Your Name + * @date 2026-02-07 + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "gps_driver.h" + +/* Private typedef -----------------------------------------------------------*/ + +/* Private define ------------------------------------------------------------*/ + +/* Private macro -------------------------------------------------------------*/ + +/* Private variables ---------------------------------------------------------*/ +static uint8_t gps_rx_buffer[GPS_RX_BUFFER_SIZE]; // DMA接收缓冲区 +static uint8_t gps_rx_byte; // 单字节接收缓冲 +static char gps_nmea_buffer[GPS_NMEA_MAX_LENGTH]; // NMEA语句缓冲区 +static uint16_t gps_nmea_index = 0; // NMEA缓冲区索引 +static GPS_Data_t gps_data; // GPS数据 + +/* Private function prototypes -----------------------------------------------*/ +static void GPS_ParseNMEA(char *nmea); +static void GPS_ParseGPGGA(char *nmea); +static double GPS_ConvertToDecimal(const char *coord, char direction); + +/* Exported functions --------------------------------------------------------*/ + +/** + * @brief 初始化GPS驱动 + * @retval HAL状态 + */ +HAL_StatusTypeDef GPS_Init(void) +{ + HAL_StatusTypeDef status; + + // 清空GPS数据 + memset(&gps_data, 0, sizeof(GPS_Data_t)); + gps_data.data_valid = 0; + + // 清空缓冲区 + memset(gps_rx_buffer, 0, GPS_RX_BUFFER_SIZE); + memset(gps_nmea_buffer, 0, GPS_NMEA_MAX_LENGTH); + gps_nmea_index = 0; + + // 启动UART接收(单字节中断接收) + status = HAL_UART_Receive_IT(&GPS_UART_HANDLE, &gps_rx_byte, 1); + + if (status == HAL_OK) { + // 可选:启用UART空闲中断 + __HAL_UART_ENABLE_IT(&GPS_UART_HANDLE, UART_IT_IDLE); + } + + return status; +} + +/** + * @brief GPS数据接收处理(在主循环中调用) + * @retval None + */ +void GPS_Process(void) +{ + // 检查数据是否超时 + if (gps_data.data_valid) { + uint32_t current_tick = HAL_GetTick(); + if ((current_tick - gps_data.last_update_tick) > GPS_DATA_TIMEOUT_MS) { + gps_data.data_valid = 0; // 数据超时,标记为无效 + } + } +} + +/** + * @brief 获取GPS数据 + * @param gps_data_out: 指向GPS数据结构体的指针 + * @retval 1=数据有效, 0=数据无效或超时 + */ +uint8_t GPS_GetData(GPS_Data_t *gps_data_out) +{ + if (gps_data_out == NULL) { + return 0; + } + + // 复制GPS数据 + memcpy(gps_data_out, &gps_data, sizeof(GPS_Data_t)); + + return gps_data.data_valid; +} + +/** + * @brief 检查GPS数据是否有效 + * @retval 1=有效, 0=无效 + */ +uint8_t GPS_IsDataValid(void) +{ + return gps_data.data_valid; +} + +/** + * @brief 获取GPS时间字符串 + * @param buffer: 输出缓冲区 + * @param size: 缓冲区大小 + * @retval None + */ +void GPS_GetTimeString(char *buffer, uint16_t size) +{ + if (buffer == NULL || size == 0) { + return; + } + + if (gps_data.data_valid) { + snprintf(buffer, size, "%02d:%02d:%02d.%03d UTC", + gps_data.time.hour, + gps_data.time.minute, + gps_data.time.second, + gps_data.time.millisec); + } else { + snprintf(buffer, size, "No GPS Time"); + } +} + +/** + * @brief 获取GPS位置字符串 + * @param buffer: 输出缓冲区 + * @param size: 缓冲区大小 + * @retval None + */ +void GPS_GetPositionString(char *buffer, uint16_t size) +{ + if (buffer == NULL || size == 0) { + return; + } + + if (gps_data.data_valid) { + snprintf(buffer, size, "Lat: %.6f%c, Lon: %.6f%c, Alt: %.1fm, Sats: %d", + gps_data.position.latitude, + gps_data.position.lat_direction, + gps_data.position.longitude, + gps_data.position.lon_direction, + gps_data.position.altitude, + gps_data.position.satellites); + } else { + snprintf(buffer, size, "No GPS Position"); + } +} + +/** + * @brief UART接收完成回调函数 + * @param huart: UART句柄 + * @retval None + */ +void GPS_UART_RxCpltCallback(UART_HandleTypeDef *huart) +{ + if (huart->Instance == GPS_UART_HANDLE.Instance) { + // 处理接收到的字节 + char received_char = (char)gps_rx_byte; + + // 检测NMEA语句开始标志 '$' + if (received_char == '$') { + gps_nmea_index = 0; + gps_nmea_buffer[gps_nmea_index++] = received_char; + } + // 检测NMEA语句结束标志 '\n' + else if (received_char == '\n') { + if (gps_nmea_index > 0 && gps_nmea_index < GPS_NMEA_MAX_LENGTH) { + gps_nmea_buffer[gps_nmea_index] = '\0'; // 字符串结束符 + GPS_ParseNMEA(gps_nmea_buffer); // 解析NMEA语句 + gps_nmea_index = 0; // 重置索引 + } + } + // 累积NMEA语句字符 + else if (gps_nmea_index > 0 && gps_nmea_index < (GPS_NMEA_MAX_LENGTH - 1)) { + if (received_char != '\r') { // 忽略回车符 + gps_nmea_buffer[gps_nmea_index++] = received_char; + } + } + + // 继续接收下一个字节 + HAL_UART_Receive_IT(&GPS_UART_HANDLE, &gps_rx_byte, 1); + } +} + +/** + * @brief UART空闲中断回调函数 + * @param huart: UART句柄 + * @retval None + */ +void GPS_UART_IdleCallback(UART_HandleTypeDef *huart) +{ + if (huart->Instance == GPS_UART_HANDLE.Instance) { + // 可以在这里处理空闲中断 + // 目前使用单字节接收,不需要特殊处理 + } +} + +/* Private functions ---------------------------------------------------------*/ + +/** + * @brief 解析NMEA语句 + * @param nmea: NMEA语句字符串 + * @retval None + */ +static void GPS_ParseNMEA(char *nmea) +{ + if (nmea == NULL || nmea[0] != '$') { + return; + } + + // 检查是否为GPGGA或GNGGA语句 + if (strncmp(nmea, "$GPGGA", 6) == 0 || strncmp(nmea, "$GNGGA", 6) == 0) { + GPS_ParseGPGGA(nmea); + } + // 可以添加其他NMEA语句的解析,如GPRMC等 +} + +/** + * @brief 解析GPGGA语句 + * @param nmea: GPGGA语句字符串 + * @retval None + * + * GPGGA格式示例: + * $GPGGA,123519.00,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 + * + * 字段说明: + * 0: $GPGGA + * 1: UTC时间 (hhmmss.ss) + * 2: 纬度 (ddmm.mmmm) + * 3: 纬度方向 (N/S) + * 4: 经度 (dddmm.mmmm) + * 5: 经度方向 (E/W) + * 6: 定位质量 (0=无效, 1=GPS, 2=DGPS, etc.) + * 7: 使用的卫星数量 + * 8: HDOP水平精度因子 + * 9: 海拔高度 + * 10: 高度单位 (M) + * 11: 大地水准面高度 + * 12: 高度单位 (M) + * 13: 差分GPS数据年龄 + * 14: 差分参考站ID + * 15: 校验和 + */ +static void GPS_ParseGPGGA(char *nmea) +{ + char *token; + char *saveptr; + int field_index = 0; + char temp_buffer[32]; + + // 使用strtok_r进行字符串分割(线程安全) + token = strtok_r(nmea, ",", &saveptr); + + while (token != NULL) { + switch (field_index) { + case 1: // UTC时间 + if (strlen(token) >= 6) { + // 解析时间 hhmmss.ss + char hour_str[3] = {token[0], token[1], '\0'}; + char min_str[3] = {token[2], token[3], '\0'}; + char sec_str[3] = {token[4], token[5], '\0'}; + + gps_data.time.hour = (uint8_t)atoi(hour_str); + gps_data.time.minute = (uint8_t)atoi(min_str); + gps_data.time.second = (uint8_t)atoi(sec_str); + + // 解析毫秒(如果有) + if (strlen(token) > 7 && token[6] == '.') { + char ms_str[4] = {0}; + strncpy(ms_str, &token[7], 3); + gps_data.time.millisec = (uint16_t)atoi(ms_str); + } else { + gps_data.time.millisec = 0; + } + } + break; + + case 2: // 纬度 + if (strlen(token) > 0) { + strncpy(temp_buffer, token, sizeof(temp_buffer) - 1); + temp_buffer[sizeof(temp_buffer) - 1] = '\0'; + } + break; + + case 3: // 纬度方向 + if (strlen(token) > 0) { + gps_data.position.lat_direction = token[0]; + // 转换纬度为十进制度 + gps_data.position.latitude = GPS_ConvertToDecimal(temp_buffer, token[0]); + } + break; + + case 4: // 经度 + if (strlen(token) > 0) { + strncpy(temp_buffer, token, sizeof(temp_buffer) - 1); + temp_buffer[sizeof(temp_buffer) - 1] = '\0'; + } + break; + + case 5: // 经度方向 + if (strlen(token) > 0) { + gps_data.position.lon_direction = token[0]; + // 转换经度为十进制度 + gps_data.position.longitude = GPS_ConvertToDecimal(temp_buffer, token[0]); + } + break; + + case 6: // 定位质量 + if (strlen(token) > 0) { + gps_data.position.fix_status = (GPS_FixStatus_t)atoi(token); + } + break; + + case 7: // 卫星数量 + if (strlen(token) > 0) { + gps_data.position.satellites = (uint8_t)atoi(token); + } + break; + + case 8: // HDOP + if (strlen(token) > 0) { + gps_data.position.hdop = (float)atof(token); + } + break; + + case 9: // 海拔高度 + if (strlen(token) > 0) { + gps_data.position.altitude = atof(token); + } + break; + + default: + break; + } + + token = strtok_r(NULL, ",", &saveptr); + field_index++; + } + + // 更新数据有效标志和时间戳 + if (gps_data.position.fix_status > GPS_FIX_INVALID) { + gps_data.data_valid = 1; + gps_data.last_update_tick = HAL_GetTick(); + } else { + gps_data.data_valid = 0; + } +} + +/** + * @brief 将GPS坐标格式转换为十进制度 + * @param coord: GPS坐标字符串 (ddmm.mmmm 或 dddmm.mmmm) + * @param direction: 方向字符 (N/S/E/W) + * @retval 十进制度数 + * + * 示例: + * 输入: "4807.038", 'N' + * 输出: 48.1173 (度) + */ +static double GPS_ConvertToDecimal(const char *coord, char direction) +{ + if (coord == NULL || strlen(coord) < 4) { + return 0.0; + } + + double value = atof(coord); + + // 判断是纬度还是经度(纬度2位度数,经度3位度数) + int degree_digits = (strlen(coord) >= 5 && coord[4] == '.') ? 2 : 3; + + // 提取度数和分钟 + double degrees = (int)(value / 100.0); + double minutes = value - (degrees * 100.0); + + // 转换为十进制度 + double decimal = degrees + (minutes / 60.0); + + // 根据方向调整符号 + if (direction == 'S' || direction == 'W') { + decimal = -decimal; + } + + return decimal; +} diff --git a/User/gps_driver.h b/User/gps_driver.h new file mode 100644 index 0000000..afe67d5 --- /dev/null +++ b/User/gps_driver.h @@ -0,0 +1,150 @@ +/** + ****************************************************************************** + * @file gps_driver.h + * @brief GPS NMEA数据接收和解析驱动 + * @author Your Name + * @date 2026-02-07 + ****************************************************************************** + * @attention + * + * 本驱动用于通过USART3接收GPS模块的NMEA数据,主要解析GPGGA语句 + * 提取时间、经纬度等信息 + * + ****************************************************************************** + */ + +#ifndef __GPS_DRIVER_H +#define __GPS_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "main.h" +#include "usart.h" +#include +#include +#include + +/* Exported types ------------------------------------------------------------*/ + +/** + * @brief GPS定位状态 + */ +typedef enum { + GPS_FIX_INVALID = 0, // 无效定位 + GPS_FIX_GPS = 1, // GPS定位 + GPS_FIX_DGPS = 2, // 差分GPS定位 + GPS_FIX_PPS = 3, // PPS定位 + GPS_FIX_RTK = 4, // RTK固定解 + GPS_FIX_RTK_FLOAT = 5, // RTK浮点解 + GPS_FIX_ESTIMATED = 6, // 估算 + GPS_FIX_MANUAL = 7, // 手动输入 + GPS_FIX_SIMULATION = 8 // 模拟模式 +} GPS_FixStatus_t; + +/** + * @brief GPS时间结构体 + */ +typedef struct { + uint8_t hour; // 时 (UTC) + uint8_t minute; // 分 + uint8_t second; // 秒 + uint16_t millisec; // 毫秒 +} GPS_Time_t; + +/** + * @brief GPS位置结构体 + */ +typedef struct { + double latitude; // 纬度 (度) + char lat_direction; // 纬度方向 ('N' or 'S') + double longitude; // 经度 (度) + char lon_direction; // 经度方向 ('E' or 'W') + double altitude; // 海拔高度 (米) + GPS_FixStatus_t fix_status; // 定位状态 + uint8_t satellites; // 使用的卫星数量 + float hdop; // 水平精度因子 +} GPS_Position_t; + +/** + * @brief GPS数据结构体 + */ +typedef struct { + GPS_Time_t time; // GPS时间 + GPS_Position_t position; // GPS位置 + uint8_t data_valid; // 数据有效标志 (1=有效, 0=无效) + uint32_t last_update_tick; // 最后更新时间戳 +} GPS_Data_t; + +/* Exported constants --------------------------------------------------------*/ +#define GPS_UART_HANDLE huart3 // GPS使用的UART句柄 +#define GPS_RX_BUFFER_SIZE 512 // 接收缓冲区大小 +#define GPS_NMEA_MAX_LENGTH 128 // NMEA语句最大长度 +#define GPS_DATA_TIMEOUT_MS 2000 // 数据超时时间(ms) + +/* Exported macro ------------------------------------------------------------*/ + +/* Exported functions prototypes ---------------------------------------------*/ + +/** + * @brief 初始化GPS驱动 + * @retval HAL状态 + */ +HAL_StatusTypeDef GPS_Init(void); + +/** + * @brief GPS数据接收处理(在主循环中调用) + * @retval None + */ +void GPS_Process(void); + +/** + * @brief 获取GPS数据 + * @param gps_data: 指向GPS数据结构体的指针 + * @retval 1=数据有效, 0=数据无效或超时 + */ +uint8_t GPS_GetData(GPS_Data_t *gps_data); + +/** + * @brief 检查GPS数据是否有效 + * @retval 1=有效, 0=无效 + */ +uint8_t GPS_IsDataValid(void); + +/** + * @brief 获取GPS时间字符串 + * @param buffer: 输出缓冲区 + * @param size: 缓冲区大小 + * @retval None + */ +void GPS_GetTimeString(char *buffer, uint16_t size); + +/** + * @brief 获取GPS位置字符串 + * @param buffer: 输出缓冲区 + * @param size: 缓冲区大小 + * @retval None + */ +void GPS_GetPositionString(char *buffer, uint16_t size); + +/** + * @brief UART接收完成回调函数(在stm32f4xx_it.c中调用) + * @param huart: UART句柄 + * @retval None + */ +void GPS_UART_RxCpltCallback(UART_HandleTypeDef *huart); + +/** + * @brief UART空闲中断回调函数(在stm32f4xx_it.c中调用) + * @param huart: UART句柄 + * @retval None + */ +void GPS_UART_IdleCallback(UART_HandleTypeDef *huart); + +#ifdef __cplusplus +} +#endif + +#endif /* __GPS_DRIVER_H */