1. STM32多核架构概览:M7与M4如何“双人舞”

各位同学,咱们直接进入正题。STM32H7系列这颗双核芯片,说白了就是一颗芯片里塞了两个“大脑”:一个Cortex-M7,一个Cortex-M4。M7是主将,主频高、性能强,适合跑主算法、复杂控制;M4是副手,功耗低、够灵活,适合做数据采集、外设管理、低功耗后台任务。

我刚开始接触H7时,第一反应是:“这不就是两个单片机连一起吗?”后来发现,事情远没那么简单。两个核怎么分工?怎么通信?怎么不打架?这些才是真正的坑。

1.1 M7与M4的分工哲学

先看一张我手绘的架构图,帮你快速建立整体认知:

STM32H7双核架构与通信概览 Cortex-M7 主频480MHz 主控/算法/实时控制 Cortex-M4 主频240MHz 数据采集/外设/低功耗 共享RAM (D2域) SRAM1/SRAM2/SRAM3 双核均可访问 HSEM 硬件信号量 MAILBOX 消息邮箱 M7核心 M4核心 共享内存 HSEM MAILBOX

从图上你能看到,两个核通过共享RAM交换数据,通过HSEM和MAILBOX传递控制信号。说白了,共享RAM是“黑板”,两个核都在上面写东西;HSEM是“粉笔”,谁拿到粉笔谁才能写;MAILBOX是“传纸条”,一个核写完通知另一个核来看。

1.2 双核通信机制:HSEM与MAILBOX

这里我重点讲两个最常用的通信机制。你想想看,两个核同时往一个变量里写数据,会发生什么?数据错乱、死锁、系统崩溃——我在项目中就吃过这个亏。

HSEM(硬件信号量)

HSEM说白了就是一个硬件锁。M7想访问共享资源时,先“申请”一个信号量;申请成功才能操作,操作完“释放”。M4同理。硬件级别的原子操作,比软件锁可靠得多。

关键点:HSEM是非阻塞的。申请失败立即返回,不会死等。你需要自己决定是重试还是做别的事。

MAILBOX(消息邮箱)

MAILBOX更像一个“门铃”。一个核往邮箱里写消息,然后触发中断通知另一个核。另一个核在中断里读取消息并处理。这种方式适合传递控制命令、状态变化等小数据量信息。

我的习惯:大块数据走共享RAM,控制信号走MAILBOX,同步保护走HSEM。三者配合,基本能覆盖90%的双核通信场景。

1.3 内存架构与共享RAM区域划分

STM32H7的内存布局有点复杂。我把它简化成三个域:

域 包含 谁可以访问 典型用途
D1域 ITCM、DTCM、AXI SRAM 仅M7 M7的代码、紧耦合数据
D2域 SRAM1、SRAM2、SRAM3 M7 + M4 + DMA 双核共享数据、通信缓冲区
D3域 SRAM4、备份SRAM M7 + M4 + 低功耗外设 低功耗保持数据、唤醒源

嗯,这里要注意:D2域是双核通信的主战场。我建议你把共享数据、环形缓冲区、命令队列都放在D2域的SRAM1里。为什么?因为SRAM1支持双核同时访问,且延迟最低。

我曾经踩过的坑:把共享变量定义在D1域,结果M4死活读不到数据。后来才发现D1域默认只对M7开放。记住:共享数据必须放在D2域,且要使用__attribute__((section(".sram1")))指定段。

1.4 双核启动流程

双核启动顺序也有讲究。默认情况下:

  1. M7先启动,执行SystemInit、初始化时钟、配置外设。
  2. M7加载M4的代码到D2域(M4的向量表必须放在D2域)。
  3. M7释放M4的复位,M4开始执行。
  4. M4初始化自己的外设,然后通过MAILBOX通知M7“我准备好了”。

这个顺序不能乱。我见过有人让M4先跑,结果M4访问的外设时钟还没开,直接HardFault。

1.5 一个简单的双核通信示例

下面是一个M7往共享缓冲区写数据,M4读取的伪代码框架:

// M7端代码
#define SHARED_BUF_ADDR  0x30000000  // SRAM1起始地址
#define BUF_SIZE         256

// 申请HSEM信号量(假设使用HSEM0)
while (HSEM->CR[0] != 0) {
    // 等待或做其他事
}
// 写共享数据
uint32_t *buf = (uint32_t *)SHARED_BUF_ADDR;
buf[0] = sensor_value;
// 释放HSEM
HSEM->CR[0] = 0;
// 通过MAILBOX通知M4
MAILBOX->M4_MSG = 0x01;
// M4端代码(在MAILBOX中断中)
void MAILBOX_IRQHandler(void) {
    uint32_t msg = MAILBOX->M4_MSG;
    if (msg == 0x01) {
        // 申请HSEM
        while (HSEM->CR[0] != 0);
        // 读取共享数据
        uint32_t *buf = (uint32_t *)SHARED_BUF_ADDR;
        process_data(buf[0]);
        // 释放HSEM
        HSEM->CR[0] = 0;
    }
}

避坑指南:HSEM的申请和释放必须成对出现。我曾经在某个中断里申请了HSEM但忘记释放,结果另一个核永远拿不到锁,整个系统卡死。调试了整整一天才找到原因。

1.6 小结

这一章我们理清了STM32H7双核架构的骨架:M7主控、M4辅助,通过共享RAM交换数据,通过HSEM保证互斥,通过MAILBOX传递事件。内存方面,D2域是双核通信的核心区域,所有共享数据必须放在这里。