很多人第一次拿到STM32MP157F-DK2第一反应是先折腾A7双核跑个Linux、点个屏幕、看看桌面。但真正把这块板子的价值发挥出来绕不开那个藏在里面的Cortex-M4核。M4核负责实时控制、高速IO、低延迟中断跟A7上跑Linux做业务是完全互补的两条路线。于是我一开始的想法很简单用STM32CubeProgrammer把M4的固件烧进去像以前玩STM32F103那样“Download”一下就跑起来。结果真上手才发现MP1的M4核不是你想烧就能烧的连“烧录”这个概念都和单片机不太一样。这篇文章就围绕STM32MP157F-DK2上用STM32CubeProgrammer给M4 Core烧录这件事把原理、步骤、坑和部署方式都完整讲一遍。1. M4核在MP1里的真实角色为什么它不能“一键烧录”1.1 A7与M4的分工以及你该把什么代码放M4上STM32MP157F-DK2使用的是STM32MP157F芯片双核Cortex-A7负责跑操作系统Cortex-M4则是独立的实时处理单元。A7上面跑的是OpenSTLinux这类完整Linux发行版进程调度、文件系统、网络协议栈这些都是它的主场。但Linux天然不适合做硬实时任务中断响应延迟、调度不确定性在工控、电机控制、音频采集这些场景里就是硬伤。M4核的存在就是为了接住这些“不能等”的活它可以直接访问外设寄存器跑裸机或者FreeRTOS响应延迟可以做到微秒级甚至更低。所以你在做项目规划的时候要先想清楚哪些代码放A7哪些放M4。比如A7上做用户界面、网络服务、日志存储M4上做传感器数据采集、PWM输出、编码器读取、通信协议解析。现实里的典型做法是M4跑一个状态机处理实时性要求高的外设事件然后通过共享内存和A7交换数据。理解了这一点你就知道为什么M4核的固件加载方式和A7不一样——它不是启动链里的一环而是由A7侧“拉起来”的一个协处理器。1.2 M4核没有独立启动能力烧录与加载的本质区别这是新手最容易懵的地方。STM32MP157的启动流程走的是BootROM - TF-A - U-Boot - Linux整个链条里默认没有M4的位置。M4核没有自己的BootROM启动入口它的代码要能被A7侧的软件主动加载到M4专用的SRAM地址从0x10000000开始然后让M4从那里执行。这跟普通单片机从内部Flash取指是完全不同的逻辑。所以“Flashing M4 Core”这个说法严格拆开有两种含义。第一种是用STM32CubeProgrammer通过ST-LINK把M4固件直接下载到M4 SRAM并运行这适合调试阶段掉电就没了。第二种是把M4固件文件持久化放到SD卡、eMMC或NOR Flash里然后由U-Boot或Linux的remoteproc机制在每次上电时加载运行这才是真正意义上的“部署”。很多人烧录完发现板子重启后M4不跑就是因为只做了第一种或者做了第二种但缺少A7侧的加载配置。这篇文章会把这两条路都走一遍。2. 动手前的准备工作工具链、固件材料和板卡接线2.1 先装好STM32CubeProgrammer版本与驱动的坑STM32CubeProgrammer是ST官方的烧录工具集成了下载、调试、Flash编程、OTP读写等功能。当前建议直接从ST官网下载最新版第一次使用前注意几个细节。以Windows为例CubeProgrammer安装完成后板载ST-LINK驱动不一定自动生效。你可以在设备管理器里检查有没有出现“STMicroelectronics STLink dongle”或者“STM32 STLink”相关设备。如果看不到需要手动安装安装目录下的ST-LINK USB驱动路径一般是C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\drivers\ST-LINK_USB_V2_1_Driver更新驱动时指向这个目录即可。Linux下操作则要注意权限问题。直接把用户加入plugdev组是比较常规的做法否则STM32CubeProgrammer连接ST-LINK时会报权限不足。命令行工具的位置在安装目录的bin/STM32_Programmer_CLILinux下通常叫STM32_Programmer_CLIWindows下是STM32_Programmer_CLI.exe。写这篇文章时我用的版本是2.15后来换到2.17操作接口基本没变化。需要特别注意CubeProgrammer版本不要太老老版本对MP1系列的支持不够完整识别芯片时容易出问题。2.2 M4固件从哪里来CubeMX生成、官方例程和编译产物烧录M4核之前你手上得有一个专门针对M4核编译出来的固件文件常见格式是.elf或.bin。获取渠道主要有三条第一用STM32CubeMX生成。新建工程时选择芯片STM32MP157F-DK2注意有一个选项要你选择“Cortex-M4”作为目标核然后按常规方式配置外设、生成代码最后用STM32CubeIDE编译产出.elf文件。CubeMX生成M4工程时会自动帮你处理好内存地址链接脚本直接指向M4 SRAM区域。第二使用STM32Cube_FW_MP1固件包里的现成例程。ST官方把M4的例程全部放在固件包里面路径类似Projects/STM32MP157F-DK2/Examples/里面包含GPIO、UART、I2C、SPI各种外设示例。第一次调试建议直接用这些例程编译出来的elf省去自己写代码出问题排查的时间。第三自己的裸机或者FreeRTOS工程。这种就要特别注意链接脚本M4固件的加载地址必须是0x10000000起始的SYSRAM区域链接脚本错了烧进去也跑不起来。我建议你调试阶段就把Debug目录下生成的.elf文件单独拷贝到一个好找的位置后面一系列操作都要用到它。2.3 板卡接线和ST-LINK枚举确认STM32MP157F-DK2板子上的USB口有好几个给M4烧录用的板载ST-LINK是独立的Micro-USB口通常标记为“ST-LINK USB”接到电脑后板子会通过ST-LINK供电。千万不要插到旁边那个USB Type-C口那是A7的USB OTG口跟调试器没有任何关系。先把线接好然后打开设备管理器或者Linux的lsusb确认ST-LINK已经被系统识别。Windows下如果看到“STM32 STLink”以及一个“Virtual COM Port”说明枚举成功。如果只看到COM口但看不到STLink多半是驱动没加载好。另外DK2板载ST-LINK的版本不同识别出来的名称可能是ST-LINK/V2-1也可能是ST-LINK/V3这都不影响CubeProgrammer操作。还要多说一句板子的BOOT拨码开关SW1在烧录M4时不是必需调整的只要ST-LINK能连上目标芯片就可以直接下载M4固件。但是如果你希望M4固件能持久化部署并且上电后由Linux自动加载那就需要保证板子正常从SD卡或者eMMC启动OpenSTLinux系统这块后面专门讲。3. 核心实操用STM32CubeProgrammer把M4固件跑起来3.1 快速验证GUI方式在线下载到M4 SRAM打开STM32CubeProgrammer界面左边选择“ST-LINK”接口右边点“Connect”。如果连接成功软件会读出芯片系列信息显示为STM32MP157Fxx。实际连接中经常遇到A7侧Linux已经跑起来的情况这可能导致连接失败因为A7的调试访问权限可能被Linux占用。我的经验是连接时勾选“HOTPLUG”模式或者在点击Connect的瞬间按住板上的NRST复位键不放让CubeProgrammer在复位窗口期内抓到芯片。连接成功之后选择“Open file”找到之前编译好的M4固件elf文件。加载进来后CubeProgrammer会解析出固件中各个段的地址。这时不要去看A7侧的Flash地址M4固件的段应该分布在0x10000000附近的SYSRAM区域。点击“Download”按钮软件会把代码写入M4的SRAM。然后点击“Run”或者“Start execution”M4核就开始运行了。怎么判断M4确实跑了最简单就是用官方例程。比如GPIO例程会让板子上的LED闪烁UART例程会在调试串口打印日志。如果屏幕没有任何反应先去确认你选的例程对应的外设和板子丝印一致。这种在线下载方式胜在速度快、不需要改启动配置调试M4逻辑非常方便。但缺点也很明显断电后固件就没了每次上电都要重新下载。3.2 命令行烧录用CLI方式把流程脚本化命令行方式适合批量测试和集成到自动化流程里。STM32CubeProgrammer的命令行工具是STM32_Programmer_CLI基本连接命令如下STM32_Programmer_CLI -c portSWD modeHOTPLUG这段命令表示通过ST-LINK的SWD接口连接目标芯片modeHOTPLUG对应GUI里的热插拔模式可以绕过Linux对调试口的占用问题。连接后直接下载M4固件STM32_Programmer_CLI -c portSWD modeHOTPLUG -d ./Debug/stm32mp157f-dk2-m4-fw.elf-d参数表示下载Download。下载完成后软件会提示“Download verified successfully”之类的信息。如果你希望下载后立即运行可以加上-rst让芯片复位复位后M4是否启动取决于当前A7侧是否配置了加载动作。更稳妥的做法是下载后通过调试接口直接设置M4的PC指针为固件入口地址但这已经属于调试器的高级玩法了日常用到不多。还有一个实用参数是-v打印详细日志遇到下载失败时一定要加这个参数看具体报错。STM32_Programmer_CLI -c portSWD modeHOTPLUG -v -d ./Debug/xxx.elf命令行方式的优势在于可重复、可脚本化提交代码后一键烧录对开发效率提升很明显。3.3 烧录到NOR Flash能行但别用默认方式直接干有些人想让M4固件持久化到板载NOR Flash里觉得这样最可靠。STM32CubeProgrammer里确实支持外部Flash编程方式是在连接界面点击“External Loader”按钮选择MX25L25645G_STM32MP15x-NOR.stldr这是DK2板载NOR Flash的加载器。选择后即可通过ST-LINK直接读写NOR内容。但我必须提醒一下直接用External Loader方式烧M4的elf文件到NOR里是很多人踩坑的重灾区。原因在于M4固件elf内部的段地址是0x10000000对应的SYSRAM地址而External Loader是按NOR Flash的存储地址来写的。直接把elf烧进去NOR里存的数据虽然是完整的但启动时U-Boot或者Linux不知道该从哪里读取也不知道怎么把这段数据搬运到M4的SRAM里。所以M4自然跑不起来。我试过的可行方案是把M4固件转换成裸二进制镜像然后自己定义一个NOR偏移比如0x100000用External Loader把bin写进去。之后在U-Boot里通过命令手动把NOR中的数据读出来加载到M4 SRAM再启动。这个方案可行但配置起来比较麻烦而且不同OpenSTLinux版本的NOR布局不一样偏移地址很容易冲突。我的建议是除非你非常熟悉自己的Flash布局表否则部署M4固件用后面要讲的SD卡文件系统方式比直接折腾NOR省心得多。4. 让M4上电自启Linux侧remoteproc与U-Boot加载4.1 把M4固件放到SD卡文件系统里由Linux自动加载在OpenSTLinux系统上部署M4固件的标准做法是把它放到/lib/firmware/目录下然后通过remoteproc框架加载。M4固件文件直接复制过去就行例如sudo cp stm32mp157f-dk2-m4-fw.elf /lib/firmware/然后通过sysfs接口指定固件文件名并启动echo stm32mp157f-dk2-m4-fw.elf /sys/class/remoteproc/remoteproc0/firmware echo start /sys/class/remoteproc/remoteproc0/state这里的remoteproc0是M4核对应的remoteproc设备节点具体编号取决于设备树配置有的板上可能是remoteproc1。可以通过ls /sys/class/remoteproc/查看实际情况。启动后可以用cat /sys/class/remoteproc/remoteproc0/state确认正常会显示running。如果想每次开机自动加载可以在系统启动脚本里加入上述命令或者写一个systemd service。还有一点要注意/lib/firmware/里的M4固件文件名必须和设备树里firmware-name属性配置的一致。默认的设备树一般已经预留了M4的加载节点但如果你是自己改的设备树就得检查一下节点下有没有这一项。4.2 U-Boot阶段手动加载M4固件如果你需要在Linux启动之前就把M4跑起来比如某些实时逻辑不希望等Linux完全起来那么可以在U-Boot阶段手动加载。首先确保M4固件文件放在SD卡的可访问分区里比如bootfs分区FAT格式。进入U-Boot命令行后执行以下系列操作load mmc 0:4 0xC0000000 stm32mp157f-dk2-m4-fw.elf rproc init rproc load 0 0xC0000000 $filesize rproc start 0第一条命令把固件从SD卡的第四个分区加载到内存地址0xC0000000第二条初始化remoteproc第三条把内存里的固件加载到M4核心最后启动M4。注意分区编号可能与实际SD卡布局有关最好先用mmc part命令查一下bootfs在哪个分区。U-Boot方式的好处是依赖少、启动早适合对开机时间有要求的产品。但配置起来要理解U-Boot环境变量和分区布局排错难度也高一些适合有一定基础之后再玩。4.3 设备树与资源预留为什么M4要用外设必须先“分家”M4和A7共享大量外设如果两边同时操作同一个UART或者GPIO轻则功能异常重则整个系统崩溃。所以在Linux侧M4用到的外设必须通过设备树和ETZPCExtended TrustZone Protection Controller资源管理器做好隔离。CubeMX在生成M4工程时会自动生成一个M4侧的设备树描述片段里面列出了M4独占的外设和对应的资源。你在OpenSTLinux的Linux内核设备树里需要把M4占用的那部分外设节点从A7的可用列表里剔除或者在节点上添加状态标记防止驱动加载。这一步如果漏了最容易看到的现象是M4固件里初始化UARTLinux那边同时把这个UART当控制台用两边不断抢板子看起来就像卡死了一样。推荐的做法是先在CubeMX里把外设归属规划好再一次性生成A7和M4两侧的工程配置不要手动去改手动改很容易留下隐患。5. 高频踩坑记录连接失败、烧录后不工作、外设冲突5.1 ST-LINK连接不上先查驱动再查复位窗口连接问题是所有人都会遇到的第一个坎。报错信息通常是“No ST-LINK detected”或者“Error: ST-LINK error (DEV_TARGET_HELD_UNDER_RESET)”。前者是PC和ST-LINK之间的USB通信问题优先检查USB线、驱动和接口后者是目标芯片复位状态问题说明ST-LINK连上了但芯片处于复位状态需要使用HOTPLUG模式或者在连接瞬间松开复位键。还有一个容易被忽略的场景Linux正在运行A7并且调试接口被系统配置为不可访问这时候ST-LINK连接也会失败。我遇到这种情况的做法是先把板子完全断电然后按住复位键上电再在CubeProgrammer里点击Connect连接成功后松开复位键。多试几次找到那个时序窗口之后后面就顺利了。5.2 固件下载成功但M4不运行下载成功只说明数据写进去了不代表M4会执行。这里最常见的原因是A7侧根本没有发起加载。如果你用的是在线下载方式CubeProgrammer下载的是整个elf镜像M4的PC指针初始值也在elf里理论上下载后直接运行是可以的但前提是A7侧没有把M4置于reset状态或者没有占用M4的调试权限。如果A7侧的remoteproc驱动已经加载并接管了M4你再用CubeProgrammer下载就可能在启动M4时被Linux顶回去。解决办法有两种一是先在Linux里执行echo stop /sys/class/remoteproc/remoteproc0/state释放M4再通过CubeProgrammer下载二是确认elf链接地址正确。M4固件的链接脚本一定要基于0x10000000地址如果你拿一个A7的elf去下载下载是成功的但M4根本没法按那个地址执行自然就没反应。5.3 M4运行后Linux异常八成是外设资源冲突我印象最深的是一次UART例程调试。M4固件能跑串口也有输出但系统运行一段时间后A7侧也出现日志混乱最后整个Linux变得卡顿。排查下来问题出在M4把UART4初始化了而A7侧Linux的设备树里UART4也处于启用状态。两边同时对同一个外设寄存器做读写结果就是数据互相覆盖。这种问题要从两个层面解决。第一层是设备树把M4专用的外设节点在A7侧禁用或标记为不可用第二层是ETZPC配置确保对应的资源隔离寄存器把外设分配给了M4。STM32CubeMX生成代码时会同时配置这两个层面所以一定要用CubeMX来维护工程而不是自己手工加外设。另外M4与A7要通信的话共享内存区域也要在设备树里预留并且要使用reserved-memory节点声明防止Linux把这段内存分配给其他进程。5.4 版本不匹配CubeProgrammer、OpenSTLinux、固件包三方对齐MP1平台的软件栈比较复杂CubeProgrammer、OpenSTLinux系统版本、STM32Cube_FW_MP1固件包版本三者之间如果差距太大就会出现一些莫名其妙的问题。比如老版本CubeProgrammer可能不支持新板子的NOR Flash External Loader新版本OpenSTLinux里remoteproc节点的属性名和老设备树不兼容。我的习惯是安装CubeProgrammer前看一眼官方Release Note确认它支持STM32MP157F系列部署M4固件前先查OpenSTLinux版本对应的Wiki页面看看M4固件推荐的编译工具链和加载方式。保持整体工具链同一批发布版本能省掉大量排查时间。6. 烧录M4核这件事我的几点体会如果让我给一个新手规划学习路径我会建议先不要碰NOR Flash烧录也不要去折腾U-Boot环境变量。第一步用STM32CubeProgrammer在线下载官方GPIO例程看到LED闪烁建立信心。第二步把elf文件放到SD卡上通过Linux的remoteproc自动加载让M4固件能够实现上电自启。第三步再试着用U-Boot命令行手动加载理解整个启动链路的各个环节。这三步走完你对MP1异构架构的理解会上一个台阶。还有一个实用小技巧调试M4固件时不一定非要用CubeProgrammerSTM32CubeIDE里也可以直接连接M4核进行在线调试断点、变量监控都比单纯烧录方便得多。但烧录和部署的场景CubeProgrammer还是不可替代的。每次操作前多看一眼串口日志多用CLI的-v参数打印细节很多坑都能在日志里找到答案。从这个项目里我最大的收获是MP1平台不是把两个CPU摆在同一个芯片里就完事它需要你把整个系统当作一个整体来设计A7跑应用M4跑实时逻辑两者之间用共享内存和资源隔离划清边界。理解了M4的“从属”地位烧录、加载、启动这些问题就都顺理成章了。