
把 STM32H747 当成一颗高性能单核芯片来写第一版代码很可能很快能点亮但一进入联调阶段就会开始怀疑人生为什么 M7 写下去的数据M4 好像没看到为什么外部 RAM 里的 DMA 数据总是旧值为什么两个核明明都在跑系统反而比单核更难稳定这些问题不是 CPU 性能不够而是从单核思维跨到异构双核思维时必须重新理解分工、内存、Cache、启动和外设归属。STM32H747 真正考验人的地方不是它能跑多快而是你能不能把两颗完全不同性格的 Cortex-M 内核放在同一套工程体系里仍然让系统保持可控、可复现、可长期维护。1. 先想清楚STM32H747 的双核到底该怎么分工很多人打开 STM32H747 的数据手册第一眼看到的是两个 Arm 内核第一反应是“那我是不是可以把原来的主循环拆成两半一个核跑前半段一个核跑后半段”。这恰恰是最容易踩坑的起点。双核不是把单核代码切成两半而是要先定义“谁来管什么”“谁的数据从哪里来”“谁有权操作哪个外设”。1.1 异构双核不是对称双核M7 和 M4 的性格不一样STM32H747 是一颗异构双核芯片它由 Cortex-M7 和 Cortex-M4 组成。这两个内核虽然名字里都有 Cortex-M但它们在指令执行能力、缓存结构、总线路径和适合承担的任务类型上都是不一样的。Cortex-M7 是“大引擎”它更适合处理比较大的指令流、复杂算法、文件系统、网络协议栈、图形界面这类对算力有一定要求的任务。Cortex-M4 更“轻”功耗和中断延迟特性往往更适合做周期明确的实时采样、控制逻辑、故障保护、简单的电机控制或者通信状态机。这里有一个容易被忽略的点两个内核并不是对称关系M4 不是 M7 的“平替小核心”。它们更像是一个项目里分工不同的两个角色。一个角色负责前台的大局观比如界面、网关、数据整合另一个角色负责后台的确定性比如采样、控制、保护。典型但非唯一的分工方向可以这样看内核适合承担的任务常见注意事项Cortex-M7HMI、复杂协议、文件系统、批量数据处理、算法运算对 Cache、内存带宽、总线上多主设备访问更敏感Cortex-M4周期控制、快速采样、状态机、故障保护、低延迟响应任务要清晰不承担太重的“全量计算”否则延时不可控如果某个项目里所有任务其实只占单核 40% 的负载那强行双核不一定划算。双核带来的启动同步、共享内存、版本匹配、调试复杂度都是实实在在的工程成本。1.2 单核思维最容易漏掉三个层面从单核 MCU 转到 STM32H747有三个层面最容易“想当然”。第一启动。STM32H747 不是两个核上电后各自从 Flash 开头取向量表这么简单。两个核的启动方式、向量表位置、复位后谁先跑、谁负责唤醒谁都需要在工程最开始就定好。否则你烧录完发现只有一个核在工作另一个人在 Debug 里根本看不到。第二外设归属。虽然一颗芯片里有很多外设但不是所有外设都适合两个核共享操作。FDCAN、UART、SPI、显示控制器这些东西一般应该在设计阶段就指定给某一个核而不是让两个核都有访问权。两个核同时去操作同一个外设寄存器或者在两个核里同时打开同一个外设中断等于在系统里埋了一颗不定时炸弹。第三内存和状态共享。两个核看到的同一片 SRAM不代表它们看到的“永远是同一个内容”。如果 M7 开启了 D-CacheM4 直接往共享 SRAM 写数据M7 如果没有做缓存维护读到的可能是旧数据。这个问题的隐蔽程度远高于普通嵌入式 bug因为它不是每次必现而是偶发出现。1.3 什么时候双核是真的加分什么时候只是增加复杂度判断一个项目是否真的需要 STM32H747 这类双核可以问自己三个问题当前是否有两类任务对“实时性”和“算力”有完全不同甚至互相挤兑的要求是否有一个任务是高优先级周期性的不能因为另一个核跑协议栈或界面而被延迟打乱是否已经有现成的单核 STM32H7 或 Cortex-M7 芯片能够满足需求只是觉得“双核听起来更强”如果只是想把两个业务模块分开跑而两者之间没有明显的时间敏感冲突单核加 RTOS 多数情况下会更简单。真正的双核价值是当“界面刷屏”和“快速控制”挤在同一个核上时能把它们隔离在两个独立的执行环境里。这也意味着双核的收益不是“快”而是“隔离”和“确定性”。2. 从零搭建时钟、启动和内存布局是硬核项目的地基STM32H747 这类芯片跑 Demo 不难难的是从空工程开始稳定地建立一套两个核都能工作的最小系统。很多双核项目在早期就翻车不是因为某段算法没写好而是基础配置里的时钟、启动地址和内存归属没有先立起来。2.1 先把时钟和供电策略固定下来不要先跑高级功能开始写业务逻辑之前第一件事是在 CubeMX 里把时钟树整理清楚。H747 双核里不是所有外设都挂在同一个总线域下M7 和 M4 的外设时钟需要分别确认。实际操作中我一般建议这样的顺序对照板子的晶振和电源设计确认外部时钟来源。在时钟树里先把 CPU1 和 CPU2 的时钟源选好。逐个打开需要的外设检查 AHB/APB 分频后外设时钟是否在合理范围。先使用 CubeMX 推荐的 VOS 等级和电源配置跑通最小系统后再考虑低功耗优化。保留时钟初始化代码和库生成的 USER CODE 区域不要随便改生成结构。这里要特别提一句不要看到别人的工程里用了某组 PLL 参数就直接抄。H747 的时钟来源、外部晶振频率、Flash 的供电电压区间不同板子可能不一样。如果外部晶振型号不同或 PCB 负载电容不同照搬时钟参数轻则时钟不启动重则启动后间歇性 HardFault。如果原始工程没有明确给出时钟配置最稳妥的做法是先参考 ST 官方开发板或 CubeMX 默认生成的配置先用内部时钟或已知可用的外部晶振把系统跑起来再逐步切换到目标频率。2.2 两个核的程序怎么放Flash 分区与向量表约定STM32H747 两个核通常共享同一片外部 Flash 空间只是两个核的程序会被放到不同地址段。工程上需要为 M7 和 M4 分别建立链接脚本规定各自代码放在 Flash 的哪一段、各自常量放在哪一段、栈和堆放在哪一段。链接脚本的地址只是一个示例不要直接照抄到不了解容量的板子上/* 示例结构M7 放前段M4 放后段 */ FLASH_M7 (rx) : ORIGIN 0x08000000, LENGTH 768K RAM_M7 (rwx) : ORIGIN 0x20000000, LENGTH 512K FLASH_M4 (rx) : ORIGIN 0x080C0000, LENGTH 512K RAM_M4 (rwx) : ORIGIN 0x10000000, LENGTH 248K这里还有两个关键点。第一M4 的向量表必须落在它自己的启动地址上。M4 从哪个 Flash 地址取向量表必须和链接脚本里的VECTOR_TABLE地址保持一致。如果 M4 是从 Flash 启动那么烧录进去的二进制开头就应该是正确的向量表。如果 M4 是从 RAM 运行则还要在启动代码里把 VTOR 指向 RAM 中新的向量表。第二M7 和 M4 的 Flash 区域不要互相覆盖。双核开发时最容易发生的情况是M7 工程烧录时按照全片擦除模式把 M4 的代码也擦掉了或者 M4 的链接脚本地址后来挪过但烧录工具里的地址没有同步更新。这个坑通常在量产配合或者升级验证时才暴露排查起来很痛苦。建议在工程最开始就做一次“双核点灯”验证M7 写一个共享状态寄存器M4 读到自己地址的代码正常跑起来后翻转 GPIO。只有这一步稳定了再往下加外设和业务逻辑。2.3 内存分区、Cache 和 TCM这不是一枚普通单核 SRAMSTM32H7 系列的内存设计非常讲究它不像早年 STM32F1 那样只有一个简单的 SRAM 池。H747 内部有多种 RAM 类型常见的有紧耦合内存 TCM、AHB SRAM、以及分布在不同总线域下的 SRAM 区域。这些 RAM 各有各的访问路径和特点。ITCM 和 DTCM 是贴近 CPU 核心的高效内存访问延迟低适合放代码、栈和热数据。但有一类问题要特别注意不是所有 DMA 都能访问 TCM。如果设计者习惯性地把 DMA 接收缓冲区放在 DTCM 里等到要测试 DMA 时才突然发现 DMA 根本没法搬到那里或者数据路径绕了一大圈性能严重打折。对于 DMA 缓冲区更稳妥的选择是放在能被 DMA 访问的 SRAM 区域里并通过链接脚本或内存属性明确标识出来。Cortex-M7 自带 I-Cache 和 D-Cache这是性能的重要来源同时也是数据一致性问题的主要来源。如果你开启 D-CacheDMA 往 SRAM 里写数据后CPU 直接读同一块地址有可能读到的是缓存里的旧值。反过来CPU 往 SRAM 里写了一段数据后让 DMA 去发送DMA 读到的也可能不是 CPU 刚写入的最新内容。最简单的规避思路有两种把 DMA 缓冲区所在的区域通过 MPU 配置成 Non-cacheable让 CPU 和 DMA 每次直接访问物理 SRAM。如果缓冲区必须保持 cacheable那就做好 Cache 的 clean 和 invalidate 操作。很多 STM32H7 工程里的“跑一段时间数据就错一次”问题都出在第二种方案没有做完整。不要以为只在初始化时启用 Cache 就够了收数据和发数据的边界都需要处理。2.4 先跑最小双核工程再把外设一个一个加进去双核项目最忌讳“一把梭”。比较稳妥的做法是先建立一个最小骨架M7 初始化必要时钟、启动 M4。M7 和 M4 各自通过共享内存和 Mailbox 完成一次握手。两个核都把自己的启动状态写到一个协议状态块中。两个核各自点亮自己的状态 LED。确认握手稳定后再依次加入 FDCAN、SDMMC、显示等模块。这种做法的根本逻辑是尽可能把“双核本身能不能协同工作”这件事从“双核加外设业务”里分离出来。如果握手都不稳定后面加再多模块都只会让问题更难定位。3. 双核协作信号量、Mailbox、共享内存和启动顺序当两个核同时运行它们之间必然有数据交换和任务同步需求。常见的协作机制可以拆成三件工具Mailbox 负责通知硬件信号量负责互斥共享 SRAM 负责真正搬数据。很多第一次做双核的人会以为只要定义一个全局结构体再用 volatile 修饰一下就能在两个核之间通信了。实际上这里还缺两样东西Cache 一致性和内存屏障。3.1 Mailbox 是门铃不是货车Mailbox 在 STM32H7 双核方案中更像一个“门铃”它用来告诉另一个核“我这边有新事件你该来处理了”而不是把一大块数据塞进邮箱里传过去。真正的数据放在共享 SRAM 中Mailbox 负责通知对方去取。实际工程里可以把 Mailbox 的使用简化成一个事件标志系统。比如 M7 需要把一组控制参数传给 M4M7 把要传递的数据写入共享内存中的一块缓冲区。M7 发布一个带递增序号的事件。M7 通过 Mailbox 通知 M4。M4 收到 Mailbox 中断后读取共享缓冲区长度和序号处理数据。M4 处理完成后再通过 Mailbox 返回确认。有人可能会问为什么不直接在 Mailbox 中断回调里处理业务逻辑因为中断回调越短越好。更好的做法是在中断回调里只清事件、置标志、唤醒业务线程或主循环真正的协议解析和解包放到正常上下文执行。3.2 共享数据不能只靠 volatile很多双核通信代码的最初版本长这样// M7 写入 shared-data value; shared-flag 1; // M4 读取 if (shared-flag 1) { process(shared-data); }这段代码在完全不开启 D-Cache 的简单实验里可能能跑但它经不起真实工程检验。原因有两个第一个原因是编译器优化。如果编译器认为这两个写入只在当前核里可见它可能重排写入顺序或者把一个后续不会再使用的变量优化掉。volatile能阻止一部分优化但并不能完整保证多核视角下的顺序。第二个原因是 Cache。如果两个核在共享内存上读写而其中一个核开启了 D-CacheCPU 写下去的数据可能还在 Cache 里还没有真正落到 SRAM另一个核去读时自然读不到。更规范的示意做法是// M7 这一侧 lock(shared_sem); shared-cmd CMD_SET_FREQ; shared-seq; shared-freq target_freq; __DMB(); // 保证上面的写入在释放锁之前对其他总线主设备可见 release(shared_sem); notify_m4(MBOX_CMD_READY);在释放信号量之前做一次内存屏障目的不是让代码更好看而是为了告诉编译器和硬件“之前写入共享内存的内容必须已经发生不能被重排到释放操作之后。”同样的道理也适用于 M4 读取端。不要完全依赖 CPU 自身“碰巧读到了新值”而应该通过信号量和内存屏障协议保证读操作发生在得到事件通知之后。3.3 给共享协议加上版本号和序号双核工程里的另一个常见问题是代码版本错配。M7 程序升级了M4 程序还是旧的两个核之间解析的结构体不再一致。轻则某个功能不生效重则直接把共享内存中的数据构造成非法状态导致另一个核跑飞。所以共享协议块里至少应该包含这些字段magic标识这块内存是按什么协议组织的。versionM7 和 M4 各自的中断服务代码版本。seq每次通信的递增序号用来检测是否漏消息。coreStatus[2]两个核各自的心跳和错误码。len本次事件的有效数据长度。crc对业务数据的校验值。M4 收到 Mailbox 后第一件事不是直接处理数据而是先检查 magic 和 version。如果发现版本不匹配就进入错误处理流程而不是强行解析共享缓冲区。这样做可以把“双核版本不一致”这个工程问题变成系统里的一个可检测错误状态而不是演变成随机崩溃。3.4 启动同步不要两个核一起盲目抢跑双核项目里复位后两个核的执行顺序必须有一个明确约定。如果 M7 先启动那么 M7 可以先初始化公共资源、共享内存状态以及负责释放 M4 启动条件的那部分时钟和复位逻辑。M4 复位后不能立刻去访问还没初始化的共享数据结构必须等 M7 发出“公共资源就绪”的消息。更完整的启动序列可以这样设计M7 复位后先初始化自身时钟、MPU、Cache。M7 初始化共享协议块和 Mailbox。M7 释放 M4 的复位条件让 M4 进入运行。M4 上电后先关闭全局中断初始化自己的时钟和私有外设。M4 把coreStatus[1]置为 READY再通过 Mailbox 通知 M7。M7 收到 M4 READY 后把coreStatus[0]也置为 READY然后两个核再进入各自的业务主循环。不要省略第二步到第五步之间的等待。如果 M7 刚刚写完共享协议块M4 就立刻跑进来读数据在 Cache、总线和编译重排的共同作用下M4 很可能读到 M7 初始化一半的协议块。4. 实际外设里的高频技术点Cache、DMA、FDCAN 和显示双核通信虽然复杂但它只是骨架。真实项目里大量时间还是花在外设调试上。STM32H747 的外设集成度很高但越高级的外设越需要理解它和 Cache、DMA、中断之间的配合。4.1 Cache 与 DMA先想清楚谁在写数据谁在读数据很多 STM32H7 项目在刚开始调试 UART DMA 时都会遇到“ DMA 中断明明产生了但 buffer 里的数据始终是旧的”。这个问题的本质是 D-Cache 的缓存策略。DMA 把数据从外设搬到 SRAM 后SRAM 里确实有了新数据但如果 CPU 之前已经访问过这段地址旧副本可能还在 D-Cache 里。CPU 再去读的时候它优先从 Cache 命中而不是访问物理 SRAM所以读到的是旧值。针对 DMA 接收数据一个常见的处理模式是// DMA 接收完成中断中 SCB_InvalidateDCache_by_Addr((uint32_t *)rx_buf, rx_len); process_rx_data(rx_buf, rx_len);Invalidate 的作用是先把 D-Cache 里对应地址的缓存行作废下一次 CPU 访问时不得不从物理 SRAM 重新加载这样就能读到 DMA 刚写入的数据。反过来如果 CPU 要填充一段数据然后交给 DMA 发送发送之前需要做 CleanSCB_CleanDCache_by_Addr((uint32_t *)tx_buf, tx_len);Clean 的作用是把 D-Cache 中尚未落回 SRAM 的数据写回去确保 DMA 去读物理 SRAM 时拿到的数据是完整的。一个实操经验是DMA 缓冲区的首地址最好按 Cache Line 对齐。Cortex-M7 的 D-Cache 行长度常见为 32 字节如果缓冲区和长度没有对齐invalidate 的边界处理就会出现“只作废了前半段后半段缓存仍残留旧数据”的尴尬。如果不想在每个 DMA 回调里都做 Cache 维护也可以考虑把 DMA 缓冲区放到一个通过 MPU 配置成 Non-cacheable 的独立区域。这样 CPU 读写这段缓冲区会直接访问物理 SRAMDMA 再访问时不会出现一致性问题。但代价是这段区域失去了缓存加速不适合做高频访问的大块热数据。4.2 FDCAN先分清单核操作权再优化波特率和滤波器在 STM32H747 里跑 FDCAN 时第一原则是指定一个核作为 FDCAN 的唯一主人。另一个核如果需要 CAN 数据应该通过双核通信协议去读取而不是直接操作同一个 FDCAN 外设寄存器。FDCAN 和普通 CAN 最大的区别之一是它支持更高的数据速率并且可以设置不同的仲裁段速率和数据段速率。很多人配置时只关心波特率数字是否正确却忽略了以下因素CAN 外设使用的是哪个时钟源是否已经开启了相应总线时钟。位时间里的同步段、传播段、相位段分配是否合理。采样点位置是否匹配总线上的其他节点。外部收发器是否支持 CAN-FD 的高速率是否考虑了线路寄生电容。如果总线上出现偶发错误帧或者高概率握手失败建议先不要怀疑对方节点先打开 CAN 错误日志观察是位错误还是填充错误再用回环模式排除外部收发器的影响。在 CAN-FD 配置里仲裁段和数据段是两组位定时参数。工程上并不建议把数据段速率盲目拉到最高尤其是线束较长或者连接器质量不稳定的应用中高速数据段误码率会明显上升。先稳定跑仲裁速率再逐步提升数据段速率比一次性把参数拉满然后找不到原因要靠谱得多。4.3 显示刷新和图形搬运用 DMA2D 而不是 CPU 一像素一像素刷STM32H747 之所以被很多项目选中是因为它一方面有不错的算力另一方面又集成了显示链路所需要的硬件加速能力。如果你的项目带屏幕那么刷新和图像搬运方式会直接决定界面流畅度。一颗 Cortex-M7 虽然算力强但如果你靠 CPU 去逐像素填充界面或做图层混合即使主频很高帧率也会被这种低效操作拖垮。此时硬件 2D 加速模块 DMA2D 通常会比 CPU 更适合做整块颜色填充。Framebuffer 之间的拷贝。图像格式转换。半透明图层混合。图形显示还有一个常见误区不处理垂直同步就切换 Framebuffer。如果 LTDC 正在扫描屏幕中间时你突然改掉活跃层地址用户会看到画面撕裂。更稳的做法是使用多缓冲在 VSync 中断里完成“下一次要显示哪一帧”的切换。如果 Framebuffer 和 DMA2D 之间存在 Cache 交互也要专门处理。比如 CPU 先渲染了一帧图像到 Framebuffer如果这段地址命中 D-Cache 且还没有 cleanDMA2D 去读取时就可能拿到不完整的图像。此时要么把 Framebuffer 配置为 Non-cacheable要么在交给 DMA2D 之前做 clean。5. 问题排查链路H747 工程出问题时不要急着瞎调参数双核项目最容易让人崩溃的还不是某个功能不工作而是现象非常随机有时正常有时卡死有时连续几小时没问题上电一次就出现 HardFault。这里最忌讳的是没有排查链路靠运气反复编译烧录试。5.1 HardFault 先做“清零定位”不要直接怀疑算法遇到 HardFault第一步先判断是上电立刻崩还是运行一段时间后崩。上电立刻崩的常见原因包括PLL/时钟配置不对外设时钟超上限。M4 或 M7 的向量表位置和启动地址不匹配。链接脚本中 RAM 地址越界栈指针初始化读到了无效值。MPU 配置把当前代码所在区域设成了不可执行。运行一段时间再崩的常见原因则更多栈溢出。堆溢出。数组越界写坏了相邻变量。共享内存被两个核同时写坏。看门狗超时后复位到了异常状态。建议在工程里保留两个故障信息记录区一个专门记录 HardFault 发生时的 PC、LR 和 CFSR 等状态寄存器另一个记录业务标志比如当前正在执行的模块、最近一次共享消息序号、错误码。否则 MCU 一旦复位你面对的就只剩一个“重新跑起来但什么都没发生”的板子。5.2 共享数据偶发错误的排查顺序症状是“数据偶发错一两个字节”或“某一帧参数莫名其妙异常”可以按下面这个顺序排查先确定现象是固定某个数据错还是随机错。再确认是哪个核在写、哪个核在读是否存在两个核同时写的可能。检查写入和读取之间有没有信号量保护。检查开启 D-Cache 后收发边界有没有做 clean/invalidate。检查缓冲区地址是不是被链接脚本放到了 DMA 访问不到的区域。检查共享结构体大小和字节对齐是否和 Mailbox 消息长度一致。最后再检查协议序号和 CRC确认有没有漏消息或重复消息。很多时候这类问题不是某一个条件单独造成的而是“Cache 没有 invalidate 共享锁没有释放 中断里读数据”三个问题叠加在一起。先按链路逐层排除比一个人盯着逻辑反复看更容易找到根因。5.3 双核项目一定要做“故障指示墙”双核系统很难靠单点日志来定位卡死。如果 M7 在忙某件非常耗时的任务时把中断关得时间过长M4 的 Mailbox 中断可能被延后如果 M4 陷入某个死循环M7 看起来可能还在正常工作。此时如果没有状态指示你会看到系统整体不响应但不知道是哪一个核先“掉链子”。可以在共享诊断块里安排这样一组字段typedef struct { uint32_t magic; uint16_t swVersion; uint16_t seq; uint32_t heartbeatM7; uint32_t heartbeatM4; uint32_t lastErrorM7; uint32_t lastErrorM4; } DiagBlock;两个核各自在自己的主循环里更新心跳值。调试点 LED 或预留 Debug GPIO把心跳异常和复位源映射成具体的错误代码。还要养成看复位源寄存器的习惯。很多人遇到偶发复位后的第一反应是“是不是哪里写越界了”但打开复位源寄存器往往会发现是一次欠压复位或者窗口看门狗复位。不同复位源对应的排查思路完全不同。6. 从 Demo 到量产还要补齐四块拼图很多 STM32H747 项目在开发环境里跑得非常顺利却在批量生产或长期运行一段时间后暴露问题。原因往往不是单一芯片坏掉而是工程化能力没跟上。6.1 程序升级时要考虑两个核的版本一致性双核 OTA 比单核复杂得多。单核 OTA 只要更新一份 App双核 OTA 则需要同时管理 M7 App 和 M4 App。升级流程不能简单理解成“先写 M7再写 M4”。如果 M7 新版已经生效M4 旧版还在跑共享协议结构体可能就完全对不上了。稳妥方案是先让 Bootloader 把新版 M7 和 M4 都下载到暂存区并校验然后在下次系统复位时一起切换。还有一个工程细节不要在 M4 还在从 Flash 执行代码的情况下去擦写 M4 所在的那段 Flash 区域。否则 M4 取指可能失败出现不可预期的行为。升级前要通过双核协议通知 M4 进入暂停状态再执行烧录或跳转。6.2 看门狗和错误恢复要定义清楚双核系统的看门狗策略不能只是“M7 喂一个M4 喂一个”。如果 M7 觉得系统正常但 M4 已经跑飞整个系统从外部看可能已经处于失控状态但没有任何机制去恢复 M4。更实用的是在系统里增加“监督者”机制。可以由一个核负责检查另一个核的心跳计数如果发现对方超过规定时间没有更新心跳就主动记录错误并通过复位或重新握手流程恢复。这个时间上限要根据业务运行周期留有余地不能设得太紧否则误判会让系统频繁重启。6.3 CubeMX 生成代码与手写业务代码要分离STM32H747 这种芯片外设多、CubeMX 初始化和生成代码量大项目后期如果手动改动过多生成区块再想升级时钟或新增外设就会非常痛苦。我建议从一开始就把代码分成三层芯片初始化层主要来自 CubeMX 生成业务代码只写在 USER CODE 区块内。双核公共协议层共享结构体、Mailbox 状态机、版本管理。业务实现层M7 和 M4 各自的功能模块。双核公共协议层不要放在自动生成目录里被反复覆盖最好单独建立头文件和源文件并由代码仓库统一管理。6.4 不要用“跑通了”代替“跑稳定了”双核系统的稳定性试炼需要一种“疲劳测试”思维。如果你只是在调试台上点一下按钮看到功能正常就认为移植完成那还差得很远。建议针对双核通信做几类长期测试长时间压力测试持续几小时甚至几十小时压测核间通信。随机注入测试人为制造 M4 启动失败、Mailbox 消息丢失等场景观察系统是否能自动恢复。异常边界测试把 DMA 缓冲区长度改到临界值把共享消息序列号推到翻转位置看处理逻辑是否完整。版本错配测试故意烧录旧版 M4 与新版 M7确认能通过版本号检测到错误。这些测试的意义不是证明代码当前能跑而是证明代码在边界、异常和外部干扰下依然能保持可控。STM32H747 不是一颗拿来跑普通 Demo 的芯片。它能够承担的任务很重但前提是你尊重它的双核属性、内存属性、Cache 属性和外设归属。真正让一个硬核项目稳定下来的往往不是某个聪明的算法而是从启动握手到共享协议再到错误恢复这条完整链路都被你想明白了。先把分工和复位顺序设计好再逐个外设验证最后用压力测试兜底这套路径虽然慢但却是双核产品最值得走的捷径。