ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

RT-Thread i.MX RT1180 双核 BSP 实战:两核分开跑、分开聊、从核还能被叫醒

RT-Thread i.MX RT1180 双核 BSP 实战:两核分开跑、分开聊、从核还能被叫醒

RT-Thread i.MX RT1180 双核 BSP 实战:两核分开跑、分开聊、从核还能被叫醒

【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址: https://gitcode.com/gh_mirrors/rt/rt-thread

i.MX RT1180 把两颗 Arm 核心放在同一颗芯片里,配套的 RT-Thread BSP 采取"双工程、各自编译、各自烧录"的做法,这对习惯单核 MCU 的开发者来说很容易看晕。本文以 imxrt1180-nxp-evk 为例,按任务主线推进:第 1 步完成编译烧录并验证启动,第 2 步讲透用 menuconfig 按需裁剪外设,第 3、4 步拆解主核唤醒从核、核间消息传递两条源码链路。适合用 RT-Thread 做双核项目的入门者。

先看懂分工:为什么双核就是两个工程

一颗芯片、两颗核、一主一从

两颗核的定位差别很大:

  • Cortex-M7(CM7):800 MHz 主频,带 FPU,适合电机控制、信号处理这类算力密集的活;
  • Cortex-M33(CM33):240 MHz 主频,自带 TrustZone 安全扩展,适合安全启动、系统管理、网络协议栈这类任务。

芯片还提供双千兆以太网、EtherCAT 等工业接口,并支持宽温工作,目标场景是工业控制、电机驱动。这个 BSP 里主从关系是固定的:CM33 是主核,CM7 是从核(remote),从核上电后不会自己跑起来,必须由主核把它踢起来。

同一个 BSP 下的两个独立工程

工程结构直接体现了这种分工:imxrt1180-nxp-evk目录下并排放着cm33/与cm7/,二者各自拥有完整的 rtconfig.h、构建脚本、IDE 模板和链接脚本,编译产物互不掺和。

两个工程的分工可以直接从各自的 rtconfig.h 确认:

  • CM33 侧:架构宏为ARCH_ARM_CORTEX_M33+ARCH_ARM_CORTEX_SECURE,控制台设备是uart1(LPUART1,RX/TX DMA 通道 0 和 1),见 cm33/rtconfig.h;
  • CM7 侧:架构宏为ARCH_ARM_CORTEX_M7+ARCH_ARM_CORTEX_FPU,控制台设备是uart12(LPUART12,DMA 通道 22 和 23),见 cm7/rtconfig.h。

⚠️串口别接串了:两核日志各走各的。想看 CM33 的输出,把 USB 转串口接到 UART1;看 CM7 则接 UART12,两边都按 115200-8-1-N 设置。

第 1 步:拉包、出工程、验证第一次启动

如果还没拿到代码,先 clone RT-Thread 仓库:https://gitcode.com/gh_mirrors/rt/rt-thread ,然后进入bsp/nxp/imx/imxrt/imxrt1180-nxp-evk对应核的目录,打开 ENV。

编译前唯一的硬前提是拉取软件包。该 BSP 的外设驱动来自 NXP IMXRT 驱动包(rtconfig.h 里能看到PKG_USING_NXP_IMXRT_DRIVER开关),漏掉这步,编译会直接缺库报错:

pkgs --update scons --target=mdk5 scons -j8

三行对应三条路线:scons --target=mdk5产出 Keil 工程,scons --target=iar产出 IAR 工程,scons -j8走 GCC 直接编译(8 线程并行)。工程文件会落在对应核的目录里,比如cm33/。

🔌硬件侧:数据线把开发板接到 PC(板载调试器),打开电源开关。工程默认走 CMSIS-DAP 下载,确认连接后点一下下载按钮,镜像就进板子了。

