什么是MCU里应尽量遵循的寄存器谨慎赋值法

发布时间:2023-02-01  

今天给大家介绍的是改动i.MXRT1xxx里IOMUXC_GPR寄存器保留位可能会造成系统异常。


在痞子衡的嵌入式技术交流群里有一位非常活跃的朋友,前段时间反映了一个在i.MXRT1062应用程序里动态调整FlexRAM导致WDOG模块工作异常的问题。经过一番排查,发现了i.MXRT芯片系统设计里的一个小秘密,这个秘密警示我们在MCU里应尽量遵循谨慎的外设寄存器赋值法。


这个寄存器谨慎赋值法是什么?这里先卖个关子,文末会揭秘。今天就将这个问题解决过程还原一下,希望对大家有所启发:


一、重配FlexRAM影响WDOG的表象问题

先交待一下问题背景,这个网友是在i.MXRT1062板子上做的测试,使用的是 SDK_EVK-MIMXRT1060boardsevkmimxrt1060driver_exampleswdogiar 例程(XiP),他对工程启动文件和主函数改动如下:

嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!


无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。

int main(void)

{

    wdog_config_t config;

    BOARD_ConfigMPU();

    BOARD_InitPins();

    BOARD_BootClockRUN();

    BOARD_InitDebugConsole();


    PRINTF("rn******** System Start ********rn");


    // 使能WDOG模块,设置Timeout时间,不启用中断

    WDOG_GetDefaultConfig(&config);

    // Timeout value is (0xF + 1)/2 = 8 sec.

    config.timeoutValue = 0xFU;

    WDOG_Init(DEMO_WDOG_BASE, &config);

    PRINTF("--- wdog Init done---rn");


    while (1)

    {

        // 故意不喂狗,让WDOG超时复位系统

        //WDOG_Refresh(DEMO_WDOG_BASE);

        PRINTF(" rnWDOG has be refreshed!");


        /* Delay. */

        delay(SystemCoreClock);

    }

}

他在启动文件 startup_MIMXRT1062.s 里将默认128KB ITCM、128KB DTCM、256KB OCRAM的FlexRAM分配调整成了256KB DTCM、256KB OCRAM(关于FlexRAM基本知识,参见旧文 《百变星君FlexRAM》),这种FlexRAM动态调整方式仅适用XiP工程。最终运行结果里看,应用程序似乎仅运行了一次,没有像预想得那样重复启动执行。

如果在 startup_MIMXRT1062.s 里将重配FlexRAM代码去掉,这个WDOG例程是可以正常工作的,串口助手里可以看到循环打印,所以这很容易让人推断出FlexRAM重配功能导致WDOG模块工作异常了。

二、找到程序异常的根本原因

由于这个WDOG例程并不是完全功能异常,至少首次打印是有的,说明重配FlexRAM并没有对程序堆栈运存等造成实质影响,启动文件里那段重配FlexRAM代码本身没有逻辑问题。而打印输出在WDOG超时时间到了之后就没有了,看起来WDOG模块应该是正常产生了软复位。


为了最小化代码去定位问题,我们将这个网友WDOG例程主函数修改如下,去掉WDOG相关代码,直接用 NVIC_SystemReset() 代替。运行后发现,仍然仅有一次打印,这个实验的意义是那段重配FlexRAM代码会导致软复位后程序没法再次运行,而跟具体WDOG模块无关。


int main(void)

{

    BOARD_ConfigMPU();

    BOARD_InitPins();

    BOARD_BootClockRUN();

    BOARD_InitDebugConsole();


    PRINTF("rn******** System Start ********rn");


    while (1)

    {

        NVIC_SystemReset();

    }

}

我们现在将焦点放回到重配FlexRAM那段汇编代码本身,代码很简单,就是将i.MXRT芯片内部的IOMUXC_GPR->GPR17(基址0x400ac044)和IOMUXC_GPR->GPR16(基址0x400ac040)分别整体赋值为0x5555aaaa和0x00000007,单纯从寄存器有效功能位定义上来看,这样操作是没问题的。


   LDR R0,=0x400AC044

    LDR R1,=0x5555aaaa

    STR R1,[R0]

    LDR R0,=0x400AC040

    LDR R1,=0x00000007

    STR R1,[R0]

翻看手册里关于IOMUXC_GPR->GPR17和IOMUXC_GPR->GPR16寄存器的位定义,发现IOMUXC_GPR->GPR16寄存器中有很多bit是保留位,并且其中bit21保留位默认值是1,与其他保留位默认值0不一样。显然 IOMUXC_GPR->GPR16 = 0x00000007 这样的赋值语句会将其bit21误清零,并且IOMUXC_GPR寄存器在软复位后也不会改变其值 (参见《SystemReset不复位的GPR寄存器小结》一文)。

难道问题是由IOMUXC_GPR->GPR16[21]保留位被误清零导致的?死马当活马医吧,我们修改一下重配FlexRAM代码如下(两种方式都行),将IOMUXC_GPR->GPR16[21]保持为默认1。


