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/。
二、交叉编译器配置与验证
安装 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= 参数是否匹配。