判断成功的标准:烧录完系统自动运行,串口工具打开对应口、复位一下,先看到 RT-Thread 的启动 banner(带 logo 和版本号),接着是应用输出:

  • CM33 工程打印MIMXRT1180_CM33 Hello_World,同时板载 LED(cm33/applications/main.c 中的GET_PIN(4, 27))以 500 ms 为周期翻转;
  • CM7 工程由 cm7/applications/main.c 打印MIMXRT1180_CM7 Hello_World。

✅ 两路串口先后出现各自的 Hello_World,说明两核各自拉起来了。

第 2 步:用 menuconfig 按需裁剪外设

默认配置是最小集:QSPI Flash、GPIO、DMA 加上各自的控制台串口,其余全部关闭。要启用额外资源,标准动作是在对应核的 BSP 目录执行menuconfig勾选选项,保存退出,再pkgs --update,最后重新执行 scons 生成工程,顺序不能反。

哪些外设可用

对照 cm33/board/Kconfig(cm7 侧同构),按支持状态分成三档:

  • 开箱即用:QSPI Flash XIP(代码直接驻留外部 NOR flash 执行)、flexspi 驱动、CherryUSB(设备/主机双角色可选)、NETC 网络交换(双千兆以太网)、EtherCAT、GPIO(GET_PIN接口)、LPUART1 ~ LPUART12、LPSPI1 ~ LPSPI4、LPI2C(每路 100/400 kHz 二选一)、LPADC(附多通道 DMA 示例)、Timer(含 DWT 周期计数与微秒忙等)、SDIO、RTC、FLEXCAN3;
  • 需要主从协同开关:多核启动(kick-off)与核间消息(RPMsg-Lite),第 3、4 步专门讲;
  • 暂未支持:PWM,文档标注为后续版本规划。

勾选选项时注意两条联动逻辑

菜单里不少选项不是孤立的,只看表面容易踩空:

  • DMA 是被别人带起来的:串口、SPI、I2C 没有独立的"整板 DMA"入口,各通道的 RX/TX DMA 开关一勾,会自动连带选中BSP_USING_DMA;通道号要单独填(串口范围 0~32,SPI/I2C 范围 0~31,默认按序递增)。通道配置长这样:

  • 一个选项带出一串依赖:以太网开关会自动带起 lwIP、SAL、netdev,还能进一步选 EP0 直连或交换模式(NETC_USE_SWT)、默认开启 PHY;文件系统开关下可以组合"SD 卡 + FATFS"或"QSPI Flash + LittleFS",后者依赖 FAL 分区表(见cm33/board/ports/fal_cfg.h)。

内存布局靠一个选项切换

Kconfig 末尾是内存布局三选一,对应cm33/board/linker_scripts/下三组.ld/.scf/.icf文件:

  • RAM:镜像整体放进内部 SRAM 跑;
  • FlexSPI NOR(默认):代码在外部 NOR flash 上 XIP 执行;
  • FlexSPI NOR + HyperRAM:XIP 之外再挂外部 HyperRAM 当附加内存。

配合 cm33/rtconfig.py 的机制:它扫描 rtconfig.h 里的BSP_LINKER_SCRIPT_*宏挑出匹配的链接脚本,生成 Keil 工程时还会把project.uvoptx的当前 Target 同步成对应名称。也就是说 menuconfig 里切一下选项、重新生成工程,内存布局就自动跟上,不用手改链接文件。

⚠️改完布局一定要重新生成工程,拿旧工程继续编译,链接地址和新布局对不上,板子起不来。

第 3 步:主核唤醒从核(MCMGR kickoff 流程)

