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

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

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

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

一、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;

  如果在超时时间内没有收到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功能不正常的情况,我们需要解决这个问题。

二、寻找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寄存器。

三、在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主页知乎主页微信公众号 平台上。

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

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

  1. 痞子衡嵌入式:关于i.MXRT中FlexSPI外设lookupTable里配置Normal read的一个小误区

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT中FlexSPI外设lookupTable里配置Normal read的一个小误区. 关于串行四线NOR Flash,当其作 ...

  2. 痞子衡嵌入式:聊聊系统看门狗WDOG1在i.MXRT1xxx系统启动中的应用及影响

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是系统看门狗WDOG1在i.MXRT1xxx系统启动中的应用及影响. 软件看门狗模块(WDOG)在 MCU 应用里可以说是非常基础的功能模 ...

  3. 痞子衡嵌入式:理解i.MXRT中FlexSPI外设lookupTable里配置访问行列混合寻址Memory的参数值

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT中FlexSPI外设lookupTable里配置访问行列混合寻址Memory的参数值. 关于 FlexSPI 外设的 loo ...

  4. 痞子衡嵌入式:从头开始认识i.MXRT启动头FDCB里的lookupTable

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT启动头FDCB里的lookupTable. 一个MCU内部通常有很多外设模块,这些外设模块是各MCU厂商做差异化产品的本质, ...

  5. 痞子衡嵌入式:系统时钟配置不当会导致i.MXRT1xxx系列下OTFAD加密启动失败

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是系统时钟配置不当会导致i.MXRT1xxx系列下OTFAD加密启动失败问题. 我们知道,i.MXRT1xxx家族早期型号(RT1050/ ...

  6. 痞子衡嵌入式:了解i.MXRTxxx系列ROM中灵活的串行NOR Flash启动硬复位引脚选择

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRTxxx系列ROM中灵活的串行NOR Flash启动硬复位引脚选择. 关于 i.MXRT 系列 BootROM 中串行 NOR ...

  7. 痞子衡嵌入式:对比i.MXRT与LPC在RTC外设GPREG寄存器使用上的异同

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是对比i.MXRT与LPC在RTC外设GPREG寄存器使用上的异同. 本篇是 <在SBL项目实战中妙用i.MXRT1xxx里Syst ...

  8. 痞子衡嵌入式:i.MXRT1010, 1170型号上不一样的SNVS GPR寄存器读写控制设计

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1010, 1170型号上不一样的SNVS GPR寄存器读写控制设计. 痞子衡之前两篇文章 <在SBL项目实战中妙用i ...

  9. 痞子衡嵌入式:改动i.MXRT1xxx里IOMUXC_GPR寄存器保留位可能会造成系统异常

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是改动i.MXRT1xxx里IOMUXC_GPR寄存器保留位可能会造成系统异常. 痞子衡的嵌入式技术交流群里有一位非常活跃的朋友(网名:文 ...

随机推荐

  1. js的13种继承

    js继承的13种方式 也可以说只有12种,ES6的extend 也是12种方法之一-寄生继承的语法糖 1.原型链法 代码示例 Child.prototype = new Parent(); 所属模式: ...

  2. openpyxl 库

    1. 简介与安装 2.具体示例 2.1 获取workbook与sheet对象 2.2 访问单元格及其值 2.3 写数据 2.4 设置样式 1. 简介与安装 openpyxl 简介 openpyxl 是 ...

  3. kubectl简介

    kubectl简介 kubectl是操作k8s集群的命令行工具,安装在k8s的master节点,kubectl在$HOME/.kube目录中查找一个名为config的文件, 你可以通过设置Kubeco ...

  4. 关于Spring Data JPA 多表查询 返回自定义Vo的问题记录

    这两天开了一个新项目,使用SpringBoot+SpringData,  刚做了一个小功能,都是一张表的操作没什么问题,今天设计到了两张表联查,两张表各取了几个字段,组合成了一个vo, 当我用原生sq ...

  5. dalvik浅析三:类加载

    android的安装包是个apk文件,其中包含dex.资源及签名文件.其中dex是包含程序运行的类代码,而android是运行在dalvik(5.0之前)上的.本篇我们就来看下dalvik是如何把de ...

  6. <input type="file" id="fileID">文本框里的值清空方法

    一般情况下,不允许通过脚本来对文件上传框赋值. 下面是一个变通的方法.就是创建一个新的input type="file" 把原来的替换掉. <!DOCTYPE html PU ...

  7. Portswigger web security academy:Cross-site request forgery (CSRF)

    Portswigger web security academy:Cross-site request forgery (CSRF) 目录 Portswigger web security acade ...

  8. thinkphp 5中的混合查询

    1.手册样例thinkphp 5.0Db::table('think_user') ->where('name',['like','thinkphp%'],['like','%thinkphp' ...

  9. 三、jmeter常用的元件及组件

    一.HTTP cookie Manager 用来储浏览器产生的用户信息,Stepping Thread Group 可用于模拟阶梯加压! 二.HTTP Cache Manager 缓存管理器(模拟浏览 ...

  10. .NET Core 对象( Transient、Scope、Singleton )生命周期详解 (对象创建以及释放)

    首先我们在VS2019中创建一个.NET Core的控制台程序,方便演示: 需要安装两个依赖包 Microsoft.Extensions.DependencyInjection 依赖注入对象的具体实现 ...