系统集成与发布:从代码到产线的最后一公里
各位同学,今天我们来聊聊整个移植过程中最“接地气”的一章——系统集成与发布。说白了,就是把咱们前面辛辛苦苦移植好的代码,变成能烧到板子上、能跑起来、能交给工厂批量生产的完整系统镜像。
我个人习惯把这一章叫做“临门一脚”。代码写得再好,如果最后打包出来的镜像有问题,或者产线烧录效率太低,那前面的工作基本白干。我在项目里见过太多次,开发板跑得飞起,一到产线就各种翻车。嗯,今天咱们就把这些坑提前填上。
核心要点:系统集成不是简单的“make”一下完事,它涉及Build系统定制、分区规划、镜像打包、工厂烧录和产测五个环节。每个环节都有门道。
一、Build系统定制:别用默认配置直接上
Android的Build系统,说白了就是一套基于Makefile和Soong的构建框架。但咱们做i.MX平台移植,不能直接用AOSP的默认配置。为什么?因为硬件平台不同,驱动、HAL、设备树都不一样。
我建议你从这几个地方入手:
- BoardConfig.mk:定义板级配置,比如分区大小、内核路径、bootloader参数。我习惯把分区大小写死在这里,避免后续镜像超限。
- device.mk:包含产品相关的模块列表,比如要编译哪些HAL、哪些服务。
- AndroidProducts.mk:声明产品名称和配置文件路径。这里容易踩坑——产品名不要带空格和特殊字符,产线工具会报错。
# 一个典型的BoardConfig.mk片段
TARGET_BOARD_PLATFORM := imx8m
TARGET_NO_BOOTLOADER := false
BOARD_BOOTIMAGE_PARTITION_SIZE := 33554432 # 32MB
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1073741824 # 1GB
BOARD_USERDATAIMAGE_PARTITION_SIZE := 2147483648 # 2GB
小技巧:我在做i.MX8M项目时,发现默认的system分区只有512MB,结果GMS包都塞不下。后来我提前算好所有预装APK的大小,把分区改到1.2GB才搞定。建议你也在BoardConfig里预留20%的余量。
二、产品分区规划:别等镜像装不下才后悔
分区规划,说白了就是给系统各个部分分配存储空间。Android Automotive的分区比手机版更复杂,因为要放车机专属数据。
典型的i.MX Android Automotive分区表长这样:
| 分区名 | 大小 | 用途 |
|---|---|---|
| boot | 32MB | 内核+ramdisk |
| system | 1GB~2GB | 系统文件 |
| vendor | 512MB | 厂商HAL和驱动 |
| product | 512MB | 产品定制APK |
| userdata | 剩余空间 | 用户数据 |
| cache | 256MB | OTA缓存 |
这里有个坑——vendor分区和product分区。Android 10以后强制要求分离,但很多i.MX的BSP默认还是合在一起的。我曾经因为没改分区表,导致vendor镜像写爆,整台车机变砖。嗯,后来我学乖了,每次改分区前先跑一遍df -h看看实际占用。
警告:分区大小一旦定下来,后期改起来非常麻烦。尤其是userdata分区,如果产线已经烧录了第一批设备,再改分区表意味着要重新格式化所有存储芯片。所以前期规划一定要慎重。
三、系统镜像打包:make命令背后的门道
打包镜像,大家都会敲make -j8。但你真的知道它背后干了什么吗?
简单说,Android的打包流程分三步:
- 编译:把源码编译成二进制文件、APK、JAR包。
- 组装:把编译产物按分区目录结构放好。
- 生成镜像:用
mkuserimg或build_image工具生成ext4或sparse格式的镜像文件。
我个人习惯在打包前加一个make clean,虽然慢一点,但能避免增量编译带来的奇怪问题。有一次我偷懒没clean,结果vendor分区里残留了一个旧版本的HAL,导致触摸屏失灵。排查了整整两天才找到原因。
# 打包命令示例
source build/envsetup.sh
lunch evk_8mp-userdebug
make -j8 2>&1 | tee build.log
# 打包完成后,镜像在 out/target/product/evk_8mp/ 下
ls -lh out/target/product/evk_8mp/*.img
避坑指南:我曾经遇到过打包出来的system.img比分区大,烧录时直接报错。后来发现是APK里塞了太多so库。建议你在Android.mk里用LOCAL_MODULE_TAGS := optional来控制哪些模块进系统镜像,别一股脑全编进去。
四、工厂烧录:效率与稳定性的平衡
工厂烧录,是量产环节最头疼的事。开发板烧录可以用fastboot,但产线上不可能一台一台手动敲命令。
常用的烧录方式有两种:
- SD卡烧录:把镜像放到SD卡里,板子从SD卡启动后自动烧录到eMMC。适合小批量。
- USB烧录:用NXP的UUU工具(Universal Update Utility),通过USB把镜像推送到板子。适合大批量。
我推荐用UUU,因为它支持脚本化。你可以写一个uuu.auto脚本,把烧录流程自动化:
# uuu.auto 脚本示例
uuu_version 1.0.0
# 烧录bootloader
SDP: boot -f flash.bin
SDPV: delay 1000
SDPV: write -f u-boot.imx
SDPV: jump
# 烧录分区镜像
FB: ucmd setenv fastboot_dev mmc
FB: flash bootloader flash.bin
FB: flash boot boot.img
FB: flash system system.img
FB: flash vendor vendor.img
FB: flash product product.img
FB: flash userdata userdata.img
FB: flash cache cache.img
# 重启
FB: ucmd reset
注意:产线烧录时,USB线缆质量很重要。我曾经遇到一批设备烧录到一半失败,排查发现是USB线太长导致信号衰减。后来统一换成1米以内的屏蔽线,问题解决。另外,烧录工位最好配UPS,断电一次整条线都得重来。
五、产测:别让坏板子流出工厂
产测,就是生产测试。烧录完系统后,得验证硬件和软件是否正常工作。车机不比手机,出了问题可能涉及安全。
我一般把产测分成两个阶段:
- 硬件自检:上电后跑一段裸机程序,检测DDR、eMMC、网卡、CAN总线等关键硬件。这个阶段不依赖Android系统。
- 系统功能测试:Android启动后,用自动化脚本测试触摸屏、蓝牙、Wi-Fi、GPS、音频等。
这里分享一个我踩过的坑:有一批设备烧录后触摸屏没反应,产测报告全是PASS。后来发现是产测脚本只检查了触摸屏驱动是否加载,没检查实际触控坐标上报。从那以后,我要求产测必须模拟真实操作——比如画一条线,看坐标是否连续。
产测脚本核心逻辑:
# 检查触摸屏
adb shell getevent -c 10 /dev/input/event1 | grep "ABS_MT_POSITION"
if [ $? -eq 0 ]; then
echo "Touch: PASS"
else
echo "Touch: FAIL"
fi
# 检查Wi-Fi
adb shell iw dev wlan0 link | grep "Connected"
if [ $? -eq 0 ]; then
echo "Wi-Fi: PASS"
else
echo "Wi-Fi: FAIL"
fi
知识体系总览
下面这张图,我把整个系统集成与发布的流程串起来了。你可以把它贴在工位旁边,每次做集成时对照着检查。
这张图里,我特意画了一条从产测回到Build定制的虚线。为什么?因为产测发现的问题,往往需要回到Build系统去修改——比如某个驱动没编进去,或者分区大小不够。这个闭环,是量产项目里最常见的迭代模式。