ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

把STM32MP135DK当单片机玩:M4裸机脱机跑LED全流程

把STM32MP135DK当单片机玩:M4裸机脱机跑LED全流程 刚拿到 STM32MP135DK 这块板子的时候我的第一反应是“又得先养一套 Linux 系统”默认出厂 SD 卡插进去串口能看到 U-Boot再跑起完整 Linux 也没问题。但真到做项目时问题就来了我只想点个灯或者写一段类似 51 单片机那样的循环逻辑凭什么非要先烧系统、配 Yocto、搞根文件系统后来翻了 ST 的参考手册才发现STM32MP135 里远不止一颗 1GHz 的 Cortex-A7它内部还藏着一颗最高 209MHz 的 Cortex-M4。这颗 M4 并不是摆设完全可以当成 STM32F4 这样的普通单片机来写裸机程序操作 GPIO、跑 PWM、挂 RTOS统统不依赖 Linux。更妙的是让 A7 侧的 U-Boot 在启动阶段把 M4 固件从 SD 卡读进 DDR再释放并启动 M4 内核整个开发板就能实现真正的“脱机运行”。也就是说编译好的 LED 程序放进 SD 卡上电自动执行不连电脑、不接调试器也不需要往芯片里烧死固件。这篇文章就把这个玩法从头到尾拆开讲为什么 MP135 能这么用、CubeIDE 工程怎么建、U-Boot 怎么从 SD 卡加载 M4 程序、如何固化启动脚本、以及我实操中踩过的坑。适合手里有 MP13 系列开发板、又暂时不想折腾大型 Linux 镜像的人也适合刚接触 MPU 但依赖 CubeMX 生态的嵌入式工程师。1. 为什么能把 MP135DK 当成单片机玩1.1 它不是 MCU但 M4 核心是真舵机STM32MP135 属于异构多核处理器默认主视角都集中在 Cortex-A7 上毕竟一颗 1GHz 的 A7 跑 Linux 才是它的“本职工作”。但芯片内部其实还集成了一颗 Cortex-M4这颗 M4 和传统 MCU 里的核心本质相同只是被放到了 MPU 里和 A7 共享外设控制器、DDR 控制器和中断控制器。这意味着你写 M4 程序时的体验和普通 STM32F4 几乎没区别。可以用 HAL 库、可以操作寄存器、可以跑裸机任务也可以移植 FreeRTOS。由于它和 A7 共享同一套存储体系M4 代码甚至可以直接放在 DDR 里执行内存空间比一般片内 SRAM 大得多这反而成了优势。但它的“单片机属性”不是默认打开的。芯片上电后Cortex-M4 内核默认处于复位状态需要由 A7 侧引导代码通常是 U-Boot或者 Linux 里的 remoteproc 框架把它“解除复位”并加载固件。所以我们的核心思路就变成了让 A7 只扮演引导器的角色U-Boot 起来之后不启动 Linux而是从 SD 卡把 M4 固件搬到指定内存再启动 M4。1.2 脱机运行的完整链路SD → ROM → FSBL → U-Boot → M4要理解“脱机”二字得先清楚 MP13 的启动流程。芯片复位后内部 BootROM 根据 BOOT 引脚电平决定从哪个介质读取第一段引导代码。如果配置为 SD 卡启动BootROM 会从 SD 卡的 FSBL 分区读取 U-Boot SPL这段代码完成最基础的 DDR 初始化和时钟初始化然后加载 FIP 固件包括可信固件 TF-A 和真正的 U-Boot 主程序。U-Boot 主程序跑起来之后默认会尝试在 bootfs 分区里查找 boot.scr 脚本或者启动 Linux 镜像。我们要做的就是在这一层插入自定义逻辑从 SD 卡读取 M4 固件文件加载到指定 RAM 地址随后释放 M4 的复位让 M4 开始执行裸机代码。这里有个关键点为什么必须由 A7 先启动因为 M4 的裸机程序通常放在外部 DDR 里而 DDR 初始化是在 A7 引导阶段完成的。也就是说即便你只想用 M4 点灯A7 侧的引导代码仍然必须跑起来它负责给 M4 “铺好路”。理清这条链路后面所有操作逻辑就顺了。1.3 这套玩法适合谁如果你手头的任务只是“周期翻转一个 GPIO”“驱动一个小型传感器”“跑一段实时控制算法”完全没必要把 Linux 拉下水。用 MP135DK 当单片机玩核心收益是能复用成熟但复杂的 MPU 硬件外设同时保持 MCU 级别的开发效率。当然也要承认边界。这套方式不是官方主推的 Linux M4 协同开发模式A7 侧只做了极简启动相当于一个“高级 bootloader”。M4 程序里不会被 Linux 动态管理固件升级完全靠替换 SD 卡里的文件完成。如果你的应用最终要和 Linux 生态深度整合比如 Web 服务、视觉处理、AI 推理那还是老老实实跑完整系统再用 remoteproc 加载 M4 更稳妥。2. 动手前的软硬件清单2.1 硬件准备与板载资源确认这次实验我用的是 ST 官方 MP135DK 开发板典型配置单核 A7 1GHz M4 209MHz、带 LCD 显示屏接口、以太网口、USB Host/Device、SD 卡槽、板载 ST-LINK 调试器。做“脱机跑 LED”实验其实硬件要求不高MP135DK 开发板一块。一张 Micro SD 卡官方原始 Linux 镜像卡最好后续操作会省很多事。USB 转 Type-C 数据线用于连接 ST-LINK 给板子供电并查看串口日志。可选一张 USB 读卡器方便把 SD 卡插到电脑上改文件。动手的第一步不是写代码而是去官网下载 MP135DK 的《用户手册》和《数据手册》把原理图翻出来。重点确认两个信息BOOT 拨码开关的位置与默认状态、板载用户 LED 的 GPIO 连接。以我们自己操作的这块板为例板上用户可编程 LED 丝印标号和 M4 可访问的 GPIO 引脚在原理图里都能查到。这里必须强调一句不同批次或版本的人板子引脚可能存在差异通电之前先看原理图别照抄别人的工程就当完事了。2.2 软件工具链选型STM32CubeIDE官方集成开发环境自带 GCC 交叉编译工具链创建 M4 裸机工程最方便。STM32CubeProgrammer虽然本方案不依赖它烧写但偶尔看链接文件、擦空卡、调试 BOOT 配置时会用到。u-boot-tools里面提供 mkimage 工具用于把 boot.cmd 脚本打包成 boot.scr。串口终端工具我用的是 minicomWindows 下用 PuTTY 也行主要看 U-Boot 日志。这里多说一句工具链背后的选择逻辑。很多人上来就在 Linux 主机里配一套 buildroot 或 Yocto但“把 MP135 当单片机”玩法的优势恰恰是能完全活在 STM32CubeIDE 里。IDE 自动生成链接脚本、启动文件、HAL 驱动你只需要关心用户代码和 U-Boot 侧的几个命令。2.3 SD 卡与 BOOT 启动模式拿到手的一张官方 MP135DK SD 卡默认已经被刷成 Linux 启动镜像。它内部用的是 GPT 分区表一般包含 fsbl1、fsbl2、fip、bootfs、rootfs 等分区。我们不需要重新分区也不需要重新下载整卡镜像只要往 bootfs 分区里放 M4 固件和启动脚本即可。BOOT 拨码开关通常有 4 位对应 BOOT0 ~ BOOT3芯片复位时 BootROM 依据这组电平选择启动介质。具体组合和丝印位置要查手册但在默认出厂状态下MP135DK 一般配置为 SD 卡启动。如果你曾经改过拨码去搞 USB DFU 烧写记得先拨回 SD 卡启动状态。最容易踩的坑就是这个明明 SD 卡里啥都对上电后串口却没有任何 U-Boot 输出十有八九是 BOOT 拨码不对。3. 用 CubeIDE 创建 M4 裸机工程3.1 新建工程时的几个关键选择打开 STM32CubeIDE新建 STM32 Project器件选择 STM32MP135F-DK。如果无法选择 MP13 系列说明本地 CubeMX 包还没有安装对应的 MP13 支持包需要先在 Help 里更新固件包。工程向导会让你在 Cortex-A7 裸机、Cortex-M4 裸机、Linux 应用开发这类模板里做选择。这里直接选 Cortex-M4 裸机即可不要勾选设备树或 Linux 相关组件。选完之后IDE 会生成一个面向 Cortex-M4 的独立工程启动文件和链接脚本都已经按 MP13 的 M4 拓扑配置好了。重点观察链接脚本不同版本生成的 M4 固件基地址并不完全一样。有些工程把代码放在 DDR 保留区域有些放在 MCU SRAM。后面 U-Boot 加载时我们会选择 ELF 文件交给 rproc 框架解析它会按照 ELF 段把代码和数据放到对应地址。所以在 CubeIDE 里我们基本不用手算加载地址但必须知道自己代码最终落在哪里避免与 U-Boot 或 Linux 预留区冲突。3.2 点亮板载 LED 的工程配置M4 裸机系统初始化流程和 STM32F4 很像。在main.c里需要完成 HAL_Init、时钟配置、GPIO 初始化然后进入主循环翻转 LED 引脚。时钟配置是容易被忽略的地方。MP13 的 M4 支持最高 209MHz 运行CubeIDE 会基于 HSE 或者 HSI 经过 PLL 得到一个系统时钟。建议在 CubeMX 的 Clock Configuration 页面直接确认 SYSCLK 是否为 209MHz并留意 M4 与 A7 GPIO 时钟域是否同步。点灯这种简单任务不会暴露性能问题但如果你后面接定时器、串口或 ADC时钟树不清爽会让我们调到头大。GPIO 部分我在工程里选择的是板载用户 LED 对应的引脚。由于 M4 和 A7 共享大部分 GPIO 控制器理论上这个引脚可能同时被 U-Boot 或者 Linux 设备树占用。但在“只跑 U-Boot 不跑 Linux”的场景下我们不需要操心设备树只要确认 U-Boot 没有在启动阶段把对应引脚反复拉低即可。一个可行的main.c主循环代码逻辑如下#include main.h int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); while (1) { HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); HAL_Delay(200); } }这段代码看着和 F4 开发板的点灯程序几乎一样这也是这个玩法最吸引我的地方只需要把目标器件换成 MP135F-DK生成初始化代码剩下的逻辑完全复用 MCU 开发经验。编译后会在工程 Debug 目录生成.elf文件这个文件就是 SD 卡所需的 M4 固件。3.3 链接脚本里的坑第一次编译生成 M4 工程时我被链接脚本的报错卡了一阵。主要原因是 CubeIDE 自动生成的 M4 链接脚本里代码段起始地址如果和调试器下载配置不匹配构建阶段不会报错但上电后极易跑飞。尤其当你用的是极简 U-Boot 加载方式而不是 ST-LINK 直接下载时这个问题会被放大。我的建议是每次生成工程后先打开.ld文件确认 MEMORY 区域里RAM或DDR的起始地址与你预期一致。手上这块板的例子里M4 代码被链接到 DDR 的一段保留区域这段区域由 U-Boot 通过内存映射留给 M4Linux 不使用它。这样既保证了 M4 能享受大容量内存又不会在后续升级 Linux 时互相踩踏。4. 让 U-Boot 把固件装进 M44.1 手工验证先走一遍 U-Boot 命令工程编译通过后第一步不要急着做自动化启动脚本先在 U-Boot 命令行里手动验证整条链路。把生成的.elf文件拷贝到 SD 卡的 bootfs 分区。如果你的电脑能直接挂载 FAT 分区直接拖进去如果 bootfs 是 ext4 格式就在 Linux 主机上用dd配合挂载或者索性把 bootfs 分区格式化为 FAT32。开发板上电在串口终端进入 U-Boot 控制台开机时按任意键打断自动启动。先用part list mmc 0查看 SD 卡分区确认 bootfs 分区的编号不同镜像布局可能有差异不能死记。看到分区后常用命令序列如下# 确认 SD 卡分区编号例如 bootfs 是 mmc 0:4 part list mmc 0 # 查看 bootfs 分区根目录 fatls mmc 0:4 / # 把 M4 固件读取到 DDR 的临时加载地址 setenv m4_addr 0xC4000000 fatload mmc 0:4 ${m4_addr} m4-led.elf # 初始化 remoteproc 框架 rproc init # 加载 ELF 固件到 M4 实例 0 rproc load 0 ${m4_addr} ${filesize} # 启动 M4 rproc start 0如果一切顺利命令执行完的瞬间开发板上的 LED 就开始按主循环里的节奏闪烁。这里rproc load会解析 ELF 结构并不需要你手动指定目的地址固件会按照 ELF 段中的地址属性搬到正确位置。如果手动链路跑不通问题大概率出在三个地方文件没放在正确的 bootfs 分区、固定地址被 U-Boot 自己占用、或者生成的 ELF 不是 Cortex-M4 的可执行文件。先逐个排除再推进到下一步。4.2 写 boot.scr 实现完全脱机手动敲命令只能算调试真正“脱机”的关键是让 U-Boot 每次上电都自动执行同样的动作。U-Boot 默认支持启动脚本boot.scr它原本是给 Linux 镜像准备引导参数用的但完全能被我“借”来加载 M4 固件。我在bootfs分区根目录放了一个boot.cmd文件内容如下setenv m4_addr 0xC4000000 fatload mmc 0:4 ${m4_addr} m4-led.elf if itest.s $? 0; then rproc init rproc load 0 ${m4_addr} ${filesize} rproc start 0 else echo M4 firmware load failed! fi然后把boot.cmd使用 mkimage 工具打包成boot.scrmkimage -T script -C none -n MP13 M4 boot -d boot.cmd boot.scr把生成的boot.scr复制到 SD 卡 bootfs 分区根目录。再次上电U-Boot 会在默认启动流程里找到并执行 boot.scr自动加载并启动 M4 固件然后停在 U-Boot 控制台。这里有个细节值得注意默认正式镜像的 U-Boot 默认 bootcmd 确实会寻找 boot.scr但如果你用的是自己编译的 U-Boot可能要检查环境变量bootcmd是否包含类似于loadbootscript的逻辑。没有的话需要用setenv bootcmd fatload mmc 0:4 0xC4000000 boot.scr; source 0xC4000000这类环境变量兜底再saveenv保存。4.3 分区格式与手工建卡建议如果你手头没有官方刷好的 Linux SD 卡需要从零制作一张启动卡我不会建议你完全手工建 GPT 分区。最好使用 ST 提供的脚本create_sdcard_from_stm32mp1_boards.sh刷官方镜像刷完之后再挂载 bootfs 分区替换里头的boot.scr与 M4 固件。严格要求之下SD 卡格式至少有两点要确认。一是分区表官方是基于 GPT 的至少包含fsbl1、fsbl2、fip、bootfs、rootfs二是 bootfs 分区格式常见为 FAT32但不同脚本版本也可能生成 ext4。我的建议是让 bootfs 用 FAT32因为 Windows 下直接拷贝文件最省事U-Boot 读取也稳定。如果格式不对轻则 fatload 命令失败重则整个启动流程卡在 FSBL 阶段。5. 脱机运行与问题排查5.1 实测脱机效果把 boot.scr 和 m4-led.elf 都放好后实验场景是板子完全断电拔掉调试线只保留 SD 卡重新上电。供电这个过程其实仍需要从 ST-LINK 的 USB 口取电但程序运行不再依赖 ST-LINK 的调试会话。此时打开串口终端能看到 U-Boot 和 FSBL 的启动日志等待 boot.scr 执行后LED 开始自行闪烁。即使连续按复位键或者断电再上电行为依旧一致因为程序源头在 SD 卡不会因内存掉电而丢失。这就算完成了“SD 卡脱机运行 LED 程序”。如果你想确认加载地址有没有问题还可以在 U-Boot 里手动执行md 0xC4000000查看内存头部数据MDK 里也看不出什么但至少能验证固件确实被读进了预期位置。5.2 常见问题速查表现象可能原因排查方向上电后串口无任何输出BOOT 拨码不在 SD 卡启动档查用户手册确认 BOOT0~BOOT3SD 卡可启动 Linux但找不到 m4-led.elfbootfs 分区号不对用part list mmc 0确认分区编号fatload 报文件未找到文件没拷贝成功或大小写不对检查 FAT 分区根目录确认文件名完全一致rproc load 报地址错误ELF 链接地址与目标 RAM 不符检查 CubeIDE 生成的链接脚本rproc load 成功但 LED 无反应M4 未启动或 GPIO 配置被 U-Boot 干扰检查 rproc start 返回值确认 GPIO 时钟手动命令正常重启后失效boot.scr 未被自动执行确认 boot.scr 名字、位置、bootcmd 环境变量切回 Linux 后 M4 程序不跑boot.scr 被 Linux 启动流程覆盖删除或重命名自定义 boot.scr 即可恢复5.3 排查技巧与调试建议我最常用的一条技巧是把 M4 程序的入口处开头第一件事翻转一个空闲 GPIO并用示波器或者万用表观察。这样能快速判断 M4 有没有真的跑起来而不至于在 HAL_Delay 或者时钟配置里盲找问题。如果 M4 程序放的是串口 debug 信息也要注意 MP135DK 上标准调试串口默认是 A7 控制台M4 想输出日志需要额外配置对应的 UART 外设。比如我用 USART4 做 M4 日志输出波特率 115200把它接入同一个 USB-TTL 转换器就能看到。没有串口输出不一定代表 M4 没有运行先看 GPIO 状态最直接。另一个实用技巧是调通首版点灯程序前不要急着写复杂的 Boot 脚本。先用 U-Boot 手动敲命令验证确认链路没问题再打包 boot.scr。否则一旦有变量环境、路径或权限问题排查起来会和排地雷一样。5.4 进阶思路这套机制跑通后能玩的事情其实很多。M4 上可以移植 FreeRTOS任务调度、信号量、队列这些都和普通 MCU 一样再用 M4 的定时器做 PWM输出给电机驱动或者调光电路实时性比 Linux 侧好得多。更进一步的玩法是让 M4 和 A7 侧保持“半协同”。A7 跑着 U-Boot 停在控制台M4 负责一个实时任务两者之间如果后续需要通信可以在共享内存里定义一块协议区域M4 写状态A7 通过 U-Boot 命令读取内存检查状态。当然这只是一种轻量协同真正复杂场景还是得回到 Linux 的 remoteproc 框架。我个人实际操作里的最大体会是这条路径适合快速验证“MPU 外设能力 MCU 开发效率”的组合但它更适合作为项目前期的快速原型而不是长期生产系统。如果你只想用一颗芯片同时承担跑 Linux 服务和裸机实时控制还是要认真设计内存映射和 IPC 方案参考 ST 官方推荐的 Linux M4 协作方式。不过单就“把 MP135 当单片机玩”这件事来说SD 卡脱机跑点灯绝对是一个性价比极高的入门实验让我在一晚上之内彻底理解了这个异构芯片的启动闭环。
返回列表