STM32读取TESEO LIV3FL时GPGGA消息过滤异常问题排查
问题原因分析
1. NMEA帧边界处理缺失
NMEA每条消息都是以\r\n结尾的完整帧,你之前的代码应该是在数据流中直接匹配$GPGGA,没等整帧接收完成就输出,导致下一条消息的开头被错误拼接在当前GPGGA消息后,出现内容错乱。
2. 重启乱码&数据截断的诱因
- 外设配置不同步:重启后初始化顺序错误,导致UART/I2C的波特率、时钟配置未同步,直接引发乱码
- 缓冲区残留旧数据:重启后未清空接收缓冲区,新数据与残留的旧数据混合,造成乱码
- I2C读取不完整:GNSS模块分批发送数据,若每次仅读取部分字节,剩余数据会留在I2C缓冲区中,导致后续帧错位、数据截断
解决办法
1. 先收整帧再过滤,避免边收边处理
核心做法:
用环形缓冲区暂存I2C接收的所有字节,从中提取以\r\n结尾的完整NMEA帧,再判断是否为$GPGGA消息,确认后再输出。
代码修改示例(基于HAL库):
#define RING_BUF_SIZE 512 uint8_t ring_buf[RING_BUF_SIZE]; uint16_t buf_head = 0; uint16_t buf_tail = 0; // I2C中断接收完成回调 void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) { // 将接收字节存入环形缓冲区 ring_buf[buf_head] = recv_byte; // 替换为实际接收的字节变量 buf_head = (buf_head + 1) % RING_BUF_SIZE; // 开启下一次I2C中断接收 HAL_I2C_Master_Receive_IT(hi2c, GNSS_I2C_ADDR, &recv_byte, 1); } // 帧处理函数,放在主循环中调用 void parse_nmea_frames(void) { static uint8_t frame_buf[128]; // NMEA单帧最长约80字节,留冗余空间 static uint16_t frame_len = 0; while(buf_head != buf_tail) { uint8_t byte = ring_buf[buf_tail]; buf_tail = (buf_tail + 1) % RING_BUF_SIZE; if(byte == '\n') { // 完整帧接收完成 frame_buf[frame_len] = '\0'; // 过滤$GPGGA消息 if(strstr((char*)frame_buf, "$GPGGA") != NULL) { HAL_UART_Transmit(&huart2, frame_buf, frame_len, HAL_MAX_DELAY); HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", 2, HAL_MAX_DELAY); } frame_len = 0; // 重置帧缓冲区 } else { if(frame_len < sizeof(frame_buf)-1) { frame_buf[frame_len++] = byte; } else { // 缓冲区溢出,直接重置避免异常数据 frame_len = 0; } } } }
2. 修复重启乱码问题
- 调整初始化顺序:先初始化系统时钟(RCC),再初始化I2C和UART,确保外设时钟配置稳定
- 初始化时清空缓冲区:在main函数初始化阶段,手动清空所有缓冲区,避免旧数据干扰
// 清空环形缓冲区 memset(ring_buf, 0, RING_BUF_SIZE); buf_head = 0; buf_tail = 0; // 清空帧缓冲区 memset(frame_buf, 0, sizeof(frame_buf)); frame_len = 0; - 核对I2C速率:TESEO LIV3FL默认I2C速率为100kHz或400kHz,检查STM32的I2C初始化配置,确保两者速率匹配
3. 解决数据截断问题
- 增大缓冲区容量:若GNSS数据发送速率较高,可将环形缓冲区大小调整为1024字节,避免数据溢出丢包
- 批量读取I2C数据:先查询GNSS模块I2C缓冲区的可用字节数,一次性读取所有数据,减少中断开销,避免漏读
// 批量读取可用数据,替代单字节接收 uint32_t avail_bytes = HAL_I2C_GetRxDataSize(&hi2c1); if(avail_bytes > 0) { uint8_t temp_buf[32]; HAL_I2C_Master_Receive(&hi2c1, GNSS_I2C_ADDR, temp_buf, avail_bytes, HAL_MAX_DELAY); // 将批量数据存入环形缓冲区 for(uint32_t i=0; i<avail_bytes; i++) { ring_buf[buf_head] = temp_buf[i]; buf_head = (buf_head + 1) % RING_BUF_SIZE; } }
验证步骤
- 烧录修改后的代码,重启设备,检查串口输出是否无乱码
- 等待GNSS模块锁定卫星后,确认仅输出完整的
$GPGGA消息,无其他内容拼接 - 长时间运行设备,验证是否不再出现数据截断或帧错位问题
内容的提问来源于stack exchange,提问作者Airlangga Fidiyanto
相关产品推荐
相关产品推荐

