应采用状态机逐字节解析:IDLE→IN_FRAME→IN_CHECKSUM→END,以$开头、\r\n结尾、\*校验为关键标志,跳过非$行,ISR仅做FIFO入队,主循环解析,严格校验第2字段有效性及*后两位校验和。

怎么从串口数据里准确切出 $GNRMC 语句
单片机资源紧张,不能靠缓存整包再用 strstr 暴力搜索。GPS/北斗模块持续输出多条 NMEA 句子($GNGGA、$GNGLL、$GNRMC 等),必须边收边识别帧头帧尾。
关键点是:以 $ 开头、以 \r\n 结尾,且校验和前有 *(如 $GNRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\r\n)。别只认 $GNRMC 四个字符——有些模块在冷启动时会先发 $PMTK 或 $GPTXT,干扰判断。
- 用状态机逐字节解析:`IDLE → IN_FRAME → IN_CHECKSUM → END`,避免大缓冲区
- 收到
$后开始计数,遇到第一个,就检查前 5 字符是否为GNRMC(注意不是$GNRMC,因为$已作为起始标志) - 跳过所有非
$开头的行,不存、不解析,省 RAM - 确保串口接收中断服务程序(ISR)里只做最简搬运(如 FIFO 入队),解析放主循环,否则超时丢帧
sscanf 解析 $GNRMC 字段为什么常失败
裸机 C++(尤其 ARM Cortex-M 带 Newlib-nano)的 sscanf 默认不支持浮点或宽字符,且 NMEA 字段含空字段(如无效定位时 ,,),直接按格式串硬解极易崩或错位。
典型错误现象:sscanf(buf, "$GNRMC,%f,%c,%f,%c,%f,%c,%f,%f,%d,%c,%f,%c", ...) —— 这根本不能用。NMEA 字段顺序固定但内容可空,且经纬度是 ddmm.mmmm 格式,需手动拆分。
立即学习“C++免费学习笔记(深入)”;
- 改用字符扫描:用
strtok(注意单片机要自己实现轻量版)或指针遍历,分割,对第 3/4/5/6 字段(纬度、NS、经度、EW)单独处理 - 纬度
4807.038要转为十进制度:取整数部分前两位48为度,余下07.038为分,再除 60 得小数度 →48 + 7.038/60 ≈ 48.1173 - 第 2 字段是状态
A(有效)或V(无效),必须检查,否则解析出的坐标毫无意义 - 第 9 字段是日期(
ddmmyy),别误当时间解析;第 1 字段时间是hhmmss.sss,注意小数秒可能缺失
时间戳和经纬度怎么存才不溢出、不丢精度
单片机常用 int32_t 或 float,但 float 在 STM32F1 等无 FPU 芯片上极慢,且 32 位浮点只有约 6~7 位有效数字,存经纬度(如 116.308234)会截断到 116.3082,误差达米级。
更糟的是,直接存 ddmm.mmmm 原始字符串虽安全,但后续计算(如距离、偏航)无法进行。
- 推荐存为
int32_t类型的“百万分之一度”:例如116.308234° × 1000000 = 116308234,范围在 ±180000000 内,int32_t完全够用 - 时间统一转为 UTC 秒数(从 2000-01-01 00:00:00 起算),用
uint32_t可覆盖到 2136 年,比存 BCD 或字符串更易比较和差值计算 - 别用
double—— 大多数单片机编译器把它映射成软浮点,一次加法耗时上百微秒,实时性崩盘 - 校验和
*后的两位十六进制(如6A)必须验证,否则传输出错会导致坐标乱跳
模块输出速率和单片机处理能力怎么匹配
常见 GPS/北斗模块默认 1Hz 输出,但设成 5Hz 或 10Hz 后,串口数据量翻倍,而单片机若还在主循环里做完整解析+浮点运算,必然丢帧。实测某 STM32F4 在 10Hz 下未优化时丢帧率超 30%。
不是模块问题,是处理流程卡在某个环节:比如用 printf 打印调试信息阻塞了接收,或没关掉未使用的 NMEA 句子(模块默认发全部)。
- 用 AT 命令关掉不用的句子:
AT+CGNSPWR=1(开电源)→AT+CGNSTST=0(关测试)→AT+CGNSINF(查状态),最后发AT+CGNSCMD="GPGGA,GPRMC"(仅保留需要的) - 串口波特率至少设为 9600,建议 38400 或 115200(确认模块支持),避免起始位/停止位等待耗时
- 接收缓冲区大小至少为最长 NMEA 句长度(
$GNGSA可达 120 字节)× 2,防止溢出覆盖 - 解析结果用结构体封装,通过标志位通知主逻辑,别在 ISR 里做任何耗时操作
真正难的不是语法解析,是让每一帧都在截止时间前被判定、提取、验证、转换、就绪——中间任何一环用了不可重入函数、没关中断、或依赖未初始化的全局变量,都会在低温/电压波动时突然失效。


















