有人觉得搞MCU是一件简单的事情,往往觉得简单的事反而出错更多,今天转发一篇我朋友痞子衡的文章,说说MCU里面的寄存器的事。


大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是改动i.MXRT1xxx里IOMUXC_GPR寄存器保留位可能会造成系统异常

痞子衡的嵌入式技术交流群里有一位非常活跃的朋友(网名:文,痞子衡已经指定他为副群主)近日向痞子衡反映了一个在i.MXRT1062应用程序里动态调整FlexRAM导致WDOG模块工作异常的问题,经过一番排查,痞子衡发现了i.MXRT芯片系统设计里的一个小秘密,这个秘密警示我们在MCU里应尽量遵循谨慎的外设寄存器赋值法,这个寄存器谨慎赋值法是什么,痞子衡先卖个关子,文末会揭秘。痞子衡今天就将这个问题解决过程还原一下,希望对大家有所启发:

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

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

int main(void)
{wdog_config_t config;BOARD_ConfigMPU();BOARD_InitPins();BOARD_BootClockRUN();BOARD_InitDebugConsole();PRINTF("\r\n******** System Start ********\r\n");// 使能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---\r\n");while (1){// 故意不喂狗,让WDOG超时复位系统//WDOG_Refresh(DEMO_WDOG_BASE);PRINTF(" \r\nWDOG 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("\r\n******** System Start ********\r\n");while (1){NVIC_SystemReset();}
}

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

LDR R0,=0x400AC044LDR R1,=0x5555aaaaSTR R1,[R0]LDR R0,=0x400AC040LDR R1,=0x00000007STR 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_STANDARDLDR R0,=0x400AC044MOV32 R1,0x5555aaaaSTR R1,[R0]LDR R0,=0x400AC040LDR R1,[R0]ORR R1,R1,#4STR R1,[R0]
#elseLDR R0,=0x400AC044LDR R1,=0x5555aaaaSTR R1,[R0]LDR R0,=0x400AC040LDR R1,=0x00200007STR 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寄存器保留位可能会造成系统异常痞子衡便介绍完毕了,掌声在哪里~~~

什么是MCU里应尽量遵循的寄存器谨慎赋值法?相关推荐

  1. 为什么应尽量从列表的尾部进行元素的增加与删除操作?

    当列表增加或删除元素时,列表对象自动进行内存扩展或收缩,从而保证元素之间没有缝隙,但这涉及到列表元素的移动,效率较低,应尽量从列表尾部进行元素的增加与删除操作以提高处理速度.

  2. Excel里数字太长显示为科学计数法如何显示完整数字

    Excel里数字太长显示为科学计数法如何显示完整数字 注意:以下测试都是在macos的Microsoft Excel for Mac的16.53版本中实际测试的,在windows中应该也是一样的. 一 ...

  3. 个人计算机分为桌面计算机和便携式计算机,在选择传动方案时,只有为了传动布置或其它必要时才选用锥齿轮,一般情况下应尽量选用圆柱齿轮...

    参考答案如下 选择传只置或锥齿[单选题]冰中下锚时,应选择__. 几年前显卡都使用AGP作为与北桥芯片之间的接口,动方动布但现在越来越多的显卡开始采用性能更好的_____接口. [填空题]用户可以根据 ...

  4. TypeScript里应该尽量用#代替private

    TS类编译之后就会变成普通的JS类,private和protected在编译之后都会变成和public一样 访问控制只会在编译时检查 所以扩展一个类时,不能和这个类的任何private成员重名 cla ...

  5. ESP32驱动LCD液晶屏选型、262K什么意思?SPI写LCD的GRAM时序、MCU液晶屏驱动IC的寄存器功能

    最近转战ESP32,ESP32-D0WDQ6 型号的GPIO只有那么20个左右,且还有几个GPIO只能做输入,非常捉襟见肘.所以如果要驱动LCD液晶屏,绝大多数都会选择SPI接口的MCU屏. 为了编写 ...

  6. 如何让Excel里显示的数字避免通过科学计数法来显示

    我把Hybris里产品主键拷贝到excel里后, 发现这些字符串格式的主键在excel里以科学计数法的方式显示,看起来很不方便: 避免这种情形的办法也很简单: 在单元格的值前面加上英文输入法里的单引号 ...

  7. 把数组里的数组合全部列出 (递归法)

    把数组里的数组合全部列出 ,比如1和2列出来为1, 2,,12,21 package MonthSep.HWday04;import java.util.Arrays; import java.uti ...

  8. 求解VRP问题的节约里程法、sweep扫描算法和λ互换法

    第05章  求解容量约束车辆路径问题的启发式算法 Edited by Jiannywang@163.com 目  录 5.1 节约里程法. 1 5.1.1 C-W节约算法简介. 1 5.1.2 C-W ...

  9. Iconfont-阿里巴巴矢量图标库的SVG玩法

    Iconfont-阿里巴巴的矢量图标大大方便了前端代码中对图标的引用,只需要在网站上选好图标到[我的项目],再通过代码引用即可. 更为方便的是,它可以通过像控制字体font一样地控制图标的大小,颜色等 ...

最新文章

  1. Django 视图函数
  2. CC攻击介绍及如何防御
  3. EasyUI datagrid控件的基本使用
  4. java 判断 中文字符_java中判断字符串中是否有中文字符
  5. Python描述性统计示例
  6. Nginx 最全操作总结
  7. SAP CRM和Hybris Commerce里关于价格折扣的一些配置
  8. 光源时间_缩短背光源的使用寿命的原因
  9. JavaScript 事件:Web 表单如何实现禁用右键、复制粘贴/剪切和输入框自动填充?
  10. 学习Spring Boot:(五)使用 devtools热部署
  11. Windows Azure Web Site (17) 设置Web App TimeOut时间
  12. createprocess 系统找不到指定的文件_告别文件混乱和找不到,文件管理的新思路...
  13. 淦!这个非科班学妹是真的厉害...
  14. 偏导数,雅可比矩阵(jacobi matrix),黑塞矩阵(Hessian matrix)
  15. 中国AR镜片市场现状研究分析与发展前景预测报告(2022)
  16. 浙大pat | 浙大pat乙级 1001~1004
  17. Linux——Ubuntu使用个给力的镜像,安装软件速度飞快
  18. Java中如何判断一个集合中的一个元素不在另一个集合中?把不存在的元素移除
  19. 计算机网络——CSMA/CD协议
  20. 织梦网站如何上传服务器还原,网站转移教程:织梦系统数据库备份和还原的方法步骤...

热门文章

  1. 第三课、Qt的诞生和本质------------------狄泰软件学院
  2. CSDN博客投票活动开始了
  3. 图片延迟加载和滑动翻页
  4. andriod 新建 Activity_ Form (详细设置)
  5. outlook 2007 自动答复邮件
  6. CSP 1.0 语言规范
  7. 数据结构与算法--3.字符串的旋转
  8. CAE所表示的计算机术语是,计算机应用中,英文缩略语CAE所表示的计算机术语是()。...
  9. python变量命名可以有特殊符号吗,和孩子一起学习python之变量命名规则
  10. 濮阳第二届创客机器人比赛_咸阳市举行第二届机器人大赛暨第一届创客大赛