☰
RT-Thread i.MX RT1180 EVK 双核 BSP 完整实战:从双核拉起到核间通信
2026/11/4 2:20:41 网站建设 项目流程

RT-Thread i.MX RT1180 EVK 双核 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

想在双核芯片上跑起实时系统,还让两个核互相通信?这篇文章以 RT-Thread 的 i.MX RT1180 EVK 双核 BSP 为例,带你完整走一遍:先把 RT-Thread 在开发板上点亮,再用 MCMGR 让 CM33 主核拉起 CM7 从核,最后基于 RPMsg-Lite 在共享内存上跑通 ping-pong 核间通信。读完并动手做完,你将具备这块板子上独立编译、按需扩展和双核协作开发的完整能力。

认识芯片与双工程布局 🧩

i.MX RT1180 是一颗面向工业控制、电机驱动场景的跨界 MCU:一颗 800 MHz 的 Cortex-M7 负责算力密集的任务,一颗 240 MHz 的 Cortex-M33(带 TrustZone 安全扩展)负责安全启动与系统管理。板子还配有双千兆以太网(NETC 交换控制器)、EtherCAT 等工业通信接口,并支持宽温工作。

在这个 BSP 里,分工被固化为:CM33 是主核(master),CM7 是从核(remote)。两个核各自拥有完整的工程,目录结构如下:

bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/ ├── cm33/ # 主核工程 │ ├── applications/ # main.c、multicore_thread.c、mnt.c │ ├── board/ # MCUX_Config 时钟引脚配置、linker_scripts、ports │ ├── xip/ # FlexSPI NOR 启动配置 │ └── rtconfig.h / rtconfig.py └── cm7/ # 从核工程,结构与 cm33 对称

两个rtconfig.h的差异一眼就能看出各自的"人格":

配置项cm33 工程cm7 工程
架构宏ARCH_ARM_CORTEX_M33+ARCH_ARM_CORTEX_SECUREARCH_ARM_CORTEX_M7+ARCH_ARM_CORTEX_FPU
控制台设备uart1(LPUART1,RX/TX DMA 通道 0/1)uart12(LPUART12,RX/TX DMA 通道 22/23)
缓存未启用RT_USING_CACHE(M7 数据/指令缓存)
SOC 宏SOC_MIMXRT1189CVM8C_CM33SOC_MIMXRT1189CVM8C_CM7
公共默认均开启BSP_USING_QSPIFLASH、BSP_USING_DMA、BSP_USING_GPIO,链接脚本默认BSP_LINKER_SCRIPT_FLEXSPI_NOR(XIP)同左

记住两个控制台的对应关系——CM33 看 UART1,CM7 看 UART12,后面双核调试全靠它区分日志来源。

五步点亮第一个 RT-Thread 🔌

  1. 拉取仓库。在本地执行:
git clone https://gitcode.com/gh_mirrors/rt/rt-thread
  1. 更新依赖包。这个 BSP 依赖 NXP IMXRT 驱动软件包(PKG_USING_NXP_IMXRT_DRIVER),进入bsp/nxp/imx/imxrt/imxrt1180-nxp-evk/cm33/打开 ENV 后必须先执行:
pkgs --update

跳过这一步,编译会直接因缺库失败——这是本 BSP 第一个也是最常见的坑。

  1. 生成或编译工程。三套工具链任选其一:
scons --target=iar # 生成 IAR 工程 scons --target=mdk5 # 生成 Keil MDK5 工程 scons -j8 # GCC 直接编译(8 线程并行)
  1. 连接硬件并下载。用数据线把开发板接到 PC(板上自带调试器),打开电源开关。以 IAR 为例:双击工程目录下的project.eww,编译通过后点击下载即可——工程默认走 CMSIS-DAP 烧录。

  2. 串口验证。把板子对应串口接到终端工具,参数设为 115200-8-1-N,复位开发板,你会看到启动 banner:

\ | / - RT - Thread Operating System / | \ 5.2.2 build May 15 2026 17:19:35 2006 - 2024 Copyright by RT-Thread team

随后cm33/applications/main.c打印MIMXRT1180_CM33 Hello_World,并以 500 ms 周期翻转GET_PIN(4, 27)对应的板载 LED。看到 banner、LED 闪烁,说明主核链路通了。

CM7 工程完全同法操作(独立编译、独立下载),只是验证时改看 UART12,应用打印的是MIMXRT1180_CM7 Hello_World。

能力扩展:逐项打开板子资源 ⚙️

默认配置只开了最小串口与 GPIO。想多用一块板载资源,套路永远是四步:menuconfig勾选 → 保存 →pkgs --update→scons --target=mdk5(或 iar)重新生成工程。开关全部集中在cm33/board/Kconfig与cm7/board/Kconfig(两核菜单大体对称),按"想用哪个功能"来查:

