转自 | 痞子衡嵌入式
今天给大家介绍多个MCU(i.MXRT)共享一颗Flash启动的方法。
有些特殊的客户的应用会采用多颗 i.MXRT芯片设计一主多从的硬件架构(目的不一,或仿多核 MCU 系统、或拓展 GPIO 数量),因为 i.MXRT 片内无非易失性存储器,这时候为整个系统配置合适的启动设备保证每个 i.MXRT 都能加载程序正常启动是头等大事。
因为 i.MXRT 支持的启动设备类型众多,此处我们暂讨论连接在 FlexSPI 外设上的 NOR Flash 作为启动设备。熟悉 i.MXRT 的 朋友都知道,其片内 ROM 功能强大,这种情况下系统可采用的 Flash 连接启动方式并不单一,今天作者就和大家讨论下这个话题并且给大家介绍一种创新方法:
Note:本篇是上篇,主要介绍理论方法,下篇为具体实践。
一、多i.MXRT系统启动的传统设计
1.1 每个i.MXRT独享一个Flash
首先介绍得是大家直觉里第一反应的方案,那就是每个 i.MXRT 都有专属 Flash,互不干扰,这种方案没啥好说的,就是简单粗暴,但是它也有明显的优缺点:
- 优点:
1. 系统启动快,多 i.MXRT 同时启动
2. 每个 i.MXRT 均可以 XiP 启动
3. 每个 i.MXRT 单独更新专属 Flash(可同时进行,OTA 加速且程序版本管理简单)
- 缺点:
1. 硬件设计稍复杂,PCB 面积大,Flash 数量多导致成本高
2. 程序烧写流程增多(需烧写多颗 Flash),量产不便

1.2 仅主i.MXRT连接一个Flash
当主从系统里从 i.MXRT 数量多到一定程度,上述方法里的缺点就被无限放大了,Flash 数量扎眼,量产时间过长,这时候项目经理一定会跳出来让你降成本(Flash成本,产线时间成本),于是你就开始想办法减少 Flash 数量。
此时我们可以借助 i.MXRT 的 ROM ISP 功能,由主 i.MXRT 先启动运行 Flash 里的 bootloader ,然后由 bootloader 来读取 Flash 里的应用程序并通过 ISP 接口直接加载到从 i.MXRT 的内部 SRAM 里运行(多个从 i.MXRT 可按序串行下载也可并行下载,从 bootloader 设计角度来说一般是按序串行下载),当然这种方式同样有明显的优缺点:
- 优点:
1. 简化了硬件设计,PCB 面积减小,仅需一颗 Flash
2. 简化了程序烧写流程(仅需烧写一颗 Flash),方便量产
- 中性:
1. 仅有主 i.MXRT 可以 XiP 启动,从 i.MXRT 都只能 Non-XiP 启动
2. 主 i.MXRT 统一负责 OTA 升级任务(loading 较大)
- 缺点:
1. 需要额外设计符合 ROM ISP 协议的 bootloader 让主 i.MXRT 执行去加载从 i.MXRT 程序
2. 整个系统启动时间变长(如果从 i.MXRT 程序由 bootloader 按序串行下载,UART 接口速度慢)
3. 如果从 i.MXRT 程序不同,Flash 里会分区域存储不同程序,带来复杂的 OTA 版本管理问题

二、一种共享Flash启动的创新方法
上述改进方法达到了降硬件成本的目的,但是也带来了一个无法避免的系统整体启动时间过长的问题(bootloader 实现 ROM ISP 功能如果选择 USB 接口,需要额外加 Hub,还是存在硬件成本问题;如果选择 UART 接口,速度较低,程序加载时间无法忍受)。
有没有更好的方法?作者既然这么问了,那就一定是有!我们可以创新地把一颗 Flash 同时连到多个 i.MXRT 上,主 i.MXRT 能够控制所有从 i.MXRT 的 POR_B 电平。系统上电,主 i.MXRT 先 Non-XIP 启动,然后主 i.MXRT 逐一释放从 i.MXRT 的 POR_B 让它们分时占用 Flash 去 Non-XIP 启动,现在我们再来分析下它的优缺点:
- 优点:
1. 简化了硬件设计,PCB 面积减小,仅需一颗 Flash
2. 简化了程序烧写流程(仅需烧写一颗 Flash),方便量产
- 中性:
1. bootloader 可有可无,即使有也非常简单,无需承担程序加载任务
2. 整个系统启动时间可控(分时复用 Flash 启动,FlexSPI 接口加载速度较高)
3. 系统里仅有一个 i.MXRT(可主可从)可以 XiP 启动,其余 i.MXRT 都只能 Non-XiP 启动
4. 每个 i.MXRT 均可承担 OTA 升级任务,具有一定灵活性
- 缺点:
1. 如果从 i.MXRT 程序不同,Flash 里会分区域存储不同程序,带来复杂的 OTA 版本管理问题

