完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
在i.MXRT1062应用程序里动态调整FlexRAM导致WDOG模块工作异常的问题,经过一番排查,痞子衡发现了i.MXRT芯片系统设计里的一个小秘密,这个秘密警示我们在MCU里应尽量遵循谨慎的外设寄存器赋值法,这个寄存器谨慎赋值法是什么,痞子衡先卖个关子,文末会揭秘今天就将这个问题解决过程还原一下,希望对大家有所启发:
一、重配FlexRAM影响WDOG的表象问题 先交待一下问题背景,这个网友是在i.MXRT1062板子上做的测试,使用的是 SDK_EVK-MIMXRT1060boardsevkmimxrt1060driver_exampleswdogiar 例程(XiP),他对工程启动文件和主函数改动如下: 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 里将重配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); |
|
|
|
|
只有小组成员才能发言,加入小组>>
1599 浏览 0 评论
imx6ull 和 lan8742 工作起来不正常, ping 老是丢包
4738 浏览 0 评论
4229 浏览 9 评论
3822 浏览 16 评论
4398 浏览 1 评论
4211浏览 3评论
2380浏览 0评论
3394浏览 0评论
1158浏览 0评论
2843浏览 0评论
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-2 06:24 , Processed in 0.886804 second(s), Total 74, Slave 54 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖
1256