想用勾哪里说明
双千兆以太网Onboard → Ethernet:BSP_USING_ETH自动带入 lwIP/SAL/netdev;NETC_USE_SWT选 0(EP0 直连)或 1(switch 模式),PHY 默认开启
文件系统Onboard → File System:BSP_USING_FS下挂BSP_USING_QSPI_FLASH_FS(NOR Flash 上的 LittleFS,依赖 FAL 分区表,见cm33/board/ports/fal_cfg.h)或BSP_USING_SDCARD_FATFS(SD 卡 FATFS)
USBOn-chip → USB (CherryUSB):BSP_USING_USB先选控制器 OTG1/OTG2,再选 Device/Host;Host 模式可追加鼠标 shell 演示(usbh_mouse_demo.c)
更多串口On-chip → UARTLPUART1~12 逐路使能,每路可单独开 RX/TX DMA 并指定通道号(0~32)
I2C / SPI / CANOn-chip → I2C / SPI / CANLPI2C1/2/3/6 可选 100 kHz 或 400 kHz;LPSPI1~4 逐路开 DMA;CAN 对应 FLEXCAN3
SDIO / RTC / DWTOn-chip 对应菜单SDIO 下可勾CODE_STORED_ON_SDCARD让 SD 卡充当启动设备;DWT 提供dwt_delay_us等微秒级忙等接口
核间通信Onboard → Multicore:BSP_USE_MULTICORE子项 Kick-off 与 RPMsg-Lite 默认全开,详见下一节

另外两个值得留意的开关:EtherCAT(BSP_USING_ETHERCAT,在 CM7 工程的菜单里)和 HyperRAM(BSP_USE_HYPERRAM,外部 HyperRAM 扩内存)。至于 PWM,目前 BSP 尚未支持,规划中,先别在菜单里找它。

切换内存布局:链接脚本怎么选

menuconfig 末尾的 Linker Script Selection 决定镜像落位,两个工程可选项略有差异:

选项布局含义
BSP_LINKER_SCRIPT_RAM整个镜像放在片内 SRAM 运行
BSP_LINKER_SCRIPT_FLEXSPI_NOR(默认)代码经 FlexSPI 从外部 NOR Flash XIP 执行
BSP_LINKER_SCRIPT_FLEXSPI_NOR_HYPERRAMXIP 执行 + HyperRAM 作附加 RAM
BSP_LINKER_SCRIPT_HYPERRAM(仅 CM7)片内 SRAM + HyperRAM

对应文件在cm33/board/linker_scripts/、cm7/board/linker_scripts/下,.ld(GCC)、.scf(Keil)、.icf(IAR)三种后缀一一对应。rtconfig.py会扫描rtconfig.h里的BSP_LINKER_SCRIPT_*宏自动挑选脚本;生成 Keil 工程时还会调用update_keil_active_target(),把project.uvoptx的当前 Target 同步为rtthread_ram/rtthread_flexspi_nor/rtthread_flexspi_nor_hyperram——所以你只需在 menuconfig 里改选项再重新生成,无需手动切 Target。

机制深挖:CM33 如何拉起 CM7、两核如何对话 🔍

什么是 Kick-off

从核上电后并不能自己启动,需要主核替它完成"搬运镜像 + 喂启动地址 + 释放复位"这一套动作,NXP 把这件事封装在 MCMGR(Multi-Config Manager)驱动里,即 Multicore Kick-off。BSP_USE_MULTICORE_KICK_OFF的帮助文本说得很直白:CM33(master)启动 CM7(remote)时,这是正确初始化顺序所必需的。

主核侧的六步启动序列

全部逻辑在cm33/applications/multicore_thread.c中。multicore_init()通过INIT_APP_EXPORT在应用初始化阶段创建名为multicore的线程(栈 2048 字节、优先级 10、时间片 5),线程内multicore_kickoff_init()依次执行:

  1. MCMGR_Init()初始化多核管理单元;
  2. MCMGR_RegisterEvent(kMCMGR_RemoteApplicationEvent, ...)先注册 CM7 就绪事件回调——注意必须在启动从核之前注册,否则收不到就绪信号;
  3. BOARD_GetCore1ImageAddrSize()解析烧录容器,找出 CM7 可执行镜像的源地址、目的地址、大小与启动地址(容器解析逻辑在cm33/board/board.c,会遍历每个 container header 的 image entry,按 Type/核心 ID 匹配 CM7 executable);
  4. BOARD_PrepareCore1()准备从核:若 CM7 走 ITCM,则把镜像 memcpy 进 ITCM 并做 DSB/ISB;若是 XIP 运行,则初始化 TCM ECC 等前置状态(含 TCM ECC fuse 场景下的假向量表 workaround);
  5. 同步启动 CM7,并借第三个参数把共享内存基址"递"给从核:
(void)MCMGR_StartCore(kMCMGR_Core1, (void *)core1_boot_addr, (uint32_t)rpmsg_lite_base, kMCMGR_Start_Synchronous);
  1. 每 10 ms 轮询一次事件数据,直到 CM7 通过MCMGR_TriggerEvent上报APP_RPMSG_READY_EVENT_DATA为止。

从核侧的"反向握手"

