1. 项目概述为什么是STM32MP135D如果你最近在关注嵌入式Linux的开发板尤其是那些既想玩转Linux应用又想保留实时控制能力的场景那么ST意法半导体的STM32MP1系列大概率已经进入了你的视野。而STM32MP135D可以说是这个家族里为“入门”和“成本敏感”量身打造的一个明星型号。我拿到这块板子这里指基于STM32MP135D的核心板或开发套件也有一段时间了从最初对着数据手册发懵到成功点亮第一个自定义应用中间踩了不少坑也总结了不少心得。这篇指南就是想把这段从零开始的折腾过程掰开揉碎了讲清楚让你能避开我走过的弯路快速上手。简单来说STM32MP135D是一颗典型的异构多核处理器。它内部包含了一个主频可达650MHz的Arm® Cortex®-A7核心用来运行Linux这样的富操作系统处理复杂的网络、图形界面和文件系统同时它还集成了一个主频可达209MHz的Arm® Cortex®-M4核心这个核心就专攻实时控制比如精准的PWM输出、高速ADC采集或者运行RTOS实时操作系统。这种“A核M核”的架构让它既能胜任智能家居网关、工业HMI人机界面这种需要友好交互和连接能力的任务又能同时控制电机、采集传感器数据一颗芯片搞定过去可能需要两颗甚至三颗芯片才能完成的工作对于精简产品设计、降低BOM成本非常有吸引力。市面上叫“入门指南”的资料很多但很多要么是官方手册的翻译步骤跳跃要么只讲A核Linux忽略了M核的玩法。我这篇指南的目标是“全景式入门”不仅会带你完成最基础的开发环境搭建、系统烧录还会深入到双核通信、独立调试等实际项目必然会遇到的环节。你会发现从让板子“跑起来”到让两个核心“对话起来”每一步都有需要注意的细节。2. 开发环境搭建与工具链选型工欲善其事必先利其器。玩转STM32MP135D第一步不是急着接线而是要把“战场”——也就是你的开发主机环境——给布置好。这里的选择会直接影响你后续开发的效率。2.1 操作系统与主机的选择首先强烈建议使用Linux作为开发主机系统。Ubuntu LTS版本如20.04 22.04是兼容性最广、社区支持最好的选择。Windows当然也可以但需要通过WSL2Windows Subsystem for Linux来获得一个接近原生Linux的环境。直接在Windows上搞你会遇到各种路径、权限和编译工具的问题徒增烦恼。注意如果你坚持用Windows请务必安装WSL2并选择一个Ubuntu发行版。接下来的所有命令行操作都将在WSL2的Ubuntu终端中进行。虚拟机方案也可以但性能会有损耗特别是涉及大量编译时。为什么是Linux因为ST官方提供的核心开发工具——STM32CubeProgrammer、编译工具链、以及构建系统如OpenSTLinux或Yocto Project——其脚本和生态都是围绕Linux/Unix环境设计的。在Linux下一切都会顺畅得多。2.2 关键软件工具安装你需要安装的软件主要分为三类烧录工具、编译工具、源码构建工具。1. STM32CubeProgrammer这是ST官方的多功能烧录和配置工具。它不仅能通过USB、UART或ST-LINK给板子烧写固件包括TF-A、U-Boot、内核、根文件系统还能访问芯片的OTP一次性可编程区域配置安全启动等高级功能。去ST官网下载Linux版本的.tar.gz包解压后运行里面的./SetupSTM32CubeProgrammer-*.linux安装脚本即可。安装后记得将安装目录下的bin文件夹路径添加到系统的PATH环境变量中这样就能在终端直接调用STM32CubeProgrammer命令了。2. 交叉编译工具链你的电脑x86_64架构无法直接编译出能在板子Arm Cortex-A7架构上运行的代码。所以需要一个“交叉”工具链。对于A核的Linux应用开发我们使用gcc-arm-*。ST官方推荐使用Linaro或Arm官方提供的工具链。一个省心的选择是直接从Arm开发者网站下载arm-none-linux-gnueabihf这个工具链。下载后解压同样将其bin目录加入PATH。验证安装在终端输入arm-none-linux-gnueabihf-gcc --version如果能正确输出版本信息说明工具链就绪。3. 构建系统OpenSTLinux 或 Yocto这是入门阶段最复杂但也最重要的一环。STM32MP135D的完整系统镜像包括所有启动固件、内核、设备树、根文件系统不是单个文件而是通过一个构建系统自动生成的。ST提供了基于Yocto Project定制的发行版——OpenSTLinux。你需要获取它的源码仓库Manifest然后通过repo工具同步。# 1. 安装 repo 工具 mkdir -p ~/bin curl https://storage.googleapis.com/git-repo-downloads/repo ~/bin/repo chmod ax ~/bin/repo export PATH~/bin:$PATH # 2. 创建并进入工作目录 mkdir stm32mp1-openstlinux-5.10-dunfell-mp1-21-11-17 cd stm32mp1-openstlinux-5.10-dunfell-mp1-21-11-17 # 3. 初始化 repo 并同步代码 (这是一个例子具体版本和链接请参考ST官网最新文档) repo init -u https://github.com/STMicroelectronics/oe-manifest.git -b refs/tags/openstlinux-5.10-dunfell-mp1-21-11-17 repo sync -j4这个过程会下载数十GB的代码耗时很长请确保网络通畅。同步完成后你就拥有了一个可以定制、编译完整Linux系统的环境。4. 其他实用工具screen或minicom用于通过串口连接开发板查看启动日志和进行命令行交互。这是你与板子沟通的主要窗口。git版本管理必不可少。gcc和make用于编译一些本地主机工具。2.3 硬件连接准备以常见的STM32MP135D开发套件为例你需要准备Type-C数据线用于给板子供电和USB通信烧录和调试。USB转TTL串口线这是最重要的调试线连接板子的UART串口通常是PA9/PA10或PB6/PB7到你电脑的USB口。波特率通常设置为115200。MicroSD卡和读卡器用于制作启动卡。这是最常用的启动方式。网线如果你想通过网络NFS、SSH与板子交互需要连接板子的以太网口。ST-LINK调试器可选但推荐用于M核的代码调试和烧录以及A核的深度调试。连接好后打开串口终端如screen /dev/ttyUSB0 115200给板子上电你应该能看到如海浪般涌出的启动日志。如果什么都没看到请检查串口线连接、端口号和波特率。3. 系统镜像构建与烧录实战环境准备好了接下来就是“做饭”——构建系统镜像并把它“装盘”——烧录到存储设备中。这是让板子活起来的关键一步。3.1 理解启动流程与镜像构成在动手编译前有必要了解一下STM32MP135D从上电到进入Linux系统的“心路历程”。这决定了我们最终要生成哪些文件FSBL (First Stage Bootloader)芯片ROM代码从启动设备如SD卡加载的第一段程序。在ST的体系中这部分由TF-A (Trusted Firmware-A)承担。它负责初始化最基础的硬件如DDR建立安全环境并加载下一阶段。SSBL (Second Stage Bootloader)这就是我们熟悉的U-Boot。它负责更全面的硬件初始化加载设备树Device Tree并从存储设备或网络中加载Linux内核。Linux Kernel操作系统核心带有所需的驱动。Device Tree Blob (dtb)一个描述板级硬件资源哪个引脚是什么功能外设地址在哪的数据文件。内核依赖它来正确驱动硬件。对于STM32MP135D你需要一个匹配你具体板卡设计的.dtb文件。Root Filesystem根文件系统包含Linux运行所需的所有命令、库和应用程序。OpenSTLinux构建系统会帮我们一次性生成所有这些组件。3.2 使用OpenSTLinux构建镜像进入之前同步好的源码目录初始化构建环境cd your-openstlinux-directory source layers/meta-st/scripts/envsetup.sh这个脚本会提示你选择“机器”Machine对于STM32MP135D通常选择stm32mp135d-dk如果你用的是官方DK开发板或类似的配置。然后选择“发行版”Distribution比如openstlinux-weston带Wayland/Weston图形界面或openstlinux-eglfs无桌面直接使用EGLFS图形后端。接下来开始构建bitbake st-image-weston这个命令会启动Yocto的构建过程。st-image-weston是一个目标镜像配方它会自动解析所有依赖从内核、U-Boot到根文件系统全部编译一遍。这是最耗时的步骤在性能一般的电脑上可能需要数小时。首次构建会下载大量软件包请确保磁盘空间充足建议预留100GB以上。构建成功后你需要的所有镜像文件都在build-openstlinuxweston-stm32mp1/tmp-glibc/deploy/images/stm32mp135d-dk/目录下。关键文件包括tf-a-stm32mp135d-dk.stm32TF-A镜像u-boot-stm32mp135d-dk.stm32U-Boot镜像core-image-minimal-stm32mp135d-dk.bootfs.tar.xz内核、设备树等启动文件压缩包core-image-minimal-stm32mp135d-dk.rootfs.tar.xz根文件系统压缩包st-image-weston-stm32mp135d-dk.wic.gz一个完整的、可以直接写入SD卡的磁盘镜像包含所有分区。对于新手我强烈推荐直接使用.wic.gz文件它是最简单的烧录方式。3.3 多种烧录方式详解方式一使用STM32CubeProgrammer通过USB烧录适用于开发阶段这种方式需要板子处于“烧录模式”。通常通过拨动板上的启动开关Boot0让芯片从系统存储器System Memory启动里面存有ST的出厂USB DFU设备固件升级程序。板子断电设置启动开关为DFU模式。通过USB线连接板子的USB-OTG口到电脑。打开STM32CubeProgrammer选择“USB”连接方式点击连接。如果驱动正确会显示设备已连接。在“烧录”页面选择你编译好的tsv文件OpenSTLinux构建目录下通常有一个flashlayout.tsv这个文件定义了哪个镜像文件烧写到存储设备的哪个分区。然后点击“Download”即可。 这种方式可以直接烧写到板载eMMC或SD卡非常方便。方式二直接烧写SD卡镜像最通用如果你有.wic.gz文件这就非常简单了。将MicroSD卡插入电脑读卡器。使用lsblk命令确认SD卡在系统中的设备名例如/dev/sdb务必确认无误写错盘符会清空你的硬盘。解压并烧写gunzip -c st-image-weston-stm32mp135d-dk.wic.gz | sudo dd of/dev/sdb bs1M statusprogress convfsync烧写完成后安全弹出SD卡插入板子的SD卡槽设置启动开关为SD卡启动上电。方式三手动分区与部署深入理解你也可以不用.wic而是手动对SD卡分区然后分别放入各个组件。这有助于理解分区结构用fdisk对SD卡分区。通常需要一个小的FAT32分区约64M用于存放TF-A、U-Boot、内核和设备树一个大的EXT4分区用于根文件系统。将tf-a-*.stm32和u-boot-*.stm32通过dd命令写入SD卡开头的特定扇区偏移量由你的板子决定参考手册。挂载FAT32分区将zImage内核和*.dtb文件拷贝进去。挂载EXT4分区解压rootfs.tar.xz到该分区。烧录完成后连接串口上电你应该能看到TF-A和U-Boot的启动信息最后成功进入Linux登录提示符如stm32mp135d-dk login:。恭喜你的Linux世界已经成功启动了4. 双核开发基础与通信初探系统跑起来只是第一步。STM32MP135D的精髓在于双核协同。接下来我们看看如何让Cortex-M4这个“小兄弟”动起来并让它和Cortex-A7这个“老大哥”对话。4.1 Cortex-M4固件的开发与加载M核的程序开发其实和你玩普通的STM32单片机如STM32F4非常相似。你可以使用熟悉的STM32CubeIDE。创建工程在STM32CubeIDE中选择STM32MP135D作为目标芯片。关键一步是配置工程为“Cortex-M4”核心。IDE会自动生成针对M核的初始化代码。资源分配这是双核开发的核心矛盾。芯片上的外设如GPIO、定时器、ADC、I2C等需要明确分配给A核还是M核使用不能冲突。分配是通过设备树Device Tree和资源管理器如Linux侧的remoteproc框架来完成的。通常在Linux的设备树源文件.dts中会将某些外设节点标记为status “disabled”;或者将其归属到m4_rproc节点下表示这些资源预留给M核。你需要在M核的代码中只使用那些分配给你的外设。编译与生成编译M核工程会得到一个.elf文件包含调试信息和一个.bin或.hex文件纯二进制镜像。如何把这个固件加载到M核并运行呢有两种主流方式方式A由U-Boot加载静态分配将M核的.bin文件打包进Linux的根文件系统。在U-Boot启动时通过环境变量命令将这块二进制数据从存储设备如SD卡加载到指定的DDR内存地址然后启动M核。这种方式简单但M核固件更新需要重新打包根文件系统。在U-Boot命令行或bootcmd中load mmc 0:4 ${loadaddr} /lib/firmware/m4_firmware.bin cp.b ${loadaddr} 0x10000000 ${filesize} bootaux 0x10000000方式B由Linux运行时加载动态加载这是更灵活、更现代的方式。利用Linux内核的remoteproc和rpmsg框架。你需要将M核固件.elf文件放入根文件系统的/lib/firmware/目录下例如命名为m4_firmware.elf。在设备树中正确配置m4_rproc节点并指定固件路径firmware “m4_firmware.elf”;。在Linux系统启动后通过操作sysfs接口来启动和停止M核# 启动M核 echo start /sys/class/remoteproc/remoteproc0/state # 停止M核 echo stop /sys/class/remoteproc/remoteproc0/state启动后使用cat /sys/class/remoteproc/remoteproc0/state可以查看状态running或offline。这种方式允许你在系统运行时动态加载和更新M核固件。4.2 双核通信机制RPMSG两个核心物理上独立如何交换数据ST提供了基于共享内存Shared Memory和处理器间中断IPI的RPMSGRemote Processor Messaging框架。你可以把它理解成双核之间的“消息队列”。基本原理在DDR中划出一块内存区域作为“邮箱”Mailbox或“通道”VirtIO队列。A核和M核都能访问这块区域。当一方想发送消息时就将数据写入共享内存然后通过一个硬件中断IPI“踢”一下对方告诉对方“有你的信”。接收方收到中断后从共享内存读取数据。开发流程M核侧在STM32CubeIDE中你可以使用ST提供的OpenAMP库集成在HAL包中。它封装了RPMSG的初始化和通信API。你需要在M核代码中初始化一个RPMSG通道并定义好通道的名字例如“rpmsg-openamp-demo-channel”和回调函数。当收到A核消息时回调函数会被触发。A核侧在Linux用户空间你可以使用rpmsg-char驱动。加载驱动后会在/dev/下生成一个字符设备文件如/dev/rpmsg0。A核上的应用程序就可以像读写普通文件一样用open(),read(),write(),ioctl()等系统调用来与这个设备文件交互从而实现与M核的通信。一个简单的例子M核程序循环读取ADC然后将数值通过RPMSG发送给A核A核上一个简单的C程序打开/dev/rpmsg0读取数据并打印出来。这就实现了一个典型的“M核采集A核处理/显示”的协作模式。实操心得双核通信的调试是难点。务必先从最简单的“回声测试”Echo开始A核发什么M核原样发回来。确保通信链路通畅后再增加业务逻辑。同时要仔细检查设备树中关于共享内存区域vdev0vring0,vdev0vring1,vdev0buffer的配置地址和大小必须和M核代码中的定义严格一致否则通信必然失败。5. 外设驱动开发与调试技巧当基本的双核通信跑通后你会面临更实际的问题如何驱动具体的硬件外设这里既有A核Linux下的驱动也有M核裸机或RTOS下的驱动。5.1 Linux内核驱动开发入门在A核的Linux环境下驱动开发遵循标准的Linux设备驱动模型。对于STM32MP135D大部分常见外设如USB、以太网、SDMMC、I2C、SPI、UART的驱动都已经由ST或社区维护在内核中了。你的工作主要是通过设备树Device Tree来启用和配置它们。设备树是关键设备树.dts文件是一种描述硬件的数据结构。内核通过解析它来知道“这个板子上有什么设备挂在哪个总线上用什么参数”。例如使能一个I2C接口并连接一个触摸屏i2c1 { pinctrl-names default; pinctrl-0 i2c1_pins_a; status okay; clock-frequency 100000; touchscreen38 { compatible edt,edt-ft5x06; reg 0x38; interrupt-parent gpiof; interrupts 5 IRQ_TYPE_EDGE_FALLING; reset-gpios gpioe 15 GPIO_ACTIVE_LOW; }; };你需要做的就是根据你的实际硬件连接修改或添加这样的设备树节点。修改后需要重新编译设备树bitbake linux-stm32mp -c devshell进入环境后手动编译或整体重新构建并将新的.dtb文件部署到启动分区。对于内核中没有的驱动你需要自己编写。这属于更高级的主题通常需要创建一个内核模块.ko文件实现file_operations结构体中的open、read、write、ioctl等函数并向内核注册你的设备。5.2 M核裸机/RTOS驱动与调试在M核侧驱动开发更接近传统单片机。你可以使用STM32CubeMX生成初始化代码或者直接使用STM32CubeHAL库。重点在于资源隔离。时钟与电源确保A核通过Linux和M核不会同时去配置同一个时钟域或电源域否则会导致系统不稳定。通常这些系统级的资源由A核的Linux统一管理。M核在启动前需要A核为其配置好时钟和电源。这就是为什么推荐使用remoteproc动态加载的原因因为Linux的remoteproc驱动会在启动M核前做好这些准备工作。外设独占如果一个定时器分配给了M核在Linux的设备树中就必须将其status设为disabled或者从aliases中移除防止Linux内核试图去初始化它。调试调试M核的最佳工具是ST-LINK配合STM32CubeIDE或OpenOCD。你需要将ST-LINK的SWD接口连接到板子的调试口。在STM32CubeIDE中可以像调试普通STM32一样设置断点、单步执行、查看变量和寄存器。特别注意当A核也在运行时调试M核可能会受干扰。有时需要在调试前先在U-Boot或Linux中暂停A核例如进入U-Boot后不执行boot命令然后再连接调试器。5.3 系统级调试与性能分析串口日志永远是你最可靠的朋友。确保内核的printk日志级别足够通过dmesg或/proc/sys/kernel/printk调整以便看到驱动加载和运行的详细信息。网络调试为板子配置好网络有线或无线使用ssh登录比串口终端方便太多。你还可以通过网络挂载NFS根文件系统实现主机和开发板之间的文件实时共享极大提升应用开发效率。性能工具Linux下有很多强大的性能分析工具。top/htop看进程vmstat/iostat看系统IOperf可以进行性能剖析。对于M核可以在代码中插入时间戳来计算函数执行时间。示波器和逻辑分析仪当软件调试无法解决问题时比如时序问题、信号完整性问题硬件仪器是终极武器。用它们来测量GPIO波形、I2C/SPI通信时序往往能快速定位问题根源。6. 常见问题排查与避坑指南这里汇总了我自己在折腾STM32MP135D过程中遇到的一些典型问题及其解决方法希望能帮你节省大量时间。6.1 系统无法启动现象上电后串口无任何输出。检查1电源和启动开关。确认供电电压和电流足够启动开关拨到了正确的位置SD卡启动或eMMC启动。检查2串口连接。确认TX/RX线没有接反波特率是否为115200电脑端串口软件配置是否正确。检查3镜像文件。确认烧录的镜像是否针对你的具体板卡stm32mp135d-dk和stm32mp135d-xxx可能不同。尝试使用官方预编译的镜像测试以排除自己编译出错的可能。检查4SD卡或eMMC。尝试换一张SD卡或者用STM32CubeProgrammer重新完整烧写eMMC。现象卡在U-Boot阶段提示“Wrong Ram size”或“DRAM init failed”。原因DDR初始化失败。这是最常见的问题之一。解决这几乎总是设备树dtb选错了。STM32MP135D可能有不同大小的DDR配置比如256MB512MB。你必须使用与你的板载DDR芯片型号和大小完全匹配的设备树文件。仔细查阅你的板子原理图和数据手册确认DDR型号然后在U-Boot启动时通过env print fdtfile查看当前加载的设备树并通过setenv fdtfile correct-board.dtb来修改。6.2 双核通信失败现象remoteproc启动M核失败sysfs状态一直为offline或报错。检查1固件路径和文件名。确认/lib/firmware/目录下的.elf文件名与设备树中firmware属性指定的名字完全一致包括后缀。检查2资源冲突。检查设备树中是否将M核要用的外设如GPIO、定时器、DMA通道正确地分配给了M核例如在m4_rproc节点下声明或者在A核侧设为disabled。使用dmesg | grep -i remoteproc查看内核日志通常会有详细的错误信息。检查3共享内存地址。确认设备树中定义的vdev0vring0、vdev0vring1、vdev0buffer的地址范围与M核固件代码中通常在openamp_conf.h或类似文件中定义的VRING_ADDR等宏的地址完全一致。哪怕有一个字节的偏差通信都无法建立。现象RPMSG通道可以建立但收发数据异常丢包、乱码。检查1缓冲区大小。确保发送的数据长度不超过RPMSG通道的缓冲区大小默认可能只有512字节。如果需要传输大量数据需要设计分片传输协议。检查2同步问题。A核和M核的处理速度可能不同。确保你的应用层协议有足够的流控或确认机制避免生产者过快淹没消费者。6.3 外设无法使用现象在Linux下找不到I2C设备i2cdetect看不到。检查1设备树引脚复用Pinctrl。确认设备树中该I2C控制器对应的pinctrl-0引用了正确的引脚配置组pinctrl节点并且这些引脚没有被其他功能占用。检查2时钟使能。确认设备树中该I2C控制器的父节点比如i2c1的clocks属性正确且status为okay。检查3物理连接。用万用表或示波器检查I2C总线的SCL和SDA线上是否有正确的上拉电压设备地址是否正确。现象M核控制的PWM输出无波形。检查1时钟配置。M核的时钟默认可能较低需要检查并配置相关定时器的时钟源和分频器。检查2引脚复用。在M核的CubeMX配置或代码中必须将对应的GPIO设置为正确的复用功能AF。检查3A核干扰。再次确认该定时器对应的引脚在Linux设备树中已被禁用防止Linux内核在启动时将其初始化为其他功能如普通GPIO输入。6.4 构建与编译问题现象bitbake构建失败报错找不到某个包或下载失败。解决Yocto构建需要从网络下载大量源码包。失败通常是网络问题。可以尝试配置代理或者使用国内的镜像源。另一个常见原因是磁盘空间不足请确保构建目录所在分区有充足空间。现象交叉编译简单的Hello World程序失败提示找不到头文件或库。解决确保你的编译命令使用了正确的交叉编译工具链前缀arm-none-linux-gnueabihf-gcc。并且如果你需要链接开发板根文件系统里的库可能需要通过-I和-L选项指定sysroot路径这通常可以在OpenSTLinux的SDK安装后获得。折腾STM32MP135D的过程是一个典型的“先难后易”的过程。初期会被庞大的系统、复杂的工具链和双核概念所困扰但一旦你打通了环境搭建、系统构建、双核通信这条主线后面很多工作都是在这条主线上的扩展和深化。它给了你一个在单芯片上探索Linux应用和实时控制的绝佳平台无论是用于产品原型开发还是用于学习嵌入式Linux和异构计算价值都非常高。我的建议是不要试图一次性掌握所有东西跟着这篇指南先让板子跑起来再让两个核心说上话然后基于一个具体的、小小的项目目标比如用M核读取温度传感器A核通过Web界面显示去逐个攻克遇到的技术点这样学习起来最有成就感也最扎实。