2. 开发环境搭建与交叉编译工具链

STM32MP1 系列(如 STM32MP157)是异构多核处理器:Cortex-A7 双核运行 Linux,Cortex-M4 运行实时固件。为 A7 编译 Linux 程序时,不能在开发板上直接编译(资源受限、速度慢),也不能用主机 x86 的 gcc——因为指令集、ABI、动态链接器都不同。因此必须建立一套交叉编译环境:在 x86_64 主机上运行编译器,产出 ARM 32 位(armv7hf)可执行文件,再部署到目标板运行。

本节围绕 STM32MP 官方 SDK 与 Yocto 构建体系,梳理从工具链获取、内核与设备树编译,到根文件系统制作与烧录的完整链路。核心原则是:工具链、内核、根文件系统三者的 ABI 与版本必须匹配,否则会出现“编译通过、运行报错”的典型问题。

一、两条获取路径:官方 SDK 与 Yocto 自构建

ST 官方为 STM32MP1 提供两种环境搭建方式,选择取决于是否需要深度定制。

路径 A:安装预编译 SDK(推荐入门与驱动开发)。ST 发布的 en.SDK-x86_64-stm32mp1-openstlinux-6.1-yocto-mickledore-mp1-v23.06.21.tar.gz 是自解压安装包,内含交叉编译器、sysroot、内核头文件、库和 pkg-config 配置。安装后执行 source environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi,环境变量 CC、CXX、SDKTARGETSYSROOT 即被设置,可直接用 $CC 编译。这种方式省去数小时构建时间,适合内核模块、应用开发。

路径 B:Yocto 构建完整发行版(适合产品定制)。使用 repo 同步 OpenSTLinux 的 manifest,再通过 BitBake 构建。典型流程:

mkdir -p ~/stm32mp1 && cd ~/stm32mp1
repo init -u https://github.com/STMicroelectronics/oe-manifest.git \
  -b refs/tags/openstlinux-6.1-yocto-mickledore-mp1-v23.06.21
repo sync
DISTRO=openstlinux-weston MACHINE=stm32mp15-disco \
  source layers/meta-st/scripts/envsetup.sh
bitbake st-image-weston
bitbake st-image-weston -c populate_sdk   # 生成自定义SDK

其中 MACHINE 决定目标板(stm32mp15-disco、stm32mp15-eval、stm32mp15-ev1 等),DISTRO 决定图形栈(weston 或 openstlinux-eglfs)。首次构建通常需要 2~4 小时、约 100 GB 磁盘空间,产物位于 tmp-glibc/deploy/images/stm32mp15-disco/。

STM32MP1 交叉编译环境与构建产物链路 x86_64 主机(Ubuntu 20.04/22.04) 路径 A:安装预编译 SDK en.SDK-x86_64-stm32mp1-...tar.gz → 解压安装到 /opt/st/ → source environment-setup-... → $CC 直接可用(arm-ostl-linux-gnueabi-gcc) 路径 B:Yocto 自构建 repo init / repo sync(OpenSTLinux manifest) envsetup.sh 设置 DISTRO / MACHINE bitbake st-image-weston bitbake ... -c populate_sdk 产物:deploy/images/<machine>/ 交叉工具链核心组件 arm-ostl-linux-gnueabi-gcc / g++ / ld / objdump sysroot:目标板头文件与库(glibc、libdrm…) 内核头文件:用于编译 out-of-tree 模块 编译 STM32MP157 目标板(Cortex-A7) Linux 内核 zImage 源码:linux-stm32mp-6.1-r0 make ARCH=arm CROSS_COMPILE=arm-ostl-linux-gnueabi- defconfig → menuconfig → zImage + dtbs 设备树 DTB stm32mp157d-disco.dts include stm32mp157.dtsi / stm32mp15xx-*.dtsi make dtbs → stm32mp157d-disco.dtb 根文件系统 RootFS st-image-weston-openstlinux-weston-stm32mp1.ext4 或 tar.xz 解包后自定义 /etc /lib/modules 模块安装:make modules_install INSTALL_MOD_PATH= 烧录到 eMMC / SD 卡 STM32CubeProgrammer + FlashLayout.tsv TF-A(FSBL) → U-Boot(SSBL) → bootfs → rootfs 关键约束:工具链 sysroot 的 glibc 版本 ≥ 目标板 rootfs 的 glibc 版本,否则运行时报 GLIBC_x.xx not found

二、交叉编译器配置与验证

安装 SDK 后,环境脚本会导出关键变量。以 OpenSTLinux 为例,工具链前缀为 arm-ostl-linux-gnueabi-,目标三元组为 arm-ostl-linux-gnueabi,浮点 ABI 为 cortexa7t2hf-neon-vfpv4(硬浮点 + NEON)。验证方法:

