查看: 1638|回复: 1

[分享] [痞子衡]为什么说i.MXRT不走传统MCU套路?从外设寄存器访问...

[复制链接]
  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3298

    主题

    6545

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32003
    最后登录
    2024-4-9
    发表于 2021-7-12 15:08:22 | 显示全部楼层 |阅读模式
    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是对比i.MXRT与LPC在RTC外设GPREG寄存器使用上的异同。

    本篇是 《在SBL项目实战中妙用i.MXRT1xxx里SystemReset不复位的GPR寄存器》 一文的延续,SBL 项目是为 i.MXRT/LPC 系列设计的,上文只介绍了 i.MXRT1xxx 里 SystemReset 不复位的 IOMUXC_SNVS_GPR 寄存器,我们需要在 i.MXRTxxx 和 LPC 中也找出 SystemReset 不复位的通用寄存器。

    我们知道 i.MXRT1xxx 来自于 i.MX 应用处理器,而 i.MXRTxxx 来自于 LPC 微控制器,出身不同,使用上差异其实也不小。痞子衡在 i.MXRTxxx/LPC 参考手册里找了一圈,最终发现 RTC 外设里的 GPREG 寄存器符合要求。今天痞子衡就以这个通用寄存器的使用为例来展开聊一聊 i.MXRT1xxx/i.MXRTxxx/LPC 关于外设寄存器访问设计上的异同:

    一、回顾i.MXRT1xxx上的设计
    在 《在SBL项目实战中妙用i.MXRT1xxx里SystemReset不复位的GPR寄存器》 一文里我们找到符合条件的寄存器有两组,分别是 IOMUXC_SNVS_GPR 和 SNVS_LPGPR,文中最终选用了 IOMUXC_SNVS_GPR,在示例代码里是直接读写这个寄存器的,没有任何多余准备工作,甚至连 IOMUXC_SNVS_GPR 外设时钟使能的操作都不用。
    1. void gpr_rw_test(void)
    2. {
    3.     uint32_t flag = 0x5a;
    4.     IOMUXC_SNVS_GPR->GPR0 = flag;
    5.     flag = IOMUXC_SNVS_GPR->GPR0;  // flag 为 0x5a
    6. }
    复制代码
    这跟 i.MXRT1xxx 系统设计有关,在 i.MXRT1xxx 里 CCM 模块负责所有其他外设的时钟开关控制(具体在 CCM->CCGRx 寄存器),下图是 i.MXRT1052 里 CCM->CCGRx 的默认值(复位后初值,包含软/硬复位):
    11.png
    每个 CCM->CCGRx 包含 16 个 2bit 的 CGx 位,每个 CGx 控制一个外设的时钟开关,2bit 取值定义如下表所示。可以看到大部分外设默认时钟都是打开的(2'b11),仅有如下三个外设默认时钟是关闭的(2'b00):
    1. CCM->CCGR2[CG12] - xbar2
    2. CCM->CCGR2[CG11] - xbar1
    3. CCM->CCGR3[CG2]  - semc
    复制代码
    12.png

    所以我们能随意读写 IOMUXC_SNVS_GPR 寄存器是因为如下时钟控制位是默认打开的,如果将这个时钟控制位设为 2'b00,即关闭,会是什么现象呢?痞子衡挂上 J-Link 做了个读写测试,发现时钟不打开的情况下,寄存器的值依旧能够被有效读取,只是寄存器写入操作是无效的(被系统直接忽略,就像写操作没发生一样),这种体验其实跟一般 MCU 外设寄存器读写设计不太一样, i.MXRT1xxx 上对外设地址空间的访问并没有做 MCU 上常见的保护机制(即外设时钟不使能的情况下,外设寄存器的写访问应返回总线错误,读访问应返回总线错误或无效 0 值),并且复位后几乎所有外设时钟都是默认打开的。

    1. CCM->CCGR3[CG15] - iomuxc_snvs_gpr
    复制代码
    13.png
    二、再看i.MXRTxxx上的设计
    现在我们看一下 i.MXRTxxx 上 SystemReset 不复位的通用寄存器 RTC->GPREGx,我们在代码里尝试直接写这个寄存器,发现写入操作不报错也不生效,读回来是默认值 0,看起来 i.MXRTxxx 上的这里设计逻辑跟 i.MXRT1xxx 不一样。
    1. void gpreg_rw_test(void)
    2. {
    3.     uint32_t flag = 0x5a;
    4.     RTC->GPREG[0] = flag;
    5.     flag = RTC->GPREG[0];  // flag 为 0
    6. }
    复制代码
    14.png
    我们知道 i.MXRTxxx 里 CLKCTLx 模块负责所有其他外设的时钟开关控制(具体在 CLKCTLx->PSCCTLx 寄存器),下图是 i.MXRT685 里 CLKCTLx->PSCCTLx 的默认值(复位后初值,包含软/硬复位):
    15.png
    每个 CLKCTLx->PSCCTLx 包含 32 个 1bit 的 xxxPeripheral_CLK 位,每个 xxxPeripheral_CLK 控制一个外设的时钟开关,0 表示关闭,1 表示打开。可以看到大部分外设默认时钟都是关闭的,仅有 BootROM 默认时钟是开启的:


    因为 GPREGx 是 RTC 外设里的一部分,那我们试着先将 RTC 外设时钟打开,然后再写入 GPREG 寄存器,发现还是不行,后来查阅 RTC 章节,发现还需要将 RTC->CTRL[SWRESET] 位清零才可以(否则 RTC 模块一直处于复位状态),挂上 J-Link 做个读写测试,打开外设时钟后可以正常做写寄存器操作,关闭时钟后依旧能够有效读取寄存器。总结一下,i.MXRTxxx 上也没有对外设地址空间的访问做 MCU 上常见的保护机制,所以本质上它和 i.MXRT1xxx 一样,只是不像 i.MXRT1xxx 那样复位后默认打开几乎所有外设时钟。
    1. void gpreg_rw_test(void)
    2. {
    3.     // 准备工作
    4.     CLKCTL1->PSCCTL2_SET = (1UL << CLKCTL1_PSCCTL2_SET_RTC_LITE_CLK_SET_SHIFT);  // 或 CLOCK_EnableClock(kCLOCK_Rtc);
    5.     RTC->CTRL &= ~RTC_CTRL_SWRESET_MASK;
    6.     // 原代码
    7.     uint32_t flag = 0x5a;
    8.     RTC->GPREG[0] = flag;
    9.     flag = RTC->GPREG[0];  // flag 为 0x5a
    10. }
    复制代码
    16.png
    三、对比LPC上的设计
    最后我们看一下 LPC 上 SystemReset 不复位的通用寄存器 RTC->GPREGx,从 RTC 模块寄存器定义上来看,它和 i.MXRTxxx 里的 RTC 一模一样,是的,说 i.MXRTxxx 来自于 LPC 没有丝毫夸张,它们就是一个平台的产物。我们在代码里尝试直接写这个寄存器,发现读写操作都会直接发生系统错误,在线调试无法继续进行。
    1. void gpreg_rw_test(void)
    2. {
    3.     uint32_t flag = 0x5a;
    4.     RTC->GPREG[0] = flag; // 系统错误,调试无法进行
    5.     flag = RTC->GPREG[0];
    6. }
    复制代码
    我们知道 LPC 里 SYSCON 模块负责所有其他外设的时钟开关控制(具体在 SYSCON->AHBCLKCTRLx 寄存器),下图是 LPC55S69 里 SYSCON->AHBCLKCTRLx 的默认值(复位后初值,包含软/硬复位):
    17.png
    每个 SYSCON->AHBCLKCTRLx 包含 32 个 1bit 的 xxxPeripheral 位,每个 xxxPeripheral 控制一个外设的时钟开关,0 表示关闭,1 表示打开。可以看到大部分外设默认时钟都是关闭的,仅有 Flash/FMC 默认时钟是开启的:
    1. SYSCON->AHBCLKCTRL0[FLASH] - Flash 存储器
    2. SYSCON->AHBCLKCTRL0[FMC]   - FMC 控制器
    复制代码
    至于操作 RTC->GPREGx 前准备工作,与 i.MXRTxxx 上是一致的,这里不予赘述。现在我们发现 LPC 上才真正对外设地址空间的访问做了 MCU 上常见的保护机制(即外设时钟不使能的情况下,外设寄存器的写访问应返回总线错误,读访问应返回总线错误),它才是典型的 MCU 产物, 而 i.MXRT 其实更偏向 MPU 设计风格。
    1. void gpreg_rw_test(void)
    2. {
    3.     // 准备工作
    4.     SYSCON->AHBCLKCTRLSET[0] = (1UL << SYSCON_AHBCLKCTRL0_RTC_SHIFT);  // 或 CLOCK_EnableClock(kCLOCK_Rtc);
    5.     RTC->CTRL &= ~RTC_CTRL_SWRESET_MASK;
    6.     // 原代码
    7.     uint32_t flag = 0x5a;
    8.     RTC->GPREG[0] = flag;
    9.     flag = RTC->GPREG[0];  // flag 为 0x5a
    10. }
    复制代码
    至此,对比i.MXRT与LPC在RTC外设GPREG寄存器使用上的异同痞子衡便介绍完毕了,掌声在哪里~~~



    签到签到
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2024-4-10 22:38
  • 签到天数: 1335 天

    [LV.10]以坛为家III

    88

    主题

    4292

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    9049
    最后登录
    2024-4-13
    发表于 2021-7-12 16:26:06 | 显示全部楼层
    献上我的掌声  掌声
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-20 12:06 , Processed in 0.126945 second(s), 23 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表