CM7 工程里的cm7/applications/multicore_thread.c逻辑正好对称:先用MCMGR_GetStartupData(kMCMGR_Core0)反复轮询取回主核递过来的共享内存基址,再经platform_patova()完成地址视角转换,以 remote 身份调用rpmsg_lite_remote_init()初始化;初始化一完成就MCMGR_TriggerEvent回报就绪,然后rpmsg_lite_wait_for_link_up()阻塞等待链路建立,最后用rpmsg_ns_announce()向全网广播自己的服务名rpmsg-openamp-demo-channel。

RPMsg-Lite:共享内存上的消息总线

📡 RPMsg-Lite 是 RPMsg 协议的轻量实现,不走中断风暴式的 mailbox,而是把消息队列直接建在一块双核都能访问的共享内存上:

  • 共享内存区:主核侧定义 6144 字节的rpmsg_lite_base[],并要求它落入与链接脚本匹配的段。三种编译器各有一套写法:IAR 用#pragma location = "rpmsg_sh_mem_section",Keil 用__attribute__((section("rpmsg_sh_mem_section"))),GCC 用.noinit.$rpmsg_sh_mem——改段名时三处必须与链接脚本同步;
  • 主核初始化(rpmsg_lite_init(),cm33 侧):rpmsg_lite_master_init()→ 建接收队列 →rpmsg_lite_create_ept()创建本地端点(默认地址 40)→rpmsg_ns_bind()绑定名称服务后阻塞等待,直到 CM7 广播把远端端点地址写进remote_addr;
  • ping-pong 通信:rpmsg_lite_communication()里 CM33 先发首个 ping,之后 100 轮rpmsg_lite_send()发送 +rpmsg_queue_recv()阻塞收应答,CM7 侧收到后数据自增再回发——一个最简单的连通性压力测试;
  • 清理:rpmsg_lite_cleanup()依次销毁端点、队列、解绑名称服务并rpmsg_lite_deinit()。

相关参数都可在BSP_USE_MULTICORE_RPMSG_LITE下配置,源码里另有#ifndef兜底,不显式配置也能按默认值跑:

配置项默认值说明
RPMSG_LITE_LINK_ID0链路 ID,即RL_PLATFORM_IMXRT1180_M33_M7_LINK_ID
RPMSG_LOCAL_EPT_ADDRCM33 侧 40 / CM7 侧 30各自本地端点地址,范围 0~65535
RPMSG_NS_ANNOUNCE_STRINGrpmsg-openamp-demo-channel从核广播服务用的名称服务字符串
RPMSG_SHARED_MEM_SIZE6144 字节共享内存区大小(CM33 侧菜单配置)

为什么这么设计

主从划分让"谁先跑、谁负责搬运"有唯一答案;用共享内存承载消息,省去了从核侧额外的通信硬件;名称服务则把"对端端点在哪"变成可广播、可发现的动态信息——将来你加第三条通道、换端点地址,都只需改 announce 字符串,不用硬编码地址。这正是这套 BSP 可作为自研双核应用骨架的原因。

避坑清单 ⚠️

  • pkgs --update必须在第一次编译前执行,且每次 menuconfig 改动后建议再跑一次;
  • CM33 与 CM7 是两个完全独立的工程:分别生成、分别编译、分别下载,顺序上先让 CM33 起来再由它拉起 CM7;
  • 控制台不互通:CM33 日志在 UART1、CM7 日志在 UART12,双核联调时两个终端都要开着,否则"看不到从核打印"多半是接错了串口;
  • 切换链接脚本后必须重新生成工程,Keil 工程的当前 Target 会自动同步,但手动改动project.uvoptx会被下次生成覆盖;
  • RPMsg 共享内存段名与链接脚本必须匹配,换编译器时确认rpmsg_lite_base[]的段属性写法切换到了正确分支;
  • 中文 README 提到 Kick-off"将在后续版本支持",但当前仓库源码中multicore_thread.c已完整实现主从双向逻辑,以源码为准;
  • 需要整板双核镜像时,除了独立编译两核,还可以用 NXP 的 MCUXpresso Secure Provisioning Tool 生成可启动的融合镜像。

写在最后 🎯

回看这条闭环:pkgs --update拉依赖 → 三套工具链任选编译 → CMSIS-DAP 下载 → 双串口验证 → menuconfig 按需扩资源 → MCMGR 拉起从核 → RPMsg-Lite 跑通消息。i.MX RT1180 EVK 的这份双核 BSP 是 RT-Thread 仓库里少见的"双核独立编译 + 主从协作"完整范例,也是工业控制、电机驱动、双千兆网关类项目的理想起点:算力密集的实时任务丢给 CM7,安全启动、网络协议栈留在 CM33,两核之间用 RPMsg-Lite 对话。

下一步你可以从两个方向延伸:把 ping-pong 换成自己定义的消息结构,实现传感器数据跨核上报;或者横向对比同系列其它双核/单核 BSP——bsp/nxp/imx/imxrt/目录下还有 imxrt1021、imxrt1052、imxrt1060、imxrt1064、imxrt1170 等工程,外设驱动与 Kconfig 组织方式大同小异,可作对照参考。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询