多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

51和STM32实现北斗定位:串口解析、NMEA数据转经纬度

51和STM32实现北斗定位:串口解析、NMEA数据转经纬度 简介基于单片机的北斗定位系统设计文档面向电子、嵌入式及物联网方向学生与开发者参考针对老年人及弱势群体安全监护需求提供一套完整的多功能报警与定位方案。文档以STC12C5A60S2为主控结合UM220北斗模块、LCD12864显示、蜂鸣器与SIM900 GSM通信模块实现位置获取、健康信息病例、过敏史、药品位置显示、报警触发及短信发送等功能。内容覆盖系统整体框架、硬件电路设计、通信协议处理、软件流程等关键环节并详细介绍UM220接收板原理与信息协议、单片机引脚定义、液晶显示工作原理辅以摘要、目录和方案思路结构完整适合作为课程设计或毕业设计蓝本。资源为docx格式仅1个文件体积1.8MB便于离线查阅。已有195人学习适合需要快速理解北斗定位系统软硬件架构的读者深入学习。1. 北斗定位系统最难的不是定位而是把串口数据变成能用信息很多人把基于单片机的北斗定位系统想成“买模块、接天线、上电就能出经纬度”实际做一轮就会发现核心难点根本不在定位算法上因为北斗模块已经完成了卫星信号捕获、解算甚至速度加速度输出单片机要做的只是通过串口把 NMEA-0183 协议报文读进来、切出有用字段、再做展示或存储。真正容易翻车的地方集中在三处串口帧解析的边界条件、上电后首帧定位数据的判别、以及掉电保存与实时显示之间的资源争用。这篇文章面向正在做毕业设计、课程设计或工程原型的开发者用 51 和 STM32 两条最常见的路线把整套方案讲透——从模块选型、硬件接线到代码解析、数据落盘不需要碰射频和基带也能把一个能报经纬度、能存轨迹的北斗定位系统跑起来。全文以“单片机 北斗模块 串口 显示/存储”为骨架所有代码片段都以可直接抄写、按参数修改为目标。2. 定位模块选型与最小硬件系统搭建2.1 单片机选型51 够用STM32 更好扩展北斗定位模块输出的数据量很小——波特率默认 9600 或 115200一条 GNGGA 语句最长约 80 字节每秒输出 1 到 10 次。按这个数据量51 单片机的串口和定时器完全扛得住。STC89C52、STC12C5A60S2 这类增强型 51 不仅价格低、资料多而且烧录方便用 STC-ISP 通过串口直接下载适合教学和课程设计。但要注意STC89C52 的串口只有一个如果板载模块已经占用了串口做下载就需要用跳线切换或者选用带双串口的 STC15、STC8 系列。如果要做轨迹回放、TF 卡存储、LCD 绘图这类功能建议直接上 STM32F103C8T6 或 STM32F407。C8T6 有 3 个串口可以把北斗模块接在 USART2、调试信息走 USART1、显示走 SPI/I2C资源上完全不打架。而且北斗模块的数据解析属于“读一个字节、拼一个字符串、按逗号切分”的流程在 STM32 上用空闲中断加环形缓冲区会比 51 的查询方式省心很多。对比项51 系列STC89C52STM32F103C8T6串口数量1 个 UART3 个 USART解析方式查询或定时器辅助中断 空闲检测 / DMA数据存储外挂 EEPROMAT24C02I2C/SPI Flash TF 卡定位数据刷新率1Hz 可行5Hz~10Hz 也稳定开发成本低资料极多中库函数生态成熟提示如果你做的是“51 单片机 北斗”这个组合务必检查单片机的串口波特率误差。STC89C52 用 11.0592MHz 晶振时 9600 波特率误差为 0但如果用了 12MHz 晶振误差会超过 2%定位模块输出的帧会出现随机丢字节和乱码。2.2 北斗模块选型看协议、看供电、看天线接口市面上主流模块主要有三类中科微 ATGM336H、天工测控Skylab的 SKG12系列、以及兼容 u-blox 协议的 NEO-M8N。这些模块的原厂协议基本都是 NMEA-0183 的变体区别在于输出语句种类和默认波特率。ATGM336H 是国产方案里性价比很高的选择支持 BDS/GPS/GLONASS 多系统联合定位默认输出 RMC、GGA、GSA、GSV 等语句波特率 9600 可调。SKG12 系列默认 9600 波特率也是常见的毕设模块。硬件接线的关键有四个。第一VCC 必须匹配模块要求常用的 ATGM336H 支持 3.3V而很多 5V 单片机系统板需要加电平转换不能直接把 5V 接到 VCC。第二TXD 接单片机的 RXDRXD 接单片机的 TXD交叉连接这是常识但总有人错在这里。第三天线接口分清有源无源无源陶瓷天线直接焊或扣接有源天线必须从模块的 V_ANT 引脚供电否则收星数少得可怜。第四PPS 脉冲引脚秒脉冲如果只是做定位显示可以不接但如果要做时钟同步或测速就需要接到单片机的外部中断引脚上。模块引脚 单片机引脚 VCC ------ 3.3V或经 LDO GND ------ GND TXD ------ RXD单片机 RXD ------ TXD单片机若要配置模块才接 PPS ------ 可选外部中断这是最常见的四线接法。RXD 在只读数据的情况下可以不接但我一般建议焊上因为很多模块出厂波特率不是 9600 或者输出语句被改过需要发送配置指令恢复出厂设置到那时候没有 RXD 就只能拆线了。2.3 串口参数和上电时序的坑北斗模块上电后有大约 0.5~1 秒的启动时间之后开始向串口持续推送语句。模块内部的供电管理对电源纹波比较敏感如果和单片机共用一个电源而电机、继电器或 4G 模块也在工作定位模块可能出现反复重启现象是串口数据每隔几秒断一次。解决办法是给北斗模块单独加一个 LDO如 AMS1117-3.3输入输出各加 10uF 和 0.1uF 电容。模块默认输出的语句有很多条其中对定位显示有用的主要是GNGGA和GNRMC。GGA 包含经纬度、定位质量指示、卫星数、海拔高度RMC 除了经纬度还包含日期时间、地面速度、航向角。两句话在同一个定位点上内容一致区别在于 RMC 更精简推荐解析 RMC如果失效判断RMC 的定位状态位为 V 或 N再去解析 GGA 拿卫星数和海拔。这个策略能避免很多“显示 0.0000”的问题。3. NMEA-0183 协议解析从串口字节流到结构化定位数据3.1 语句结构与字段切分先看一条典型的 GNRMC 语句$GNRMC,081936.000,A,2233.4567,N,11354.1234,E,0.35,45.2,150924,,,D*69按逗号切分各字段含义如下第一个是语句 ID第二个是 UTC 时间时分秒.毫秒第三个是定位状态A 表示有效V 表示无效第四个是纬度度分格式第五个是北纬/南纬标识第六个是经度度分格式第七个是东经/西经标识第八个是地面速度节第九个是航向角。后面的日期、磁偏角等字段在简单系统中可以不处理。解析流程分三步走从串口读到以$开头、以\r\n结尾的完整行校验帧头和语句类型GNRMC、GNGGA按逗号分割并转换成 float校验和是$和*之间的所有字符按位异或结果以十六进制形式放在*后面。如果校验和错误就丢弃整帧不参与解析。这是 NMEA 解析里最容易踩的坑——很多人只判断帧头就往下解析一旦串口波特率误差导致某帧中间丢了一个字符后续所有字段都会错位而且错得毫无规律。static uint8_t calc_nmea_checksum(const char *buf) { uint8_t sum 0; // buf 指向 $ 之后、* 之前的内容 while (*buf *buf ! *) { sum ^ (uint8_t)(*buf); } return sum; }注意这里是从$后面第一个字符开始异或一直到*前为止。很多移植代码里没有判断*的退出条件导致把整行数据全异或了一遍结果和报文尾部自带的校验值永远对不上。3.2 51 单片机下的解析实现51 上内存很紧张不能开一个 256 字节的环形缓冲区去等完整帧。常见做法是使用状态机逐字节处理状态依次为等待$、接收语句体、遇到\r后判断是否完整、然后执行解析。我用这种方式在 STC15W4K32S4 上解析 ATGM336H 的 1Hz 数据RAM 占用可以控制在 128 字节以内。// 状态机接收一帧 NMEA 数据 #define NMEA_BUF_MAX 96 unsigned char nmea_buf[NMEA_BUF_MAX]; unsigned char nmea_len 0; bit nmea_ready 0; void uart_rx_handler(unsigned char ch) { if (ch $) { nmea_len 0; // 新帧开始 nmea_ready 0; return; } if (ch \n nmea_len 0) { nmea_ready 1; // 一帧结束 return; } if (nmea_len NMEA_BUF_MAX - 1) { nmea_buf[nmea_len] ch; } }这个实现里没有处理\r单独出现的情况因为 NMEA 标准的行尾是\r\n串口收到的顺序固定\n前必然有\r。如果你用的是 STC 串口中断注意在中断服务函数里调用上述函数不要在中断里做strtok或sprintf这些函数耗时长会拉低串口接收能力。解析时在主循环里检查nmea_ready如果是 1 就复制缓冲区到临时数组清理标志位再执行字段切分。这个方法叫“双缓冲”核心思想是中断只负责把数据放到缓冲区内主循环取走做计算。如果直接在主循环里读nmea_buf有可能读到半帧数据。3.3 经纬度格式转换度分转度NMEA 输出的纬度是 2233.4567表示 22 度 33.4567 分转换为十进制度数的公式为decimal_degrees degrees minutes / 60.0即22 33.4567 / 60 22.55761167。在 C 语言里处理时我有两个建议其一解析时用整型或长整型保存原始值避免 float 精度损失——比如 2233.4567 先解析成整数 22334567再做除法会得到更稳定的浮点结果其二因为北纬和东经为正、南纬和西经为负必须在解析完方向位后做符号处理否则绘制轨迹时点位会跑到地图的另一边。float convert_nmea_to_decimal(long raw, char direction) { int degrees raw / 100; float minutes (raw % 100) / 60.0f; float decimal degrees minutes; if (direction S || direction W) { decimal -decimal; } return decimal; }这里的参数raw是已经解析出来的整数如 22334567direction是 N/S/E/W 字符。换算函数里不需要考虑负号因为方向位已经决定了正负不要把raw直接转成有符号数再判断那会把 22 度变成负数。3.4 解析代码的五个常见错误第一strtok在多次调用时会修改源字符串如果后续还要用原始帧数据必须复制一份。第二解析时间字段时081936.000要按字符串截取不能直接转 float否则毫秒部分会丢。第三UTC 时间与北京时间相差 8 小时显示时要做时区转换否则用户会以为定位系统“时间错了”。第四纬度 N/S 和经度 E/W 的判断不能用if (dir N)就完事因为模块在弱信号下可能输出空的纬度字段这时候去strtok会拿到NULL直接导致单片机跑飞。第五校验和字段是 ASCII 十六进制字符串对比时要先转换成数值或者用sscanf(buf, %x, check)得到结果再比较。4. 数据显示与轨迹存储按键切换、LCD 显示、TF 卡落盘4.1 显示方案选择OLED 还是 LCD1602解析出经纬度之后最直观的展示方式就是接一块屏幕。LCD1602 只能显示两行显示完整信息需要多页切换适合课程设计因为代码简单、资料多。OLED 12864SSD1306 驱动是更好的选择I2C 接口只用两根线4 行显示能同时看到经纬度、速度、卫星数和状态而且低温环境下液晶会拖影OLED 没有这个问题。STM32 的标准做法是使用 HAL 库的 I2C 或 SPI 驱动 SSD1306显示刷新频率不需要太高——定位数据每秒只更新一次用 10Hz 以上的刷新率纯属浪费 CPU。一个值得注意的点是 OLED 的显存是 1KB128×64÷8在 51 上如果内存不够可以把显存拆成两半每次只刷上半屏或下半屏代价是显示区域要分块规划。void display_location(float lat, float lon, uint8_t satellites) { char line[20]; sprintf(line, lat:%.6f, lat); oled_show_string(0, 0, line); sprintf(line, lon:%.6f, lon); oled_show_string(0, 2, line); sprintf(line, sat:%d, satellites); oled_show_string(0, 4, line); }这段代码里的%.6f保留 6 位小数大约 0.1 米的精度对轨迹记录足够了。但注意 STM32 使用新库如 GCC 的 newlib时printf系列函数默认不支持浮点需要在编译选项里加-u _printf_float如果你用的是 Keil MDK则需要在微库选项里勾选“Use MicroLIB”否则sprintf里的%f会输出空字符串。4.2 按键切换显示页面与状态指示定位状态分为三种未搜索到卫星、已经定位但未锁定V 状态、定位有效A 状态。按键切换建议只做两个功能短按切换页面经纬度页/速度航向页/时间页长按清空轨迹记录。不要设计太多模式否则按键消抖逻辑的复杂度会超过解析本身。// 简单消抖 短按/长按判断 void key_scan(void) { static uint8_t key_cnt 0; if (key_pin 0) { key_cnt; if (key_cnt 50 { // 约 50ms 消抖 current_page; } if (key_cnt 200 { // 长按 200ms 以上执行清空操作 clear_track_file(); key_cnt 0; } } else { key_cnt 0; } }这个代码用计数方式替代延时消抖好处是按键扫描不需要阻塞主循环。计数阈值要根据你的key_scan调用周期调整如果主循环每 1ms 跑一次那key_cnt 50就是 50ms。需要注意的是长按触发后要清零计数器否则松手后再按下还会继续触发清空。4.3 TF 卡存储轨迹数据以表格形式落盘如果要做轨迹记录最实用的存储方案是 SD/TF 卡与 FATFS 文件系统。通过 SPI 接口连接模块上电后初始化 FATFS定位有效时追加一行数据到 CSV 或 TXT 文件形成表格形式存储方便导出后在 Excel 或地图工具中显示。核心代码是FIL fil; FRESULT res; char line[64]; res f_open(fil, 0:/track.csv, FA_OPEN_ALWAYS | FA_WRITE); if (res FR_OK) { f_lseek(fil, f_size(fil)); // 追加写而非覆盖 sprintf(line, %.6f,%.6f,%.1f,%d\r\n, lat, lon, speed, satellites); f_puts(line, fil); f_close(fil); }几点参数说明FA_OPEN_ALWAYS会在文件不存在时创建存在时打开f_lseek到文件末尾是实现追加的关键否则每次写都会从头部覆写。SD 卡写入速度远快于定位频率1Hz 的写入频率完全不是瓶颈但要注意 FATFS 的f_write是按扇区写入的频繁小数据量写入会导致 Flash 磨损不均衡。折中做法是每 10 秒攒一条记录攒满 5 条再批量写入掉电最多丢 5 条但 Flash 寿命可以延长一个数量级。4.4 掉电保存的关键设计北斗定位系统如果用在车上或户外电源可能随时断开。如果不做掉电保存正在写入的文件系统可能损坏TF 卡上会出现无法访问的簇。最简单的方案是接一个大电容如 4700uF并在主控上检测电源电压掉电瞬间先把文件关闭再进入低功耗。STM32 上可以用 ADC 采样 VBAT 分压后的电压低于阈值就执行f_close和挂载卸载操作。51 单片机没有内置 DAC/ADC 的话可以用一个 I/O 口检测电源状态接一个电阻分压到比较器或直接到 I/O用中断触发掉电存储流程。这个细节在答辩时经常被问到也是系统稳定性的体现。5. 实测数据调参与精度验证的实用技巧5.1 定位成功标志和收星数不是一回事ATGM336H 的 GGA 语句第一个字段后面跟着状态位0 表示未定位1 表示单点定位2 表示差分定位。状态位是 1 不代表精度高——在城市峡谷里单点定位的误差可能达到 10 米以上卫星数多也不代表位置准。实践中的判断顺序是先看 GGA 状态位是否为 1 或 2再看 RMC 语句的状态是否为 A最后看水平精度因子HDOP在 GSA 语句里。调试时不要只看串口监视器里的“A”就认为系统正常把模块放在窗台上等待 3 分钟记录首次定位的时间。正常情况下冷启动定位时间在 30~60 秒左右如果超过 5 分钟还处于 V 状态检查天线方向、馈线接头以及模块是否被金属外壳完全屏蔽。5.2 用串口工具做双机对比验证验证单片机解析是否正确有一个土办法电脑上用 USB-TTL 同时接一个北斗模块和单片机的 TXD用串口助手同时接收两份数据电脑上解析的结果和单片机上显示的结果做对比。这个方法能快速区分是模块问题还是代码问题——两边数据一致说明单机解析无误不一致就要检查单片机的波特率配置或解析逻辑。在户外测试时不要只记录静态点的数据要拿着设备走一条闭合路线回到起点后对比首尾坐标。误差在 5 米以内说明动态定位性能合格如果闭合差过大检查是不是天线摆放位置对卫星信号造成了遮挡。闭合测试数据可以作为毕业设计或项目报告的有效结果数据写入文档并附上轨迹图。5.3 提高首次定位速度的三个实用设置模块默认的启动模式是冷启动每次上电都从星历重新搜索。如果系统供电稳定且定位环境固定允许模块进入热启动可以大幅缩短定位时间。使用 ATGM336H 的专用配置指令$PCAS10,1*1C这个指令的含义是设置热启动模式写入后模块会保存星历到内部 RTC 备份区。在代码里单片机上电后延时 500ms 再发送这条指令之后模块会在 1~2 秒内输出有效定位。要注意的是这条指令只对当前供电周期有效如果需要掉电保持需要将模块的 V_BCKP 引脚接到备用电池或大电容上并确保断电后仍有 1.5V 以上的保持电压。另一个常见做法是设置输出语句精简只保留 RMC 和 GGA去掉 GSA 和 GSV减少串口中断的触发频率降低低端单片机的处理压力。指令格式因模块型号而异ATGM336H 对应的配置是设置输出语句类型SKG12 则通过 UBX 或私有协议配置购买模块时务必向卖家索要配置指令表不要把不同品牌的指令混用。第三个是把模块从室内移到窗口附近或室外空旷处首次定位后不要断电保持供电 15 分钟让星历完整下载。本文还有配套的精品资源点击获取
返回列表