简介:面向使用STM32H745芯片的双核开发者,这份基于CubeMX 6.0生成的FreeRTOS双核入门工程,完整涵盖双核初始化、外设配置与任务调度代码,可直接作为学习样板。压缩包共1286个文件,以C源文件和头文件为主体,辅以汇编启动文件、链接脚本(.icf/.sct)、CubeMX工程配置(.ioc/.mxproject)及各类静态库(.a/.lib),整体约102.35MB,目录层级规整,便于理解工程生成逻辑与各文件作用。已有3132人学习下载,适合初次接触H745双核架构或希望快速上手FreeRTOS多核编程的开发者。工程完整展示了CM7与CM4内核的启动与协作流程,覆盖外设资源分配、共享内存配置、任务创建与同步等关键环节,并保留了适合调试的工程文件与链接配置。通过对照学习可快速掌握双核FreeRTOS程序的组织方式,减少环境搭建和配置踩坑,也能为后续复杂项目提供可复用的双核开发基础,对嵌入式入门及课程设计均有参考价值。 说实话,第一次拿到STM32H745这颗芯片的时候,我心里是有抗拒的。倒不是因为它性能不够,而是因为它有“双核”——一颗Cortex-M7加一颗Cortex-M4,光是想清楚“谁先启动、谁管什么、两个核怎么说话”就够让人头大了。再加上CubeMX 6.0那会儿刚更新,界面变了,配置双核工程的路径也得重新摸索。但真正花一个下午把一个stm32h745+cubemx6.0+freertos的双核入门程序跑通之后,我发现双核其实没有想象中那么玄,关键是思路要理顺、工具链要熟、坑要提前知道。这篇东西就是把我从零跑通这套东西的完整过程写下来,包括环境、配置、代码结构、启动流程、核间通信,以及我踩过的那些最容易被新手忽略的坑。
适合谁看?已经会用STM32和FreeRTOS,但第一次接触STM32H7双核平台的工程师;或者正在犹豫要不要上双核方案、想先低成本评估一下的人。我不会贴大段官方手册翻译,只讲我实测过的东西,尽量用大白话把原理和实操都讲明白。
1. 这个项目到底在做什么?双核的定位与选型思路
1.1 STM32H745的双核价值:为什么要在一块芯片里塞两个核
STM32H745的“双核”不是一个核跑系统、一个核做冗余,而是两颗完全对等的应用处理器,一颗主频480MHz的Cortex-M7,一颗主频240MHz的Cortex-M4,通过片内总线矩阵连在一起。M7是性能主力,适合跑复杂的协议栈、图像处理、用户界面、浮点密集的算法;M4则适合做实时控制,电机控制回路、传感器采集、低速但强实时的IO响应这类活,丢给M4非常稳。
两个核的意义在于“各司其职”,而不是一个核干两个核的活。很多实时系统有个通病:逻辑复杂度一上来,实时响应就被拖累。你用单核MCU,既要跑TCP/IP协议栈,又要处理PWM换向,中断优先级怎么调都别扭。放到双核上,M7跑通信和业务逻辑,M4跑硬实时控制,天然隔离,不用在RTOS任务优先级上反复折腾。
我们做这个入门程序的目标也很明确:先让两个核各自跑起一个FreeRTOS系统,M7上建几个任务,M4上建几个任务,然后通过共享内存实现一次最简单的双向数据交换。这套流程走通了,后面再做复杂的核间通信方案就有了基础。
1.2 为什么选CubeMX 6.0 + FreeRTOS而不是裸机或手写启动文件
双核开发最大的门槛在启动阶段。M4核默认不跑,需要由M7核去“唤醒”;两个核各自有独立的中断控制器、独立的总线配置、独立的时钟树分支,这些用手写寄存器的方式配置,对新手来说量太大,很容易在初始化顺序上出错。CubeMX 6.0的价值就是把这些基础配置图形化,双核工程会生成两份独立的工程代码,一份给M7,一份给M4,各自拥有独立的main.c、FreeRTOS配置和链接脚本,启动顺序也自动处理好了。
操作系统的选择上,FreeRTOS天然适合双核入门,因为它是目前ST官方和CubeMX集成度最高的RTOS。你甚至不需要手动下载源码,CubeMX里勾一个选项,它就会把全套FreeRTOS组件拉进工程,配置项全部映射成图形界面里的下拉框,错误配置率会低很多。虽然FreeRTOS本身不支持SMP(对称多处理),在H745上也不是“一个FreeRTOS管两个核”的玩法,而是每个核独立跑一个RTOS实例,但这反而是入门最好的方式——两套系统互相独立,排查问题更简单。
2. 环境准备:板卡、软件与固件包的一次性踩平
2.1 硬件和版本清单
我这里用的是意法半导体的Nucleo-H745ZI-Q开发板。这块板子板载ST-LINK调试器,带USB转串口,还有以太网和摄像头接口,扩展能力很好,最关键的是它直接支持双核调试,可以在一个调试会话里同时看到M7和M4的执行状态。
软件方面,我当时用的是STM32CubeMX 6.0,配套的固件包是STM32Cube FW_H7_V1.9.0(不同版本功能基本一致,但现在新版本CubeMX可能已经带更新的H7包,建议用官方最新稳定版)。编译器用ARM Compiler或者GCC都可以,IDE我用的是STM32CubeIDE,因为它和CubeMX同源,生成的工程可以直接双击导入,省去很多环境配置的麻烦。
2.2 CubeMX 6.0里双核工程的几个关键入口
打开CubeMX 6.0创建新工程时,芯片型号填STM32H745ZIT6,这一步和普通工程没区别。但真正开始配置后你会发现,界面顶部多了一个“CORTEX_M7”和“CORTEX_M4”的切换按钮,这就是双核工程的入口。你一定记住:时钟树、GPIO、外设、FreeRTOS这些配置,全部可以按核分开设置,必须先选对当前在配置哪个核。
我第一次没注意这个,在M7页面把以太网配好了,然后在M4页面又配了一遍同样的引脚,结果两个核同时抢占同一个外设,编译不报错,运行完直接死机。双核配置的基本原则是:共享外设(比如某些GPIO、DMA、以太网MAC)只能由一个核来初始化,另一个核只通过核间通信去使用它,不能两边都碰。
2.3 时钟树与调试接口的配置要点
时钟树的配置是双核工程里最容易出问题的一步。H745包含两个内核时钟域,M7的SYSCLK最高480MHz,M4的SYSCLK最高240MHz。CubeMX里的时钟树界面会把这些分支都画出来,你需要先选HSE(外部高速晶振)作为时钟源,再配置PLL来生成两个核各自的时钟。
我的建议是在入门阶段直接把时钟方案交给CubeMX的自动解算功能,手动改的地方越少越好。但在调试器设置上,一定要在“Debug”选项里选择“Serial Wire”,这样ST-LINK才能通过SWD接口连到芯片。双核调试时,如果这里没开启,后面会发现只能下载程序,执行调试时断点根本停不下来。
3. 双核是如何“握手”的:启动流程与核间通信原理
3.1 上电后到底谁先跑?M7和M4的启动关系
STM32H745上电后,默认只有M7核会从Flash取出向量表开始执行,M4核的指令流是停住的。这不是芯片设计有问题,而是有意为之——系统希望由M7核负责整体系统的初始化和资源分配,确认所有环境和数据都没问题后,再释放M4核。
CubeMX生成的M7工程里,已经自动带了一段释放M4核的代码。流程大致是:M7核先把M4核的向量表基地址写到M4核的VTOR寄存器,同时在内存中准备好M4核启动需要的初始栈和初始PC值,最后通过RCC控制器里对应M4核的复位寄存器释放复位。整个过程完成后,M4核才会开始执行它自己的main函数,M7核则继续跑自己的逻辑。
这个机制的坑点是:如果你想调试M4核里的代码,必须先把M7核跑起来,或者用调试器直接手动触发M4的复位。很多新手在STM32CubeIDE里只连接M4核心,结果根本进不了main,一脸懵。当时我也卡了很久,后来才意识到双核调试的前提是先让M7把M4“放出来”。
3.2 核间通信的四个层次:从简单标志到RPMsg
两个核之间要交换数据,在H745上大概有四个层次的做法:
第一层,最简单,就是共享内存加标志位。定义一个双方都能访问的全局结构体,M7往结构体里写数据,写完把一个标志变量置1,M4轮询到这个标志就读取数据,读完再把标志清0。这个方案门槛最低,但要注意临界区保护,否则两边同时读写会数据错乱。
第二层,用硬件信号量HSEM做保护。H745内置了32个硬件信号量,专门用于多核之间对共享资源的互斥访问。它比软件临界区靠谱得多,因为硬件信号量的“获取”和“释放”是原子操作,不会被另一个核打断。
第三层,用IPCC。IPCC是ST为双核通信设计的专属硬件外设,支持双向中断触发。它本质上是一个可以用来发“消息通知”的寄存器,配合共享内存,就能实现“M7发数据并通知M4,M4被中断唤醒后读取”的异步通信模式。这已经是工程上非常实用的方案了。
第四层,跑官方OpenAMP/RPMsg协议栈。这是目前最完善的双核通信方案,它会帮你在两个核之间建立socket化的消息通道,用起来很像网络通信里的收发消息。但是OpenAMP的代码量很大,调试复杂度高,入门阶段不建议直接上。
我这个入门程序选的是第一层加第二层,即共享内存配合HSEM做互斥,既能看到双核通信的本质,又不会因为引入复杂协议栈而降低学习体验。
3.3 共享内存的正确用法:地址、缓存一致性与访问保护
共享内存不是随便找一块RAM就能用。在H745中,内存被划分成多个区域,不同的RAM块有不同的访问属性和性能特征。M7核自带一级指令缓存和一级数据缓存,而M4核没有数据缓存,这就带来一个致命问题:如果M7往共享区写入了数据,但数据只停留在M7的数据缓存里,还没有真正回写到物理RAM,那么M4直接去读这块内存,读到的可能是旧值。
解决思路有两种。第一种是M7写入共享区之前主动执行一次数据缓存清理操作,把缓存里的数据强制刷新到RAM;M7读取共享区之前,再主动执行一次数据缓存无效化操作,确保自己读的是RAM里的最新数据而不是缓存里的旧数据。第二种更干净:直接用MPU把共享内存区域配置成“不可缓存”,这样M7对这块地址的访问永远直达RAM,不需要考虑缓存的副作用。
在实际工程里,我推荐两种结合使用:简单共享区用MPU配置为不可缓存,彻底屏蔽一致性隐患;大块数据缓冲区可以保持缓存属性,通过手动清理和无效化来提升性能。入门程序阶段,用MPU改成不可缓存是最省心的。
4. 从CubeMX生成的“入门双核FreeRTOS程序”实测
4.1 生成代码后的工程结构到底长什么样
CubeMX生成双核工程后,工作区里会出现两个互相独立的包含文件夹的工程,一个后缀是CM7,另一个后缀是CM4。这两个工程共享同一个CubeMX的.ioc配置文件,但各自的代码是完全独立编译的。CM7工程的main.c里有释放M4的启动代码,还会有一个Main.c文件(注意大小写),里面是M7的入口逻辑;CM4工程则只有自己独立的main.c和Freertos配置。
每个工程的Core文件夹下都有各自的freeRTOS配置头文件,里面可以独立配置各自的任务数量、堆栈大小、时间片轮转策略等。这意味着M7上跑的任务和M4上跑的任务互不干扰,M7堆栈耗尽了不会拖累M4,反之亦然。这也是双核系统在RTOS层面最大的优势之一。
4.2 编写M7任务和M4任务的完整流程
下面是我这个入门程序里实际使用的流程,你可以照着走一遍。
第一步,确认双核都能进入主循环。先在CM7工程里把释放M4的代码打开(CubeMX默认会生成,位置在main函数开头的SystemClock_Config之后),然后在CM4工程里加一个空循环,分别编译两个工程,先后下载,然后在两处main函数入口各打一个断点。如果能同时停住,说明双核启动链路已经通了。
第二步,在CM7的FreeRTOS里创建两个任务,一个任务叫M7_Task_Write,负责往里计数;另一个任务叫M7_Task_LED,负责翻转板载LED。我建议把LCD之类的初始化放到M7任务启动后再做,避免系统启动阶段出现访问冲突。
第三步,在CM4的FreeRTOS里创建一个任务,任务里轮询读取共享区域的数据。共享区我用了一个结构体定义,左侧是CM7工程来写,右侧是CM4工程来读,代码长这样:
// 这个头文件要同时放到CM7和CM4工程里 typedef struct { volatile uint32_t magic; volatile uint32_t counter; volatile uint32_t flag_read; volatile uint32_t flag_write; } __attribute__((packed)) SharedData_t; #define SHARED_DATA_ADDR 0x30000000U // D2域SRAM,双核均可访问 SharedData_t *shared_data = (SharedData_t *)SHARED_DATA_ADDR;我建议你在CubeMX里预先在内存配置工具中,把0x30000000这段SRAM标注为共享区域,并在MPU配置里把它设成“Non-cacheable”。这样代码层面就不需要手动清理缓存,比较适合初学者。
第四步,在两个核的FreeRTOS主任务里各自调用上面的共享结构体指针,执行读写,同时用HSEM加锁,保证同一时刻只有一个核在操作这个结构体。简单说来就是:拿信号量、写数据、释放信号量、另外一边拿信号量、读数据、释放信号量。
4.3 效果验证:一例最简单的核间协作演示
为了直观看到双核通信成功,我设计的实验很简单:M7的任务每500ms把计数器加一,并把它写入共享区;M4的任务每200ms读取一次共享区,如果发现计数器的值和上一次读的不一样,就打印一行日志到串口。
串口打印内容大概是:
[M4] read from M7: counter=15 [M4] read from M7: counter=16这个过程中没有任何一根线从M7引脚接到M4引脚,数据是完全通过片内总线走的。你亲眼看到M7的增量在M4的日志里实时出现的时候,双核的概念就从抽象落到了实处。在此基础上,把两个LED分别由M7和M4控制,当计数器到达特定值时切换一个LED状态,就能做出一个非常直观的“双核协作”演示。
5. 常见问题与排查技巧实录
5.1 下载程序时提示找不到目标或一直复位
这是一个非常典型的问题。H745是双核,当ST-LINK连接芯片做程序下载时,如果连接配置不对,调试器会尝试复位整个芯片,而复位后M4核是停住的,M7核则跑起来并可能立即进入用户程序,导致调试器的连接状态被破坏,出现“连接不稳定”“下载失败”之类的报错。
我的解决方式是:在STM32CubeIDE的调试配置里,把连接模式改成“连接时不要复位目标”,让ST-LINK直接attach到目标上,而不是从零复位所有核。另外,下载时如果只下载M7工程,M7不会自动让M4跑你的新程序,需要进入调试会话后手动复位M4。所以我的习惯是先下载M7,再单独切换到M4工程下载,然后重启设备,顺序不要搞反。
5.2 M4任务一直不执行,程序卡在某个地方
M4跑不起来,90%的情况是M7根本没有成功释放M4复位。这时先检查CubeMX生成的释放M4代码有没有真的被执行到,很多人因为把这段代码放到了某个外设初始化之后,而该外设初始化本身就把M7卡住了,自然就轮不到唤醒M4。
另一个常见原因是M4的向量表地址不对。CM4工程链接脚本里设置的Flash起始地址必须和M7实际给它设置的VTOR地址完全一致,哪怕差了4字节,M4启动后都会跑飞。你可以通过查看M4工程编译后的map文件,找到__Vectors符号的实际地址,再对比一下代码里给RCC寄存器写入的地址值。
5.3 两个核同时访问同一个外设,板子直接死机
共享外设之所以叫共享,是因为它们不支持两个内核同时访问。最典型的例子是DMA和GPIO的某些控制寄存器,M7和M4同时去改,总线矩阵会出现仲裁冲突,轻则数据错误,重则卡死。
我的原则是:每个外设只分配给一个核,在CubeMX中就把另一个核对它的访问权限配置为“不启用”。如果确实需要交叉访问,所有调用必须走HSEM保护,并且要控制临界区的长度,不能在持有信号量时做耗时很长的阻塞操作。
5.4 高频通信时偶尔收到乱码,或者数据看起来被撕裂
如果你已经用共享内存做了双核通信,发现偶尔读到半截数据,比如一个32位数读出来一半是新的、一半是旧的,那基本可以确定是Cache一致性问题或者上了非原子访问。用HSEM只能保证“拿锁前不做临界操作”,不能解决“拿锁前数据缓存没回写”的问题。
我强烈建议入门阶段直接把共享内存映射为Non-cacheable,把缓存问题用最简单的方式根除。如果你后续要在大数据块通信场景里追求更高吞吐,再考虑开启MPU区域的Cache并用库函数清理缓存。具体做法是在CubeMX的MPU配置页面增加一个Region,起始地址设为共享内存起始地址,大小对齐,属性选“Normal memory, Non-cacheable”,然后把整个MPU功能使能。
如果出现两个核共享内存访问权限冲突,也可以看看是不是MPU配置的Region重叠了,M7的Region和M4的Region不要都覆盖同一个地址段。
6. 从入门双核到生产级项目的四点建议
6.1 系统设计阶段就要划分“域”,不要等写代码再分
双核项目最怕的是到了开发中后期才发现两个核对资源的需求是纠缠在一起的。你先想清楚哪些功能跑M7、哪些跑M4,定义好所有的共享资源清单(哪些内存区域、哪些外设、哪些全局状态),把接口协议写在设计文档里,再让两个核分别去找CubeMX配置。接口字段用什么样的数据结构去定义,多宽、对齐方式是什么,这些都要在写第一行代码之前定死。
6.2 通信协议尽量“显式化”,少用隐含的全局变量
虽然共享内存能直接读全局变量,但在双核环境下,两个编译单元里的全局变量地址在链接时是各自独立管理的,它们根本不会共用同一份变量。所以双核通信一定要建立在固定的物理内存地址上,通过结构体指针进行访问,不要指望两边定义一个同名全局变量就能互通。通信时尽量用显式的消息头,包含发送方ID、消息类型、数据长度、校验码,方便线上查问题。
6.3 从入门起就保持RTOS调度的“可观察性”
FreeRTOS自带任务运行状态统计和堆栈高水位统计,我建议从入门程序开始就打开这两项功能。启动后跑几分钟,打开调试器里的FreeRTOS插件或串口日志,看每个任务的实际运行时间、剩余堆栈深度、切换次数。因为FreeRTOS在双核上是两套独立系统,统计信息也要分开看,M7侧看M7任务,M4侧看M4任务,对照需求是否满足,这个过程能帮你尽早发现设置上的问题。
6.4 把官方示例当起点,但一定要自己改过一遍
ST官方给H745提供了一批双核例程,比如OpenAMP通信例程、双核LED闪烁例程,它们每个都能跑通,代码也规范,但对于入门来说直接拿起来跑一遍之后,你大概率还是不知道底层发生了什么。我的建议是用官方例程验证环境没问题后,赶紧清空重做,一步步用CubeMX配出一个只含共享内存加HSEM的最小程序,自己亲手把M7和M4串联起来。只要这一步走通了,你对双核的控制力就从“复制粘贴能跑”变成了“我知道它在怎么跑”。
我个人的体会是,每个用双核的人第一次成功建立核间通信时,都会莫名兴奋——就是那种两个核像两个人一样在隔空喊话的感觉。但兴奋只停留在第一个晚上就够了,第二天你就该想着怎么把通信做成带协议、带保护、带错误检测的正式设计,毕竟嵌入式工程师最容易欠下的,就是最后这一段“从demo到产品”的账。这篇入门程序的整体方案,就是帮你把头和尾都踩一遍,剩下的,就该你动手了。
本文还有配套的精品资源,点击获取