简介:面向Android开发者和系统集成商的和芯星通UM220-III定位芯片适配资源包,适用于导航、追踪、物联网等需要高精度北斗/GPS定位的场景。包内围绕该芯片的安卓支持,提供了framework定制代码、HAL层硬件抽象接口、测试APK以及协议和移植协助文档,帮助开发者快速解决UM220-III在Android系统上的集成与定位服务调用问题,同时降低对底层硬件细节的依赖。资源共29个文件,主要包含Java/C/C++/AIDL源码、XML配置、PDF文档和APK测试程序,其中源码和应用可用于功能验证与二次开发,PDF文档则给出协议规范和集成步骤,压缩包仅2.55MB,结构清晰便于检索。已有851人学习。借助其中framework与HAL的修改前后对照文件,可以直观理解Android系统适配定位芯片的关键差异;配合协议规范与移植步骤,开发者能够独立完成环境搭建、功能验证与性能调优,适用于从评估选型到量产落地的多个阶段。
1. 为什么 Android 设备要单独找一份 UM220-III 支持包
很多做车载终端和手持设备的开发者,第一次拿到 UM220-III 模块时,第一反应是“这不就是个串口定位模块吗,直接接上读数据不就行了”。真做起来才发现,Android 系统根本没有统一的串口定位框架,它不会自动识别模块、不会自动把 NMEA 0183 语句解析成坐标,更不会把几个卫星星座的数据交给你的 App。这就是 UM220-III Android 支持包存在的意义:它把串口读写、NMEA 解析、协议定义和常见调参工具打包成一份能直接在 Android 工程里落地的代码库,让你跳过系统定位框架,让运行 Android 的设备和主板上的 UM220-III 模块直接对话。适合车载定位终端、农机导航屏、手持巡检设备这类要用北斗双模定位但又不依赖手机自带 GPS 的产品。这篇笔记就按“包里有什么、怎么接、怎么调、坑在哪”的顺序讲一遍。
2. 拆开支持包:串口通信与协议解析的核心件
支持包不是一份普通的 SDK,它解决的是 Android 到 UM220-III 之间一整条数据通路的问题。这条通路从硬件 UART 引脚开始,经过驱动层、波特率协商、串口权限申请,最后落到 App 里的 NMEA 语句解析。不要一上来就把 jar 包拖进工程,先花半小时把包里的目录结构和文档翻一遍,能省掉后面好几天的排查时间。
2.1 支持包里常见的目录结构与文件作用
一份完整的 UM220-III Android 支持包,目录结构通常长这样:
support_package/ ├── docs/ │ ├── UM220-III_Android_开发指南.pdf │ └── NMEA_协议说明.pdf ├── libs/ │ ├── armeabi-v7a/libserial_port.so │ ├── arm64-v8a/libserial_port.so │ ├── um220_android.jar │ └── um220_config.jar ├── jni/ │ ├── serial_port/ │ │ ├── SerialPort.c │ │ └── SerialPort.h │ └── Android.mk ├── demo/ │ └── UM220_串口调试助手.apk └── README.md这里每一块都有明确分工。libserial_port.so是串口操作的底层实现,封装了打开设备节点、配置波特率、读写字节流这些 Linux 串口操作,Android 上层应用通过 jar 包里的 Java 类调用它。um220_android.jar提供串口设备的封装类和 NMEA 解析类,这是你要在业务代码里直接 import 的部分。docs/里的开发指南和协议说明最容易被忽略,但 UM220-III 输出的语句格式、私有配置指令、波特率范围都以这俩文档为准,网上流传的参数表不一定匹配你手上这个版本。demo/里的调试助手 APK 用来在真机上快速确认模块是否正常工作,属于上手阶段的“标准答案”,拿到支持包先装这个 APK,能出定位就说明硬件通路没问题,后面再谈代码接入。
2.2 为什么 Android 端首选串口直连:接口选型与电平适配
UM220-III 对外输出的定位数据走的是 UART 串口,但 Android 设备能把数据接进来有好几条路:一是把模块直接接主板的 UART 引脚,二是通过 USB-OTG 接 USB 转串口芯片,三是用蓝牙透传模块中转。支持包里的代码和文档基本都围绕第一条路展开,也就是串口直连,因为这是工业设备里最稳妥的做法。蓝牙透传有配对不稳定、数据延迟的问题,USB 转串口则要额外引入驱动芯片,这两条路都会稀释定位数据的实时性。车载终端和手持设备的主板上一般会预留串口,所以串口直连最省事。
硬件上需要确认三件事:波特率、电平、串口设备节点。UM220-III 常见的出厂波特率是 9600,数据位 8、停止位 1、无校验。这里有个容易翻车的点:如果主板上的串口芯片电平是 5V,而模块串口是 3.3V TTL 电平,直接接上轻则乱码,重则烧模块。我一般会先用万用表确认串口座电平,再决定要不要加电平转换芯片。至于设备节点,常见的是/dev/ttyS0、/dev/ttyS1,也可能是/dev/ttyMT0,这取决于 Android 设备使用的 SoC 型号,支持包帮不了你,得看主板方案商提供的串口映射表。
2.3 天线与供电:定位性能的第一道门槛
天线和供电是定位模块最容易出现的“硬件玄学”问题,代码再干净也救不回来。UM220-III 需要一个无源或有源北斗/GPS 天线,支持包文档里一般会注明天线增益推荐值。我见过不少项目,代码和串口都通了,就是搜不到星,最后发现天线只接了信号线,没给有源天线供电,导致内置 LNA 不工作,卫星信号在源头就丢了。
供电方面,UM220-III 定位模块对电源纹波比较敏感,电流需求在冷启动搜星时会短时升高。如果用主板上某个电压不稳的 LDO 供电,会在搜星时出现电压跌落,表现为模块偶发重启或输出语句中断。建议先用稳压电源给模块单独供电,验证天线和定位正常后,再改回主板供电,这样就能区分是电源问题还是天线问题。
3. 接进 Android 工程:从导入库到拿到第一个经纬度
这一章的目标很简单:把支持包里的串口库跑通,在继承某个 Activity 或者 Service 的代码里打开串口,读到以$开头的一行行 NMEA 语句,然后从 GGA 句子里把经纬度提取出来。不要贪多,先跑通这条最小通路。
3.1 最小的 Android 工程结构:JNI 与 jar 如何放
先把支持包里的资源放对位置,Android 工程里的目录结构是这样:
app/ ├── src/main/ │ ├── java/ │ │ └── com/example/um220/ │ │ ├── MainActivity.kt │ │ ├── SerialPortManager.kt │ │ └── NmeaParser.kt │ ├── jniLibs/ │ │ ├── armeabi-v7a/libserial_port.so │ │ └── arm64-v8a/libserial_port.so │ └── AndroidManifest.xml ├── libs/ │ └── um220_android.jar └── build.gradle把libserial_port.so放进src/main/jniLibs/对应的 ABI 目录,把 jar 包放进libs/或通过implementation fileTree(include: ['*.jar'], dir: 'libs')引入。有个细节:现在的 Android 设备基本都是 arm64-v8a,但很多工业主板的 Android 系统还是 32 位用户态,如果只放 arm64 的 so 库,在 32 位系统上会直接崩UnsatisfiedLinkError。稳妥做法是 armeabi-v7a 和 arm64-v8a 都放进去,让系统自己挑。
3.2 用支持包里的串口类打开 /dev/ttyS1
支持包的串口类封装了打开串口、设置波特率、读写字节流的逻辑,典型的调用方式如下:
import com.unicore.um.android.serial.SerialPort; import com.unicore.um.android.serial.SerialPortException; public class SerialPortManager { private SerialPort serialPort; public void open(String devicePath, int baudRate) throws SerialPortException { serialPort = new SerialPort(devicePath, baudRate, 0); } public void close() { if (serialPort != null) { serialPort.close(); } } }这段代码里最核心的就是new SerialPort(devicePath, baudRate, 0)。devicePath是串口设备节点,需要根据主板实际映射填写,常见是/dev/ttyS1;baudRate要和模块出厂配置一致,UM220-III 常见是 9600;第三个参数是校验方式,这里填 0 表示无校验,对应模块默认的数据位 8、停止位 1。如果打开失败,多半是权限问题,比如应用没有root权限或没有ttyS1的读写权限。在支持包的 demo 里,打开串口前通常先执行chmod 666操作,工程代码里如果检测到权限不足,也需要动态申请或依赖系统授予的root权限。
3.3 从字节流里切出 NMEA 句子并按 GGA 解析
串口读出来的是连续字节流,不能直接按行读,因为底层读操作不保证一次返回完整的一行。常见做法是按$到\n切分,再做校验:
class NmeaParser { private val buffer = StringBuilder() fun push(data: ByteArray, size: Int) { for (i in 0 until size) { val c = data[i].toInt().toChar() if (c == '\n') { val line = buffer.toString().trim() buffer.clear() handleLine(line) } else { buffer.append(c) } } } private fun handleLine(line: String) { if (!line.startsWith("$")) return val checksum = extractChecksum(line) if (isValid(line, checksum)) { parseGGA(line) } } }这里的关键点是按\n而不是按固定长度切数据,因为 GPS 模块输出的语句长度不一致,按长度切会把一句完整数据切成两半。handleLine里先校验$开头,再提取行尾*后面的两位十六进制校验值,用异或算法验证合法性。校验通过后才进入parseGGA,避免把串口干扰产生的坏数据当成定位结果。这里没有用 BufferedReader 直接按行读,因为 Android 串口读操作是流式的,自己维护缓冲区更可控。
3.4 判断这次定位是否可用:GGA 状态位与卫星数
解析 GGA 语句是拿到经纬度前的最后一步,GGA 里包含字段足够做初步定位判断。一个典型的 UM220-III 输出如下:
$GNGGA,101533.00,3112.34567,N,12134.56789,E,1,08,1.2,15.5,M,-5.8,M,,*5A解析出来的核心字段可以这样读取:
fun parseGGA(line: String) { val parts = line.split(",") if (parts.size < 6) return val longitude = convertNmeaToDecimal(parts[4], parts[5]) val latitude = convertNmeaToDecimal(parts[2], parts[3]) val quality = parts[6].toIntOrNull() ?: 0 if (quality > 0) { onLocationUpdate(longitude, latitude, quality) } }这里最关键的字段是parts[6],它是定位质量标识符。值为 0 表示无定位,1 表示单点定位,2 表示差分定位。UM220-III 同时输出 GPS 和北斗卫星的观测数据,所以语句前缀一般是$GNGGA而不是$GPGGA,解析时要以GN前缀为准。convertNmeaToDecimal负责把 NMEA 里的度分格式转成度的小数格式,这个转换很容易算错,尤其是南纬和西经的转换,需要注意带上符号。判断定位有效,不只是看有没有经纬度,还要看 quality 和卫星数,卫星数在 fields 的第 8 位,如果一直为 0,基本可以断定天线或信号有问题。
4. 按场景调参:UM220-III 的多模式配置与协议字段
串口通了,定位也能出了,接下来就是让模块按你的场景工作。车载终端需要连续定位和低功耗,手持设备需要快速定位,测绘场景需要更高精度的数据输出,这些需求都落在 UM220-III 的配置和协议选择上。支持包里通常带一个配置工具,但理解背后的参数逻辑更重要。
4.1 定位模式与输出频次:先测静态再测动态
UM220-III 是双模定位模块,可以同时接收北斗和 GPS 信号,但输出频率、定位模式、冷启动行为都可以调整。拿到模块后,我一般会先做一次静态测试,把模块放在屋顶或窗边开阔处,固定不动,观察以下现象:上电后多长时间输出第一条有效 GGA;随后 1 分钟内卫星数是否稳步上升;固定点位的经纬度波动范围是否在 3 米以内。静态测试能排除大部分天线和硬件问题,再去做车载动态测试才有意义。动态测试重点看定位是否连续,是否在过桥、进隧道后有拖尾和跳变。UM220-III 的输出频次常见有 1Hz、2Hz、5Hz 可选,车载场景一般用 5Hz,手持设备用 1Hz 就够,频次越高功耗越大,串口波特率也要相应提高,否则数据会堵在下位机缓冲里。
4.2 解析 GGA 与 RMC:字段含义对照表
GGA 主要给定位核心数据,RMC 除了定位还有时间和航向,两者在工程里各有用途。我习惯把它们整理成下面这张对照表,新接手的人照着这张表解析不会迷路。
| 字段位置 | GGA 含义 | RMC 含义 |
|---|---|---|
| 1 | UTC 时分秒 | UTC 时分秒 |
| 2 | 纬度(度分格式) | 纬度(度分格式) |
| 3 | N/S 方向 | N/S 方向 |
| 4 | 经度(度分格式) | 经度(度分格式) |
| 5 | E/W 方向 | E/W 方向 |
| 6 | 定位状态标识 | 定位状态标识 |
| 8 | 卫星数 | 地面速度 |
| 9 | HDOP 水平精度因子 | 航向角 |
GGA 适合用来做具体的坐标展示和精度判断,RMC 则适合用来做速度和航向计算。车载导航屏显示速度一般用 RMC 的第 8 个字段,单位是节,要转成 km/h 需要乘以 1.852。这里有个容易记混的点:GGA 里parts[6]是定位状态,RMC 里parts[6]也是定位状态,但 GGA 的parts[7]是卫星数,RMC 的parts[7]是完整程度不同的东西。解析时不要沿用同一条字段下标模板,要按语句类型分别处理。
4.3 双模定位参数:北斗与 GPS 一起用的收益和代价
UM220-III 的核心卖点是北斗和 GPS 双模同时接收。双模不是简单地把两家卫星数加起来,它意味着在相同的一颗天空视野内,能参与定位解算的卫星多了,城市峡谷、高架桥下、树林遮挡这类环境下的可用卫星数明显提升。实际的收益表现在:开阔场景下定位精度差别不大,但在遮挡场景下,双模比单 GPS 更容易维持固定解,定位中断次数显著减少。
代价是功耗和首次定位时间有所增加。北斗和 GPS 两个射频通道同时工作,搜星阶段电流比单系统高出不少。如果项目对功耗敏感,可以用配置工具关闭其中一个系统,只保留 GPS 或只保留北斗。我这里建议的参考区间是:双模冷启动一般需要 30 到 40 秒,热启动在几秒内完成;单系统冷启动可能更快一点,但遮挡场景的连续性会打折。这个值受天线、干扰和天空视野影响很大,不要拿模块手册上的指标直接当成验收标准,要以实际静态测试为准。
5. 避坑/常见问题/排查:串口、天线与定位三座山
做定位模块集成,代码反而花不了太多时间,大量时间都耗在串口权限、乱码、天线这几类问题上。下面这几条是我在多个项目里反复踩过的坑,按现象、原因、解决的顺序列出来,排查时照着来。
5.1 打开串口一直拿不到数据流
现象是SerialPort构造器或open方法返回正常,但read线程读到 0 字节,或者直接抛Permission denied。原因是 Android 应用没有/dev/ttyS1节点的读写权限,常见于工业主板上改过的 Android 系统默认不开启串口节点权限。解决分两步:先在 adb shell 里执行chmod 666 /dev/ttyS1测试,能读到数据就说明是权限问题;再在应用里集成支持包自带的su授权逻辑,或者让系统镜像的init.rc在开机时自动给串口节点赋权。不要指望动态申请READ_EXTERNAL_STORAGE这类普通权限能解决问题,串口权限属于系统层权限,必须依赖root或系统级配置。
5.2 串口读到一堆乱码,偶发出现$GNGGA但内容不完整
现象是字节流里能扫到$GNGGA,但后面的经纬度要么缺字段,要么出现一些没法解析的字符。原因大概率是波特率不匹配,尤其是模块被上一手设置成 115200,而你还在用 9600 去读。第二个常见原因是串口电平有问题,5V 电平直接灌到 3.3V 的模块管脚上时会出现信号畸变,导致 bit 错误。先用 demo APK 试各种常见波特率,如果 9600 和 115200 都会乱码,就量一下串口座上模块侧的电压是不是 TTL 电平,而不是 RS232 电平。市场上某些开发板的串口座标的是 RS232,必须转成 TTL 才能接 UM220-III。
5.3 能搜到星,但一直不输出有效定位
现象是日志里能解析出$GNGGA,但质量位一直为 0,卫星数始终在 0 到 3 之间徘徊。原因往往是天线位置不对或者有源天线供电缺失。把模块拿到窗边或者室外,观察卫星数是否明显上升,如果还是只有一两个,就检查天线馈线的焊点有没有开短路。还有个容易被忽略的点:天线必须放在能看到天空的位置,放在金属壳设备内部,再好的模块也搜不到星。可以在代码里把卫星数打到日志里,人工观察天线位置变化时卫星数是否跟着变化,这比单纯看有没有坐标更早暴露问题。
5.4 天线一靠近显示屏就丢星
现象是模块在桌面测试正常,装进整机后卫星数骤降,或者定位状态在 1 和 0 之间跳变。原因是显示屏、触摸屏控制器的电磁干扰耦合到了天线馈线上。解决方向有两个:一是把天线馈线换成屏蔽线,并让馈线远离显示屏驱动电路;二是调整天线位置,尽量让天线与屏幕走线垂直交叉,而不是平行走线。这类干扰问题在支持包里不会有答案,需要硬件上自己做屏蔽设计。遇到定位不稳,先怀疑电磁干扰,不要急着改软件滤波。
5.5 系统休眠后串口挂死,重新打开无效
现象是设备息屏一段时间后,定位数据长时间不更新,杀掉进程重新打开串口还是没数据。原因是 SoC 的 UART 控制器在休眠时进入低功耗状态,串口驱动没有正确恢复,或者模块自身的数据输出被系统休眠拉掉了。解决分两步:在 Android 层面,给串口线程持有的 wakelock,确保休眠时 CPU 还在从串口读数;在系统层面,和设备方案商确认 UART 引脚是否在休眠时被断电。如果只是应用层做开发,最省事的方案是买支持包的demo源码版本,里面通常已经处理了休眠唤醒后重新打开串口的逻辑,直接抄过来用。
6. 进阶验证:把 NMEA 日志变成定位质量报表
最后这一步,是我在项目验收前必做的一件事:不只看 App 界面上有没有坐标,而是把原始 NMEA 日志抓下来,统计定位有效率和卫星数分布,形成一份可以复现的定位质量记录。做法很简单,在串口读取线程里把每一条完整语句追加到本地文本文件,格式如下:
$GNGGA,101533.00,3112.34567,N,12134.56789,E,1,08,1.2,15.5,M,-5.8,M,,*5A $GNGGA,101534.00,3112.34568,N,12134.56790,E,1,09,1.1,15.5,M,-5.8,M,,*5C然后把这份日志用脚本统计:$GNGGA的总条数、质量位大于 0 的条数、卫星数的平均值和最小值。如果统计结果里有效率低于 99%,说明设备在动态跑起来后存在丢数据的问题,这时候就要回查串口波特率是不是太低,或者系统有没有在特定场景下抢占 CPU 导致串口线程失约。我个人的习惯是,无论客户说不说,验收报告里都附带这样一张统计表,它比一张截图有说服力得多。另外,UM220-III 支持包里的文档会提到 PPS 秒脉冲引脚,在车载设备里做时间同步时,可以把 PPS 脚接到主板的 GPIO 上,用来校正系统时间漂移,但这个是更高阶的玩法,前提是先把手上的串口通路和协议稳定跑透。记住一句话:定位模块集成的天花板不是代码,是天线位置和电源质量。希望帮到你。
本文还有配套的精品资源,点击获取