至此,多个i.MXRT共享一颗Flash启动的方法作者便介绍完毕了。理论介绍完毕,接下来给大家讲述实践验证的方法。
三、搭建硬件平台
理论上任意两块 i.MXRT 开发板均可以用作硬件测试平台(这里仅考虑一主一从,至于一主多从原理差不多),但是痞子衡手头 MIMXRT1010-EVK_Rev.C 开发板较多,就选用它来做测试吧,痞子衡对硬件做了如下改动:
去除从板上 U13 - Flash 保留主板上 U13 - Flash,并且飞线连接到从板 U13(低速启动仅需 CS, SCLK, IO0, IO1) 主板主芯片 GPIO_11 信号(R1870 靠主芯片端,非 D25 LED 端)连到从板主芯片 POR_B(SW3 的 Pin3/4)

四、软件代码设计
有了硬件平台,现在开始设计代码。为了简单起见,主从 i.MXRT 共用一份 App(可以在条件分支里做区分)。因为 Flash 是共享的,所以 App 必须是 Non-XIP 性质。要让芯片 BootROM 去支持 Non-XIP 启动,得使用专用上位机工具(比如痞子衡的 MCUBootUtility 或者恩智浦官方 SPT )来下载程序镜像文件,这样稍微麻烦一点。为了避免下载程序的麻烦,我们可以简单设计一个 boot_loader 工程来拷贝和跳转 boot_app 程序。
代码仓库:https://github.com/JayHeng/func-imxrt-flexspi-share-flash-boot-multiple-mcu
两个工程路径:
/boards/evkmimxrt1010/demo_apps/boot_loader
/boards/evkmimxrt1010/demo_apps/boot_app
4.1 boot_loader 工程设计
boot_loader 工程是一个 XiP 工程,由芯片 BootROM 直接启动。我们可以直接基于 SDK 里的 hello_world 来修改(flexspi_nor target),先对它的启动头 FCB 做如下修改(1-bit SPI 30MHz Normal Read Mode):

boot_app 工程是一个 Non-XiP 工程,也可以基于 SDK 里的 hello_world 来修改(debug target),它生成的 binary 直接导入到 boot_loader 工程里,具体方法可参见 《i.MXRT双核工程调试的三种方法(IAR篇)》 一文里的 2.3 通用方法:双核工程有关联,主核加载从核 小节,这里不再赘述。
boot_loader 工程里有了 boot_app 数据以及链接地址,拷贝和跳转函数设计就比较简单了,都是通用代码。最终调稳定的 boot_loader 工程可以直接在 IDE 里下载到 Flash 里运行(需调试器连接到主板 SWD 口)。
4.2 boot_app 工程设计
现在来到最核心的 boot_app 工程设计,工程还是基于 hello_world,可以从串口接收简单的字符指令(A,B...)来做不同测试,目前设计了六个测试指令:
字符指令 A(仅适用主 i.MXRT)- 将 GPIO_11 输出电平由低变高控制 POR_B 信号以释放从 i.MXRT
字符指令 B(仅适用主 i.MXRT)- 将 GPIO_11 输出电平由高变低控制 POR_B 信号以复位保持从 i.MXRT
字符指令 C(适用主/从i.MXRT)- 初始化 Flash 相关引脚为 FlexSPI 功能
字符指令 D(适用主/从i.MXRT)- 恢复 Flash 相关引脚为默认 GPIO 状态
字符指令 E(适用主/从i.MXRT)- 以一线模式擦写读 U13 Flash
字符指令 F(仅适用从 i.MXRT)- 用定时器不断翻转 GPIO_11 来驱动 D25 LED 闪烁
这里需要特别说一下指令 A 和 E,这里面涉及到主/从 i.MXRT 芯片 FlexSPI 引脚共同驱动一个 Flash 的冲突问题。当主 i.MXRT 执行 A 指令来释放从 i.MXRT 时,需要先执行 D 指令(即调用如下函数)将 FlexSPI 引脚恢复到上电默认 GPIO 状态(BootROM 启动时已将这些引脚配置成了 FlexSPI 功能),否则从 i.MXRT 可能无法正常从 Flash 启动。
void bsp_deinit_flexspi_pins(void)
{
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_06_GPIO2_IO06, 0U);
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_07_GPIO2_IO07, 0U);
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_09_GPIO2_IO09, 0U);
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_10_GPIO2_IO10, 0U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_06_GPIO2_IO06, 0x10A0U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_07_GPIO2_IO07, 0x10A0U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_09_GPIO2_IO09, 0x10A0U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_10_GPIO2_IO10, 0x10A0U);
}
C 指令和 E 指令显然是结伴而行的,当从 i.MXRT 执行过 C,E 指令且仍处于激活状态时,如果主 i.MXRT 想正常执行 C,E 指令,要么先执行 B 指令直接将从 i.MXRT 复位保持住(FlexSPI 相关引脚配置也自动复位了),要么让从 i.MXRT 先执行下 D 指令。
五、在板卡上测试
现在给板卡供电,将包含 boot_app 的 boot_loader 程序下载进 Flash,主板串口是 COM43,从板串口是 COM50,我们做一个快速测试,给主板直接发送 A 指令发现从板没有启动,但是执行过 D 指令后再发送 A 指令,这时候从板就正常启动了。此外主/从板也都能对 Flash 进行正常擦写,这证明我们的创新连接方法是切实可行的。

至此,多个i.MXRT共享一颗Flash启动的方法作者介绍完毕!
最后,深圳国际电子展将在8月26~28日举办,现在扫码,即可免费领取门票!

------------ END ------------

RTOS零中断延迟有什么特点

分享几个代码编辑器

MPU Bootloader单独编译方法详解