痞子衡嵌入式:在SBL项目实战中妙用i.MXRT1xxx里SystemReset不复位的GPR寄存器


  你们好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给你们介绍的是i.MXRT1xxx里SystemReset不复位的GPR寄存器的小妙用微信

  咱们知道稍大规模的项目代码设计通常都是多人协做完成的,在项目开始阶段的整体设计时,项目组长一般会将代码按功能进行划分,每一个功能块代码之间尽可能作到耦合度低、互不依赖、互不影响,这样各功能能够独立进行单元测试,项目得以并行开发,后期经过事先定义好的接口/协议进行功能块集成便可。架构

  但上述方法在嵌入式软件项目里有时候会遇到功能块集成后互相干扰的问题,由于嵌入式项目不少时候并非纯软件设计,也会跟片内外设资源打交道,而片内外设属于硬件范畴,硬件模块的工做是有先后状态依赖的(这点在片内时钟的配置上体现得尤为明显),出了问题传统方法是具体分析具体解决,来一个就解决一个,但任何代码的改动或者后期新特性的增长均可能会带来新的潜在干扰问题。app

  那么对于上述困境,有没有一个一劳永逸的解决方法?实际上是有的!那就是每一个功能块在设计时都不要依赖芯片初始状态,按照进入时先清理系统环境,而后作功能设计,退出时作一下系统恢复。这种方法虽然保险,可是会引入集成后项目总体运行低效的问题。今天痞子衡要在具体项目实战中介绍一种利用i.MXRT芯片内System Reset后不复位的GPR寄存器来解决属性上互斥的功能代码块集成互相干扰问题的方法。函数

1、SBL项目中的痛点

  恩智浦MCU SE团队近期一直在加班加点赶一个大项目,这个项目是为客户产品OTA需求而生的。咱们知道在线升级是每一个智能产品都不可绕开的话题,恩智浦SE团队为了方便客户在基于i.MXRT/LPC的产品上作在线升级,特别推出OTA参考设计,下面是功能架构简图:项目分为SBL + SFW两部分,SBL负责ISP本地更新(UART/USB)以及App切换管理;SFW是一个示例App,其除了客户项目业务功能外,也集成了远程更新功能(WiFi、以太、U盘、SD卡四种升级方式)。单元测试

  在SBL代码设计里,主要有两大子功能模块:一个是ISP本地更新 (isp_boot_main),另外一个是App切换管理(sbl_boot_main),其中ISP本地更新属于可选项,而App切换管理是必选。测试

  SBL的主流程是上电启动运行后,先执行ISP本地更新功能块,在超时时间内,若是有收到来自Host的ISP命令,则进入ISP命令处理,此后除非执行ISP复位命令或者有板级复位,不然不会退出ISP;ui

  若是在超时时间内没有收到ISP命令,则转到App切换管理功能块。在验证App时,若是发现Flash里有合法App,则跳转过去执行;若是没有发现合法的App,会从新返回ISP本地更新(此时是无限超时)。大概主逻辑代码以下:加密

#define COMPONENT_MCU_ISP
#define ISP_TIMEOUT (5) // in seconds

int main(void)
{
#if (defined(COMPONENT_MCU_ISP))
    // 先尝试isp本地更新(有5s超时)
    bool isInfiniteIsp = false;
    isp_boot_main(isInfiniteIsp);
#endif

    // 无本地升级则进入app跳转处理
    sbl_boot_main();
}

#if (defined(COMPONENT_MCU_ISP))
void isp_boot_main(bool isInfiniteIsp)
{
    if (isInfiniteIsp || ISP_TIMEOUT)
    {
        // isp相关功能外设初始化
        isp_boot_init();
        // 在5s超时时间内/无限超时去等待isp命令
        isp_boot_run(isInfiniteIsp);
    }
}
#endif

void sbl_boot_main(void)
{
    // 判断Flash里是否存在合法的app
    if (sbl_boot_go() != 0) 
    {
#if (defined(COMPONENT_MCU_ISP))
        // 无合法app,重入isp本地更新(无限超时)
        bool isInfiniteIsp = true;
        isp_boot_main(isInfiniteIsp);
#endif
        NVIC_SystemReset();
    }
    else
    {
        // 有合法app,跳转到app执行
        sbl_do_boot();
    }

    while (1);
}

  上述SBL设计里,你会发现ISP本地更新和App切换管理两个功能块在执行上是互斥的,是Flash里的App处理需求将它们联系在了一块儿。从软件集成角度来讲,这两个功能本不应互相影响,但实际上它们之间产生了互相影响,由于各自在设计时没有遵循进入时清理系统、退出时恢复现场的准则,因此 isp_boot_main() 在超时结束后跳到 sbl_boot_main() 对其部分验签功能产生了影响,而 sbl_boot_main() 执行后没找到合法App跳回 isp_boot_main() 时又出现ISP功能不正常的状况,咱们须要解决这个问题。.net