source /opt/st/stm32mp1/4.2-snapshot/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
echo $CC
# arm-ostl-linux-gnueabi-gcc -mthumb -mfpu=neon-vfpv4 -mfloat-abi=hard ...
$CC -v          # 查看 gcc 版本与 sysroot
$CC hello.c -o hello
file hello      # 应显示 ELF 32-bit LSB, ARM, EABI5

若 file 输出为 x86-64,说明环境变量未生效或误用了主机 gcc。另一个常见坑是 pkg-config:交叉编译时必须使用 SDK 提供的 wrapper,否则会去主机 /usr/lib 找库。SDK 已通过 PKG_CONFIG_SYSROOT_DIR 和 PKG_CONFIG_LIBDIR 处理,直接调用 $PKG_CONFIG 即可。

三、内核源码获取与编译

内核源码有两种来源:Yocto 构建目录下的 tmp-glibc/work-shared/stm32mp15-disco/kernel-source/,或 ST 单独发布的 linux-stm32mp-6.1-r0.tar.xz。后者更适合独立开发。编译步骤:

tar xf linux-stm32mp-6.1-r0.tar.xz
cd linux-stm32mp-6.1-r0
make ARCH=arm CROSS_COMPILE=arm-ostl-linux-gnueabi- defconfig
# 如需裁剪:make ... menuconfig
make ARCH=arm CROSS_COMPILE=arm-ostl-linux-gnueabi- -j$(nproc) zImage dtbs modules
make ARCH=arm CROSS_COMPILE=arm-ostl-linux-gnueabi- \
     INSTALL_MOD_PATH=../rootfs modules_install

产物:arch/arm/boot/zImage、arch/arm/boot/dts/stm32mp157d-disco.dtb、arch/arm/boot/dts/stm32mp157d-disco-m4.dtb(M4 固件场景)。defconfig 是 ST 针对 STM32MP1 的默认配置,已启用 STM32 系列驱动(如 stm32-dwmac、stm32-usart、stm32-i2c)。

四、设备树编译与修改

设备树描述硬件资源,是驱动与硬件之间的“接线图”。STM32MP157D-DK1 的板级 DTS 位于 arch/arm/boot/dts/stm32mp157d-dk1.dts,它 include 了 stm32mp157.dtsi(SoC 级)和 stm32mp15xx-dkx.dtsi(板级公共部分)。修改外设(如启用 I2C4、配置 GPIO 中断)后,只需重新编译 dtbs:

make ARCH=arm CROSS_COMPILE=arm-ostl-linux-gnueabi- dtbs
# 反编译验证
arm-ostl-linux-gnueabi-dtc -I dtb -O dts \
  arch/arm/boot/dts/stm32mp157d-dk1.dtb -o check.dts

设备树与内核版本强绑定:6.1 内核的 DTS 不能直接用于 5.15 内核,因为 stm32mp151.dtsi 中的节点属性可能已变化。修改前建议先 git log 查看该文件的变更历史。

五、根文件系统制作与烧录

根文件系统可直接使用 Yocto 生成的 .ext4 镜像,也可基于 tar.xz 自定义。自定义时需注意:/lib/modules/$(uname -r)/ 必须与目标内核版本一致,否则 insmod 会报 invalid module format。检查方法:

# 目标板
uname -r
# 主机查看模块 vermagic
arm-ostl-linux-gnueabi-modinfo mydrv.ko | grep vermagic

烧录使用 STM32CubeProgrammer,通过 USB DFU 或 UART 连接。FlashLayout 文件(FlashLayout_sdcard_stm32mp157d-dk1-optee.tsv)定义了分区顺序:FSBL(TF-A)→ FIP(U-Boot + OP-TEE)→ bootfs(zImage + dtb)→ rootfs(ext4)。执行:

STM32_Programmer_CLI -c port=usb1 \
  -w FlashLayout_sdcard_stm32mp157d-dk1-optee.tsv

烧录完成后,串口(115200 8N1)应看到 TF-A、U-Boot、内核依次启动,最后进入 Weston 或登录提示符。若卡在 U-Boot,检查 FIP 分区是否完整;若内核 panic 提示 VFS: Unable to mount root fs,检查 rootfs 分区偏移与 bootargs 中的 root= 参数是否匹配。

建议在首次搭建时保留一份“已知可用”的 SDK 与镜像组合,后续修改内核或设备树时,先替换单个组件(如只换 dtb)验证,再整体更新,便于快速定位问题来源。