运行后发现,异常问题解决了,串口助手里可以看到循环打印。现在我们知道了IOMUXC_GPR寄存器即使是保留位也不要轻易当用户标志位使用,更不要轻易改变其默认值,因为SoC占用了这些位,具体用途未详述。由此可以推测IOMUXC_GPR->GPR16[21]位跟系统启动有关,并且其值的设置是在软复位后才生效的。


#ifdef FLEXRAM_CFG_STANDARD

    LDR R0,=0x400AC044

    MOV32 R1,0x5555aaaa

    STR R1,[R0]

    LDR R0,=0x400AC040

    LDR R1,[R0]

    ORR R1,R1,#4

    STR R1,[R0]

#else

    LDR R0,=0x400AC044

    LDR R1,=0x5555aaaa

    STR R1,[R0]

    LDR R0,=0x400AC040

    LDR R1,=0x00200007

    STR R1,[R0]

#endif

三、MCU外设寄存器谨慎赋值法

现在为大家揭秘文章开头卖的关子,到底什么是谨慎的外设寄存器赋值法?


其实可以从芯片头文件定义里去学,假设我们有一个模块叫PERIPH,模块内部有一个名为REG的寄存器,这个寄存器中有功能位FUNC(单bit或者多bit),芯片头文件中通常定义如下:


typedef struct {

  __IO uint32_t REG;

} PERIPH_Type;


#define PERIPH_REG_FUNC_MASK  (0x4U) // 或者 (0xCU)

#define PERIPH_REG_FUNC_SHIFT (2U)

#define PERIPH_REG_FUNC(x)    (((uint32_t)(((uint32_t)(x)) << PERIPH_REG_FUNC_SHIFT)) & PERIPH_REG_FUNC_MASK)


#define PERIPH_BASE           (0x400AC000u)

#define PERIPH                ((PERIPH_Type *)PERIPH_BASE)

谨慎寄存器赋值法的核心要义就是每次操作都只涉及一种功能位,并且不要影响其他功能位的值,就像下面代码所示。切忌出现 PERIPH->REG = value1 | value2 | ... 这样的一次性多个不同功能位一起赋值的操作。


谨慎寄存器赋值法既可以避免模块设计里不同功能位赋值有先后顺序的限制问题,也可以防止误改某些保留位默认值的异常情况发生。当然,这也是有小小代价的,那就是会增加了一些代码长度。


// 如果PERIPH->REG[FUNC]是单bit

PERIPH->REG |= PERIPH_REG_FUNC_MASK;

PERIPH->REG &= ~PERIPH_REG_FUNC_MASK;

// 如果PERIPH->REG[FUNC]是多bit

PERIPH->REG = (PERIPH->REG & (~PERIPH_REG_FUNC_MASK)) | PERIPH_REG_FUNC(value);

至此,改动i.MXRT1xxx里IOMUXC_GPR寄存器保留位可能会造成系统异常便介绍完毕了。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    SFR区。 内部RAM低128单元 80C51低128个单元是真正的内部数据RAM区,是一个多功能复用性数据存储器,其按用途可分为3个区域,如下图所示: 1)工作寄存器区(00H~1FH) 工作寄存器区也称为通用寄存器......
    对程序员可见  3.1.1 ARM 状态时内部寄存器 在 ARM 状态,16 个通用寄存器和一个状态寄存器在任意时刻都可见。在特权(非用户)模式下,将切......
    0x00-0x7F,而从0x80-0xFF则是SFR(CPU工作寄存器和各种外设寄存器都在此)的区域。对于8052来说,内部RAM有256B,所以0x80-0xFF是高128B的RAM在使用。可这......
    操作以及逻辑操作,也可执行地址运算和转换。 寄存器部件,包括通用寄存器、专用寄存器和控制寄存器通用寄存器又可分定点数和浮点数两类,它们用来保存指令执行过程中临时存放的寄存器操作数和中间(或最......
    手册中,都有Reset and Clock Control(RCC)复位和时钟控制的章节。 在这一章节就可以看到有两类寄存器:peripheral reset register(RSTR)外设复位寄存器和......
    组的容量不可能很大。寄存器组可分为专用寄存器和通用寄存器。专用寄存器的作用是固定的,分别寄存相应的数据。而通用寄存器用途广泛并可由程序员规定其用途,通用寄存器的数目因微处理器而异。 总的来说,CPU从内......
    在任何模式都可以使用,寄存器的使用与处理器状态和工作模式有关。每种处理器模式使用不同的寄存器组。其中15个通用寄存器(R0~R14)、1或2个状态寄存器和程序计数器是通用的。 通用寄存器(R0~R15)可分成不分组寄存器......
    不为0的话,他会将这段寄存器和上面提到的SD卡里面的Signature区域的数据做一个校验,校验算法具体就没看了。当校验通过才会运行BL1,否则会报报 4.2 Verification failure......
    常用于存放操作数中间结果等。由于它们的功能及使用不作预先规定,因此称之为通用寄存器,有时也叫工作寄存器。4组通用寄存器占据内部RAM的00H~1FH单元地址。 在任一时刻,CPU只能使用其中的一组寄存器,并且把正在使用的那组寄存器称之为当前寄存器......
    STM32失能时钟和复位外设的区别;在STM32参考手册中,都有Reset and Clock Control(RCC)复位和时钟控制的章节。 在这一章节就可以看到有两类寄存器......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>