2、寻找i.MXRT中理想的GPR寄存器

  第一小节里描述的问题,能够经过功能块退出时恢复现场来解决,可是每一个模块代码量都比较大,使用代码去逐一恢复现场不太容易。痞子衡想到的一个好办法就是调用 NVIC_SystemReset() 函数来简单粗暴地将芯片系统复位,咱们所须要作的就是寻找一个区域,可以临时存放标志位,而且这个区域内容不受系统软复位的影响,芯片复位回来以后在SBL里增长对标志位的判断处理,处理结束后再将标志位清掉。设计

  在寻找这个不受系统软复位影响的区域前,咱们先对i.MXRT里面的电源管理架构做个基本了解。下图是i.MXRT1060的电源架构,除了USB和ADC模块特殊供电需求外,芯片一共有四种电源输入。在板级供电设计时,一般VDD_SNVS_IN须要单独一路外部输入(设计上应由电池供电),其余三路电源VDD_HIGH_IN / DCDC_IN / VDD_SOC_IN可共用一路外部输入(芯片POR_B引脚每每连在这个外部输入控制上)。

VDD_HIGH_IN:给芯片内部LDO供电
DCDC_IN:给芯片内部DCDC模块供电
VDD_SOC_IN:给芯片主系统(Core,SoC,Memory)供电

VDD_SNVS_IN:给芯片内部SNVS域相关模块供电

  从芯片系统复位等级上来分,一共有三类复位:第一类是借助Cortex-M7内核SCB模块的AIRCR寄存器中集成的SYSRESETREQ复位的支持、第二类是DCDC从新上电(POR_B复位)、第三类是总体从新上电。依据这三种不一样程度的复位,痞子衡整理了i.MXRT上全部可存放标志位的区域受不一样复位类型影响状况以下:

模块 \ 复位类型 NVIC_SystemReset() POR_B和DCDC从新上电 总体从新上电
TCM
OCRAM
IOMUXC_GPR
SRC_GPR
保持 复位 复位
IOMUXC_SNVS_GPR
SNVS_LPGPR
保持 保持 复位
Flash, eFuse 保持 保持 保持

  根据上表,咱们先排除掉NVM属性的Flash和eFuse,它们不符合临时存放、轻松读写的需求。TCM / OCRAM可用,但须要在SBL工程里作特殊处理,分配一块.noinit区,而且要肯定BootROM没有使用这个区域,用起来仍是有点麻烦。IOMUXC_GPR / SRC_GPR用起来简单,但它们已被SoC / BootROM占用了,不能随便使用,对芯片产生的影响未知。IOMUXC_SNVS_GPR / SNVS_LPGPR这两个都不错,但后者在使能加密时有时会被用来存放用户密钥。因此 IOMUXC_SNVS_GPR 寄存器才是最佳选择,这也是真正意义上开放给用户自由使用的GPR寄存器。

3、在SBL项目中使用GPR寄存器

  如今咱们找到了理想的 IOMUXC_SNVS_GPR 寄存器,那么可在SBL代码中增长两个函数 isp_cleanup_enter()、isp_cleanup_exit(),前者用于触发软复位前标记状态,后者用于软复位后读取标记的状态作相应处理。最终修改后的SBL主逻辑代码以下:

#define CLEANUP_ISP_TO_SBL (0x5A)
#define CLEANUP_SBL_TO_ISP (0xA5)

bool isp_cleanup_exit(bool *isInfiniteIsp)
{
    uint32_t flag = IOMUXC_SNVS_GPR->GPR0;
    switch (flag)
    {
        // SBL_TO_ISP软复位状况下,进入无限超时isp
        case CLEANUP_SBL_TO_ISP:
            *isInfiniteIsp = true;
            flag = 0x0;
            break;
        // 第一次上电或ISP_TO_SBL软复位状况下,直接退出isp
        case CLEANUP_ISP_TO_SBL:
        default:
            break;
    }
    IOMUXC_SNVS_GPR->GPR0 = 0x0;
    return flag;
}

void isp_cleanup_enter(uint32_t flag)
{
    IOMUXC_SNVS_GPR->GPR0 = flag;
    NVIC_SystemReset();
}

#if (defined(COMPONENT_MCU_ISP))
void isp_boot_main(bool isInfiniteIsp)
{
    // 加一级对于Reset不复位flag的判断处理
    if (!isp_cleanup_exit(&isInfiniteIsp))
    {
        if (isInfiniteIsp || ISP_TIMEOUT)
        {
            isp_boot_init();
            isp_boot_run(isInfiniteIsp);
            // 标记flag为ISP_TO_SBL,并触发软复位
            isp_cleanup_enter(CLEANUP_ISP_TO_SBL);
        }
    }
}
#endif

void sbl_boot_main(void)
{
    if (sbl_boot_go() != 0) 
    {
#if (defined(COMPONENT_MCU_ISP))
        // 标记flag为SBL_TO_ISP,并触发软复位
        isp_cleanup_enter(CLEANUP_SBL_TO_ISP);
#endif
        NVIC_SystemReset();
    }
    else
    {
        sbl_do_boot();
    }

    while (1);
}

  至此,i.MXRT1xxx里SystemReset不复位的GPR寄存器的小妙用痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

文章会同时发布到个人 博客园主页CSDN主页知乎主页微信公众号 平台上。

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就能够在手机上第一时间看了哦。

相关文章
相关标签/搜索