CM33 侧的多核逻辑集中在 cm33/applications/multicore_thread.c。系统启动时经INIT_APP_EXPORT自动建一个名为 multicore 的线程(栈 2048 字节、优先级 10、时间片 5),由它执行"唤醒 CM7"的全套动作。可以把它类比成"先帮搭档装好启动盘,再按下开机键",每个动作都有对应源码步骤:

  1. 初始化 MCMGR:MCMGR_Init()启动多核管理单元,它相当于那枚"开机键";
  2. 预约就绪回调:MCMGR_RegisterEvent()注册远端应用就绪事件,CM7 起来后能借此回报;
  3. 取镜像信息:BOARD_GetCore1ImageAddrSize()拿到 CM7 镜像的源地址、目的地址、大小和启动地址,并逐条打印到控制台供核对;
  4. 搬运镜像:BOARD_PrepareCore1()把镜像从源位置拷到目标内存——"装启动盘";
  5. 按下开机键:MCMGR_StartCore()以同步方式拉起 CM7,同时把 RPMsg 共享内存的基址一并传给从核;
  6. 等回执:以 10 ms 为周期轮询就绪事件,直到 CM7 回报 ready 才往下走。

对应 Kconfig 里BSP_USE_MULTICORE之下的两个子开关(kick-off 与 RPMsg-Lite,均默认打开)。kick-off 的 help 文本也明确:主核拉起从核时,这套启动序列是保证初始化顺序正确所必需的。

第 4 步:在共享内存留言板上打乒乓(RPMsg-Lite 核间通信)

两核之间不直接共享数据交换,而是用 RPMsg-Lite——RPMsg 协议的轻量实现,靠一块共享内存完成点对点消息传递。打个比方:两个核共用一块"留言板",谁要说话就在板上留言,对方路过时取走。

"留言板"在源码里是 6144 字节的缓冲区rpmsg_lite_base[],这里有个隐蔽的坑:缓冲区所在的 section 随编译器变化——IAR 用 pragma location、Keil 用 section 属性、GCC 用.noinit.$rpmsg_sh_mem,三者必须与链接脚本里的段定义逐一对上。对不上的话,两核指向的内存就不是同一块,消息全部石沉大海。

主核侧初始化按四步走:以 master 身份初始化 RPMsg-Lite 实例 → 建接收队列 → 建本地端点(默认地址 40)→ 绑定名称服务。最后一步完成后主核进入等待,等 CM7 起来后在名称服务上广播服务名,回调把远端端点地址写进变量,主核立刻转入正式通信。

正式通信是一段经典 ping-pong:主核发出一个 32 位计数值,CM7 收到后回传,如此往复约 100 轮,计数到顶通信结束,随后按顺序销毁端点、队列和名称服务句柄,收尾 RPMsg。

所有参数都有默认值,不改也能跑:

  • 链路 ID:0(即RL_PLATFORM_IMXRT1180_M33_M7_LINK_ID);
  • 本地端点地址(CM33 侧):40,范围 0~65535;
  • 名称服务广播串:rpmsg-openamp-demo-channel;
  • 共享内存大小:6144 字节。

Kconfig 里配的值会写进 rtconfig.h,源码中用#ifndef兜底,所以不显式配置也能按默认参数编译运行。CM7 侧的对称实现在 cm7/applications/multicore_thread.c,想追远端逻辑时看它。

跑通演示之后:走向自己的双核应用

到这里,开发板已经走完"启动—配置—主从拉起—核间通信"的完整闭环。做自己的应用时,改动其实只有三处:把两个工程里演示用的main()换成业务逻辑;把 RPMsg 消息的载荷从 32 位计数换成自己的协议结构;如果希望一颗芯片从单镜像整体启动,还可以借助 MCUXpresso Secure Provisioning Tool 生成可启动的双核镜像,做法见 README_zh.md 的说明。

这个 BSP 的要点浓缩起来就三句话:两核是两个独立工程,构建互不干扰;主核负责唤醒从核,顺序不能颠倒;核间消息走那块 6144 字节共享内存,section 必须和链接脚本对得上。把这三句记牢,后面往 RT1180 上叠电机控制、工业网关之类的业务,路就不会走偏。

【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址: https://gitcode.com/gh_mirrors/rt/rt-thread

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表