4. Yocto/OpenSTLinux构建产测专用镜像

产测镜像与量产镜像的目标不同:量产镜像追求功能完整、用户体验好;产测镜像追求启动快、体积小、可控性强,并且必须保证上电后自动进入测试流程,不需要人工登录、不需要手动挂载、不需要手动启动脚本。OpenSTLinux 基于 Yocto Project 构建,因此定制产测镜像的本质,是在 Yocto 的配方(recipe)与镜像层(image layer)体系里,把“最小系统 + 产测应用 + 依赖库 + 自动启动服务”组合成一个可重复构建、可版本管理的镜像。

先明确本节要构建的对象。假设目标板为 STM32MP157 系列,使用 OpenSTLinux 的 openstlinux-weston 或 openstlinux-headless 作为基础发行版。产测镜像命名为 st-image-prodtst,最终产出 .wic 或 .ext4 镜像,烧录到 eMMC 或 SD 卡后,系统启动到 prodtst.service,由该服务拉起产测主程序 prodtest,主程序通过串口或网络把测试结果上报给上位机。整个过程不依赖桌面环境,因此选择 headless 发行版更合适。

镜像分层与配方组织

Yocto 构建的核心是层(layer)与配方。建议新建一个独立层 meta-prodtst,与 ST 官方层并列,避免直接修改官方层导致升级困难。层内至少包含三类配方:镜像配方、应用配方、服务配方。镜像配方通过 IMAGE_INSTALL 把应用与依赖装入根文件系统;应用配方负责编译产测程序;服务配方负责安装 systemd 单元并设置开机自启。

下面给出一个可直接参考的目录结构示例:

meta-prodtst/
├── conf/layer.conf
├── recipes-core/images/st-image-prodtst.bb
├── recipes-apps/prodtest/prodtest_1.0.bb
└── recipes-apps/prodtest/files/
    ├── prodtest.service
    └── prodtest.conf

镜像配方 st-image-prodtst.bb 的关键内容如下,它继承 ST 的镜像基类,并显式声明要安装的包:

require recipes-st/images/st-image.inc

SUMMARY = "Production test image for STM32MP1"
LICENSE = "MIT"

IMAGE_FEATURES += "ssh-server-openssh"
IMAGE_INSTALL += "prodtest libgpiod libmodbus python3-core"

# 关闭不需要的组件,减小体积
IMAGE_INSTALL:remove = "weston weston-init"

inherit core-image

这里 IMAGE_INSTALL 中的 prodtest 是应用配方产出的包,libgpiod 用于 GPIO 控制,libmodbus 用于与测试治具通信,python3-core 用于运行部分测试脚本。移除 weston 可以显著减少根文件系统体积,同时避免桌面环境抢占启动时间。

产测应用配方与依赖

应用配方 prodtest_1.0.bb 负责把源码编译并安装到镜像中。若源码放在 files/ 下,可使用 SRC_URI = "file://...";若使用 Git 仓库,则用 SRC_URI = "git://..."。配方中通过 DEPENDS 声明编译期依赖,通过 RDEPENDS 声明运行期依赖,确保依赖库被正确打包进镜像。

SUMMARY = "Production test application"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=..."

SRC_URI = "file://prodtest.c \
           file://prodtest.service \
           file://prodtest.conf"

S = "${WORKDIR}"

DEPENDS = "libgpiod libmodbus"
RDEPENDS:${PN} = "libgpiod libmodbus python3-core"

do_compile() {
    ${CC} ${CFLAGS} ${LDFLAGS} -o prodtest prodtest.c -lgpiod -lmodbus
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 prodtest ${D}${bindir}/prodtest
    install -d ${D}${systemd_system_unitdir}
    install -m 0644 ${WORKDIR}/prodtest.service ${D}${systemd_system_unitdir}/
    install -d ${D}${sysconfdir}
    install -m 0644 ${WORKDIR}/prodtest.conf ${D}${sysconfdir}/
}

