i.MX RT1180 双核 BSP 实战:从编译下载到 CM33 拉起 CM7 核间通信
【免费下载链接】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 把800 MHz 的 Cortex-M7和240 MHz 的 Cortex-M33(带 TrustZone 安全扩展)放进同一颗芯片,配双千兆以太网与 EtherCAT 等工业接口。RT-Thread 仓库里这块 EVK 的 BSP 采用双核独立编译:CM33 工程负责安全启动、系统管理与核间通信,CM7 工程承担算力密集任务,两个工程分别编译、分别下载,主核在运行时负责把从核的镜像搬过去并拉起。
| 对照项 | CM33 工程(cm33/) | CM7 工程(cm7/) |
|---|---|---|
| 架构宏 | ARCH_ARM_CORTEX_M33+ARCH_ARM_CORTEX_SECURE | ARCH_ARM_CORTEX_M7+ARCH_ARM_CORTEX_FPU |
| 默认控制台 | LPUART1(uart1) | LPUART12(uart12) |
| 串口 DMA 通道 | RX 0 / TX 1 | RX 22 / TX 23 |
| 启动后打印 | MIMXRT1180_CM33 Hello_World | MIMXRT1180_CM7 Hello_World |
| 附加动作 | 板载 LED(GET_PIN(4, 27))每 500 ms 翻转 | 无 |
以上均写死在各自的rtconfig.h里,两工程版本同为 RT-Thread 5.x。
二、动手前清单
硬件怎么连
用数据线把 EVK 连到 PC(板上自带 CMSIS-DAP 调试器),打开电源开关。双核调试要接两根串口线:CM33 走 UART1,CM7 走 UART12,终端里各开一个窗口,参数统一115200-8-1-N。
为什么必须先跑pkgs --update
两个核的rtconfig.h都定义了PKG_USING_NXP_IMXRT_DRIVER——编译依赖 NXP IMXRT 驱动包,包内含 MIMXRT1189 SDK 的头文件与驱动源文件,仓库里不带这些库。跳过这一步,scons 会因缺头文件直接失败,这是新手踩得最多的一坑。
进入核工程目录(如cm33/),在 ENV 工具中执行:
pkgs --update它会按rtconfig.h的包配置从在线包管理器拉取驱动包,之后才能进入编译。
三、第一次跑起来:三种工具链各自的生成与下载命令
IAR 与 MDK5:生成工程后编译下载
在核工程目录执行scons --target=iar生成 IAR 工程,双击project.eww打开,编译后点击下载即可——工程默认走 CMSIS-DAP,连好板子直接烧录。MDK5 对应scons --target=mdk5,在 Keil 里 Build and Download。两条命令的区别只在你用哪套 IDE,生成的工程文件都落在对应核目录下。
GCC:直接编译
GCC 路径不生成 IDE 工程,直接编译:
scons -j8-j8表示 8 线程并行编译,省掉该参数则单线程。产物在build/目录。下载可用 J-Link:目录里备好了evkmimxrt1180.mac、evkmimxrt1180_cm33.jlinkscript和三种内存布局对应的.ini文件。
确认两个核各自活了
下载成功后系统自动运行,复位设备。CM33 串口窗口先看到启动 banner,再看到 hello 打印,板载 LED 以 500 ms 周期闪烁:
\ | / - RT - Thread Operating System / | \ 5.2.2 build May 15 2026 17:19:35 2006 - 2024 Copyright by RT-Thread team MIMXRT1180_CM33 Hello_WorldCM7 窗口看到MIMXRT1180_CM7 Hello_World即表示从核工程独立跑通。注意:此时 CM7 是被调试器单独拉起的,主核自动拉从核要靠第四章的多核开关。
四、menuconfig 四步开启外设
四步流程:menuconfig、pkgs、scons 重新生成
- 在核工程目录(如
cm33/)下打开 ENV 工具; - 执行
menuconfig勾选外设,保存退出; - 执行
pkgs --update更新软件包(新开关可能引入新依赖); - 执行
scons --target=mdk5(或iar)重新生成工程,GCC 用户直接scons。
BSP 默认只开了最小集:GPIO、LPUART1(RX/TX DMA 全开)、QSPI Flash 与 DMA。其余全关,想用什么勾什么。
常用 Kconfig 开关速查
外设开关集中在 cm33/board/Kconfig(cm7 侧结构相同),高频项速查:
| 宏 | 默认 | 作用 |
|---|---|---|
BSP_USING_LPUART1/LPUART2~12 | LPUART1 开 | 每路串口独立使能,可单独配 RX/TX DMA 通道(0~32) |
BSP_USING_SPI1~4(LPSPI) | 关 | 每路可开 DMA 并指定通道 |
BSP_USING_I2C1/2/3/6(LPI2C) | 关 | 每路二选一:100 kHz / 400 kHz,可开 DMA |
BSP_USING_CAN→FLEXCAN3 | CAN 关,勾选后 CAN3 默认开 | FLEXCAN3 驱动 |
BSP_USING_DWT | 关 | 周期计数与微秒级忙等延时(dwt_delay_us) |
BSP_USE_HYPERRAM | 关 | 板载 HyperRAM 作扩展内存 |
BSP_USING_ETH | 关 | 自动选中 lwIP/SAL/netdev;NETC_USE_SWT选 0(EP0 直连)/1(switch),PHY 默认开 |
BSP_USING_FS | 关 | 下挂 SD 卡 FATFS 或 QSPI Flash LittleFS(后者依赖 FAL 分区表) |
BSP_USING_QSPI_FLASH | 关 | 外部 NOR Flash 读写驱动 |
另:USB(CherryUSB)可选 OTG1/OTG2 与 Device/Host 角色,Host 下还能开鼠标 demo;SDIO 可进一步设CODE_STORED_ON_SDCARD用 SD 卡作启动设备。PWM 在文档中标注"暂不支持",规划在后续版本,先别找这个菜单。
链接脚本三选一:切换内存布局
菜单末尾的Linker Script Selection三选一,对应board/linker_scripts/下不同后缀的.ld/.icf/.scf:
BSP_LINKER_SCRIPT_RAM:镜像全部放内部 SRAM 运行,rtthread_ram;BSP_LINKER_SCRIPT_FLEXSPI_NOR:默认项,代码在 FlexSPI 外部 NOR Flash 上 XIP 执行,rtthread_flexspi_nor;BSP_LINKER_SCRIPT_FLEXSPI_NOR_HYPERRAM:XIP 执行 + HyperRAM 当附加 RAM,rtthread_flexspi_nor_hyperram。
rtconfig.py会扫描rtconfig.h里的BSP_LINKER_SCRIPT_*宏自动选脚本;生成 Keil 工程时把project.uvoptx的当前 Target 同步为上面三个名字之一——menuconfig 里切一下、重新生成,布局就换好了。想跑 HyperRAM 布局记得同时勾上BSP_USE_HYPERRAM驱动。
五、两个核如何协作
主核六步拉起从核:MCMGR 启动链
在 CM33 侧勾上BSP_USE_MULTICORE(其下BSP_USE_MULTICORE_KICK_OFF与BSP_USE_MULTICORE_RPMSG_LITE均默认开)。逻辑集中在 cm33/applications/multicore_thread.c,由multicore_init()经INIT_APP_EXPORT在应用初始化阶段自动创建multicore线程(栈 2048 字节、优先级 10、时间片 5),线程内依次做六件事:
MCMGR_Init()初始化多核管理单元,CM33 以 master 身份运行;MCMGR_RegisterEvent()注册远端应用事件回调,用来接收 CM7 的就绪信号;BOARD_GetCore1ImageAddrSize()取 CM7 镜像的源地址、目的地址、大小与启动地址,并逐行打印出来供你核对;BOARD_PrepareCore1()把镜像搬运到目标内存;MCMGR_StartCore()同步启动 CM7,同时把 RPMsg 共享内存基址传给从核;- 轮询等待 CM7 上报就绪事件,每 10 ms 查一次,串口打印
CM7 core is ready!后进入通信阶段。
Kconfig 的 help 写得很直白:CM33(master)启动 CM7(remote)时,这套 Kick-off 是正确初始化顺序所必需的——从核的镜像、复位向量、共享内存参数都要由主核准备好,它自己无法凭空醒来。
RPMsg-Lite:端点、队列与名称服务怎么建
RPMsg-Lite 核间通信走共享内存。主核侧定义 6144 字节的rpmsg_lite_base[]数组,按编译器分别放进指定段——IAR 用#pragma location,Keil 用 section 属性,GCC 用.noinit.$rpmsg_sh_mem,段定义必须与链接脚本匹配,否则内存拿不到。
初始化顺序:以 master 身份rpmsg_lite_master_init()→ 创建接收队列 → 创建本地端点(地址 40)→rpmsg_ns_bind()绑定名称服务,然后原地等待从核广播。CM7 侧的从核工程里,初始化后调用rpmsg_ns_announce(),用rpmsg-openamp-demo-channel这个服务名广播自己的端点;主核的等待循环被唤醒,拿到远端端点地址,打印Got remote endpoint address。
之后进入 ping-pong 验证:双方互发 4 字节数据,计数从 0 到 100 共 100 轮,串口逐轮打印 ping/pong;收尾时依次销毁端点、队列、解绑名称服务并rpmsg_lite_deinit()。四个可配置项与源码里的#ifndef兜底值一一对应:RPMSG_LITE_LINK_ID(默认 0)、RPMSG_LOCAL_EPT_ADDR(默认 40,范围 0~65535)、RPMSG_NS_ANNOUNCE_STRING(默认rpmsg-openamp-demo-channel)、RPMSG_SHARED_MEM_SIZE(默认 6144 字节),不显式配置也按默认值跑。
六、踩坑与排错:双核 BSP 常见翻车点
- 当成了一个工程:CM33 与 CM7 是两套独立工程,各自
pkgs --update、各自编译下载;主核侧搬运的 CM7 镜像必须来自对应工具链的最新构建。 - 串口对不上:CM33 控制台是 UART1,CM7 是 UART12。把线接反后一边的窗口永远安静,先核对
rtconfig.h的RT_CONSOLE_DEVICE_NAME再怀疑系统挂了。 - 编译报缺头文件/缺库:十有八九是
pkgs --update没跑或没在正确核目录跑,见第二章。 - Keil Target 对不上脚本:改完链接脚本选项不重新生成工程,Target 还停在旧布局;或手改过 uvoptx 忘了同步,运行时表现是启动异常、变量越界,把 Target 重新对齐即可。
- 主核死等从核就绪:若主核打印卡在
Waiting for CM7 core ready signal...,优先检查 RPMsg 共享内存段名是否与链接脚本一致、RPMSG_SHARED_MEM_SIZE是否两侧相同。 - PWM 暂不支持:需要 PWM 的话先看同系列 BSP 或等版本更新。
七、收尾:这块板子还能往哪走
同目录的bsp/nxp/imx/imxrt/下还有 imxrt1021/1052/1060/1064/1170 等工程,可横向对照双核写法。应用方向上,NETC 双千兆 + EtherCAT + lwIP 的组合适合做工业通信网关;电机控制则让 CM7 跑 FPU 算法、CM33 守住安全启动与核间管理——这正是这块板子的设计意图。
【免费下载链接】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),仅供参考