简介:面向 Android 系统开发者与定位模块集成商,这份和芯星通 UM220-III Android 支持包解决的是在 Android 平台上适配北斗/GPS 定位芯片的问题,涵盖 framework 定制、HAL 接口实现、测试验证与协议参考等环节,适合具备一定 Android 系统层开发经验、需要将 UM220-III 接入设备或应用的技术人员。压缩包共 29 个文件,约 2.55MB,以 10 个 Java 源码、4 个 XML 配置、3 个 C 与 2 个 C++ 源文件、2 个头文件及 2 个 mk 编译脚本为主体,另含 aidl 接口定义、测试 apk 与两份 PDF 协议文档,覆盖从框架层到硬件抽象层的完整链路。已有 857 人学习。读者可据此对照修改前后的 framework 与 HAL 代码,理解定位服务的注册与数据获取流程,借助测试应用验证定位速度、精度与信号强度,并结合协议规范与移植协助文档降低集成难度,快速排查适配中的常见问题。
1. 从一块 UM220-III 模块说起:Android 下拿到北斗数据没那么简单
手里有一块和芯星通 UM220-III 模块,想把它接到 Android 板子上跑出定位数据,很多人第一反应是「串口能收到 NMEA 语句就算通了」。真上手才发现不是这么回事:模块上电后串口确实在吐$GNRMC、$GNGGA,可 Android 应用层拿不到,LocationManager里空空如也,日志里连个卫星数都看不见。问题不在模块,而在 Android 的定位链路——从内核串口驱动、HAL 层、到 Framework 的 GNSS 服务,中间任何一环没接上,数据就卡在半路。这套 UM220-III 的 Android 支持包,解决的就是把模块输出的原始 NMEA 数据接进 Android 定位框架这件事,适合正在做车载终端、手持设备、工业平板这类带北斗定位需求的开发者。装不好会怎样?轻则应用层拿不到位置,重则 GNSS 服务反复重启、系统日志刷屏。下面按我实际部署的顺序,把环境、编译、配置、验证和翻车后的救法一条条写清楚。
2. 环境准备与版本对齐:编译前先把这几样钉死
2.1 确认模块型号与固件版本
UM220-III 是支持包对应的硬件基础,动手前先确认手上模块的固件版本。常见做法是通过串口工具发$PCAS06,0*XX查询版本,或者直接看模块丝印批次。固件版本不同,NMEA 输出语句的默认开关状态可能不一样,比如有的批次默认不开$GNGSA,后面 HAL 解析卫星仰角时就少数据。我一般会先把模块单独接到 USB 转串口上,用串口助手确认三件事:波特率(UM220-III 常见默认 9600,也有配置成 115200 的)、输出语句集合、更新率。这三项记下来,后面配 HAL 参数时直接对照,省得来回试。
2.2 Android 源码树与编译环境
支持包要编进 Android 系统镜像,所以你得有一份能完整编译的源码树。版本上,这套包常见适配 Android 8.1 到 Android 11 的 GNSS HAL 接口,Android 12 之后 HAL 接口有调整,直接套用容易在android.hardware.gnss@2.x的接口实现上编译报错。编译环境按 AOSP 官方要求来:Ubuntu 18.04 或 20.04、JDK 对应版本、repo工具、以及至少 200GB 磁盘空间。下面是我习惯先跑一遍的环境自检:
# 检查基础工具链是否齐全 sudo apt-get install -y git-core gnupg flex bison build-essential zip curl \ zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev \ x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip # 确认 JDK 版本(Android 10 及以下常用 OpenJDK 8) java -version # 确认 repo 可用 repo --version这段命令做的是补齐 AOSP 编译依赖。gcc-multilib和lib32ncurses5-dev这类 32 位库容易被漏装,漏了会在编译 host 工具时报cannot find -lz之类的错。JDK 版本要对上源码树要求,Android 10 用 OpenJDK 8,Android 11 开始部分分支要求 JDK 11,装错版本会在soong阶段直接失败。repo是拉源码用的,版本太老会在 sync 时提示 manifest 解析失败。
2.3 支持包目录结构与放置位置
拿到支持包后先别急着往里拷,解压看结构。典型内容包含 HAL 实现源码、Android.bp或Android.mk编译脚本、以及一份gnss.h配置头文件。放置位置有讲究:HAL 实现一般放到hardware/interfaces/gnss/对应版本目录下,或者作为独立 vendor 模块放到vendor/下。我一般会先建一个对照表,把包里的文件和源码树里的目标路径一一对应,避免拷错层级导致编译脚本找不到源文件:
| 支持包内文件 | 目标路径 | 作用 |
|---|---|---|
gnss_hal.cpp | hardware/interfaces/gnss/2.0/default/ | HAL 接口实现 |
Android.bp | 同上 | 编译描述 |
gnss_config.h | device/<vendor>/<board>/ | 串口与波特率配置 |
init.gnss.rc | device/<vendor>/<board>/ | 服务启动脚本 |
这张表不是让你照抄路径,而是提醒你:HAL 源码、编译脚本、板级配置、启动脚本分属不同目录,少放一处就编不过或者跑不起来。板级配置里的串口设备节点(比如/dev/ttyS1)必须和实际硬件连接一致,这个后面配置章节细说。
3. 编译集成:把 HAL 接进系统镜像
3.1 修改板级配置文件挂载串口权限
模块通过串口和主控通信,Android 默认不给普通服务访问串口节点的权限,得在ueventd或init配置里放开。常见做法是在板级的ueventd.<board>.rc里加一行,把串口节点的属主和权限设好:
# 在 ueventd.<board>.rc 中追加,ttyS1 换成实际串口 /dev/ttyS1 0660 system system这行的含义是:设备节点/dev/ttyS1权限设为0660,属主和属组都是system。GNSS HAL 服务一般以system身份运行,这样它才有读写权限。如果串口节点属主是root而服务是system,打开串口会返回Permission denied,日志里表现为 HAL 初始化失败、GNSS 服务起不来。串口号别照抄,得看你板子的实际接线,接错串口的表现是服务起来了但一直收不到数据。
3.2 配置 HAL 的串口参数与 NMEA 解析
支持包里的gnss_config.h是核心配置点,串口路径、波特率、NMEA 语句开关都在这里。下面是我改过的一个典型片段:
// gnss_config.h 关键配置项 #define GNSS_SERIAL_DEV "/dev/ttyS1" // 与实际硬件接线一致 #define GNSS_SERIAL_BAUD 9600 // 与模块固件默认波特率一致 #define GNSS_NMEA_RMC 1 // 启用 RMC,提供时间与经纬度 #define GNSS_NMEA_GGA 1 // 启用 GGA,提供定位质量与海拔 #define GNSS_NMEA_GSA 1 // 启用 GSA,提供卫星参与定位信息 #define GNSS_NMEA_GSV 1 // 启用 GSV,提供可见卫星数GNSS_SERIAL_BAUD必须和模块实际波特率一致,模块是 9600 你写 115200,收到的全是乱码,HAL 解析 NMEA 时校验和永远对不上,表现就是「串口有数据但定位一直无效」。GNSS_NMEA_GSA和GSV这两个开关很多人图省事关掉,结果应用层拿到的卫星数为 0,做信号强度显示的界面直接空白。RMC 和 GGA 是定位必需,别动。改完这些再确认Android.bp里把gnss_config.h的头文件路径加进了include_dirs,否则编译时提示找不到宏定义。
3.3 编译并打包进系统镜像
配置改完就可以编了。先单独编 GNSS HAL 模块验证语法,再整体打包,这样出错时定位快:
# 初始化编译环境 source build/envsetup.sh lunch <board>-userdebug # 先单独编译 GNSS HAL 模块,快速验证 m gnss@2.0-service # 通过后整体打包 make -j$(nproc) snodlunch选的目标要和你的板子匹配,选错目标编出来的镜像刷不进设备。m gnss@2.0-service只编 HAL 服务,几十秒就能出结果,比整体编译快得多,语法错误、头文件缺失在这一步就能暴露。make snod是只重新打包 system 镜像,适合已经刷过底包、只改了 HAL 的场景,比全量make省时间。如果m阶段报undefined reference,多半是Android.bp里shared_libs少列了依赖库,按报错里的符号去查对应库补上。
4. 避坑排查:这几处翻车我踩过不止一次
4.1 串口有数据但应用层无定位
现象:串口助手能看到 NMEA 语句,Android 应用里LocationManager一直返回 null。原因通常是 HAL 解析出的定位结果没通过GnssHal回调上报,或者 NMEA 校验和解析失败被丢弃。解决:先看logcat里 GNSS 相关 tag,确认 HAL 是否收到数据;再抓一段原始 NMEA 用校验和工具核对,UM220-III 某些固件在语句末尾多一个回车,解析器按严格格式匹配就会失败,改解析逻辑容忍\r\n即可。
4.2 GNSS 服务反复重启
现象:logcat里gnss@2.0-service起来几秒就退出,循环往复。原因多是串口打开失败或初始化超时。解决:确认ueventd里串口权限已生效(ls -l /dev/ttyS1看属主),确认gnss_config.h里的串口路径和实际节点一致,再确认模块已上电。还有一种情况是服务启动脚本里oneshot和disabled配错,服务被 init 反复拉起,检查init.gnss.rc的service段。
4.3 编译报 HAL 接口版本不匹配
现象:m gnss@2.0-service报接口方法未实现或签名不符。原因是支持包按某个 Android 版本的 HAL 接口写的,你的源码树版本不同。解决:对照源码树里hardware/interfaces/gnss/下的.hal接口文件,看方法签名差异,按你的版本调整实现;版本跨度大时,优先找支持包内是否有对应版本的适配分支,别硬改。
4.4 定位漂移严重或首次定位极慢
现象:能定位但坐标跳得厉害,或者冷启动要几分钟。原因常见于天线放置和 AGPS 数据缺失。解决:确认天线朝向和接地,模块附近别放大电流器件;首次定位慢可检查是否注入了星历辅助数据,支持包里若有 AGPS 相关配置项,确认其数据通路是否打通。这类问题不是装错了,是环境没配到位,别急着怀疑 HAL。
5. 装完怎么验证:从串口到应用层走一遍闭环
验证不能只看「服务起来了」,得从物理层一路验到应用层。我习惯按下面这个顺序走,任何一环断了都能立刻定位:
# 第一层:确认串口有原始数据 cat /dev/ttyS1 | head -20 # 第二层:确认 HAL 服务在跑 ps -A | grep gnss # 第三层:抓 GNSS 相关日志 logcat -s GnssHal GnssLocationProvider # 第四层:应用层验证(用系统自带的定位测试或自写 demo) # 观察是否回调 onLocationChanged,卫星数是否大于 0第一层cat串口能看到$GNRMC开头的语句,说明硬件链路通;看不到就回去查接线、波特率、权限。第二层确认服务进程存在,不存在看 init 日志。第三层日志里应能看到 HAL 解析出的经纬度和卫星数,如果只有「收到数据」没有「解析成功」,回去查 NMEA 开关和校验和逻辑。第四层是最终验收,应用能拿到Location对象且getSatellites()数量正常,才算真正跑通。
再补一个我常用的对照检查表,把每层的预期结果列清楚,排查时逐行打勾:
| 验证层 | 检查命令 | 正常表现 | 异常时先查 |
|---|---|---|---|
| 物理层 | cat /dev/ttyS1 | 持续输出 NMEA 语句 | 接线、波特率、权限 |
| 服务层 | ps -A | grep gnss | 进程稳定存在 | init 脚本、串口打开 |
| HAL 层 | logcat -s GnssHal | 解析出经纬度与卫星数 | NMEA 开关、校验和 |
| 应用层 | 定位 demo | 回调位置、卫星数 > 0 | Framework 回调链路 |
有个细节值得单独说:验证时别在室内靠窗随便测,UM220-III 在信号弱的环境下首次定位可能很久,容易误判成装坏了。我一般会到室外开阔处,冷启动等满两分钟再下结论。另外,改完配置重新打包后,记得make snod之后把镜像刷进去再测,只编不刷等于没改,这个低级错误我早期犯过,对着旧镜像排查了半天。
从那以后我每次集成这类定位模块,都强制按「串口原始数据 → 服务进程 → HAL 日志 → 应用回调」四层走一遍,任何一层不过就不往下走,省得问题叠在一起分不清是谁的锅。这套 UM220-III 的 Android 支持包按上面的顺序装下来,链路是能打通的,配置项和排查点都在正文里了,照着走一遍基本能复现。希望帮到你。
本文还有配套的精品资源,点击获取