inherit systemd
SYSTEMD_SERVICE:${PN} = "prodtest.service"
SYSTEMD_AUTO_ENABLE:${PN} = "enable"

其中 SYSTEMD_AUTO_ENABLE 设为 enable 是自动启动的关键,它会在镜像构建时创建指向 multi-user.target.wants 的软链接,使服务随系统启动。

自动启动服务与启动顺序

产测服务需要在外设就绪后启动,因此单元文件中应声明依赖关系。After= 表示启动顺序,Wants= 表示弱依赖。若产测程序需要网络上报,可加入 network-online.target;若需要 GPIO 就绪,可加入 sysinit.target。下面是一个可用的单元文件示例:

[Unit]
Description=Production Test Service
After=network-online.target sysinit.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/prodtest --config /etc/prodtest.conf
Restart=on-failure
RestartSec=2
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

配置文件 prodtest.conf 用于区分不同工位与测试项,例如指定串口设备、治具地址、超时时间、结果上报地址等。把配置与程序分离,可以在不重新构建镜像的情况下调整测试参数,这对产线快速换型非常重要。

产测专用镜像构建与启动链路 Yocto 构建阶段 meta-prodtst 层 st-image-prodtst.bb prodtest_1.0.bb 镜像内容 最小根文件系统 prodtest + 依赖库 prodtest.service 上电启动阶段 U-Boot → Kernel systemd 启动 prodtest.service 拉起 产测运行与结果上报 读取 prodtest.conf 执行测试项 GPIO/Modbus/串口 结果判定 PASS / FAIL 上报 上位机 日志写入 journal,可通过串口或 SSH 查看;失败时 Restart=on-failure 自动重试

构建、烧录与验证

完成配方后,在构建目录中初始化环境并执行镜像构建。以 ST 官方发布的环境为例,命令如下:

DISTRO=openstlinux-headless MACHINE=stm32mp157f-dk2 source layers/meta-st/scripts/envsetup.sh
bitbake st-image-prodtst

构建完成后,镜像位于 tmp-glibc/deploy/images/stm32mp157f-dk2/ 下,通常包含 .wic、.ext4 和 .tar.xz 等格式。使用 STM32CubeProgrammer 或 dd 命令烧录到 SD 卡或 eMMC。烧录后上电,观察串口输出,应能看到 systemd 启动日志中出现 Started Production Test Service,随后产测程序开始执行。

验证时重点检查三件事:第一,服务是否自动启动,可用 systemctl status prodtest 查看;第二,依赖库是否齐全,可用 ldd /usr/bin/prodtest 检查是否有 not found;第三,测试结果是否正确上报,可在上位机侧确认收到的 PASS/FAIL 记录。若服务未启动,优先检查 SYSTEMD_AUTO_ENABLE 是否生效,以及单元文件是否被正确安装到 /lib/systemd/system/。

产测镜像建议固定版本号并保留构建日志,便于产线出现异常时回溯到具体配方版本。每次修改配方后,重新构建并记录 bitbake -e 中的关键变量,可以快速定位依赖变化。

体积与启动时间优化

产测镜像不需要完整发行版的所有组件。可以通过 IMAGE_INSTALL:remove 移除文档、桌面、多余语言包;通过 PACKAGE_EXCLUDE 排除不需要的包;通过 IMAGE_FSTYPES 只保留实际使用的镜像格式。若启动时间仍然偏长,可检查 systemd 的 systemd-analyze blame 输出,把非关键服务改为按需启动。对于产测场景,通常把启动时间控制在 10 秒以内是比较合理的目标,具体取决于外设初始化耗时。

最后需要强调的是,产测镜像的“最小”不是一味删减,而是保证测试所需能力完整的前提下减少无关组件。产测应用、依赖库、自动启动服务三者缺一不可,配置与程序分离、层与官方层分离,才能让镜像在产线长期使用中保持可维护、可升级、可复现。