控制/MCU
一、前言
最近因为项目产品硬件设计有问题,导致设计的一款产品把硬件电源开关,以及硬件系统复位功能去掉了。更严重的是,这产品已经开始生产了,硬件已经无法修改,所以软件必须上看门狗,否则设备死机或是异常后就只能拆设备拔电池复位了。
我们使用的MCU是普冉的PY32F030,这颗芯片在低功耗应用场景下,使用看门狗会有很多的问题和缺陷,需要非常注意,稍有不慎,就会出问题。
关于看门狗在低功耗场景下的应用,几个问题点可以提前思考一下:
1.看门狗是在中断中喂狗还是在主程序中喂狗比较好?
2.看门狗初始化可以放到时钟初始化之前么?
3.如果时钟死掉了,看门狗还能正常工作么?
4.低功耗深度休眠后还需要喂狗么?如果需要,要怎么设计?使用什么唤醒设备喂狗?
5.软件独立看门狗与硬件独立看门狗它们有什么区别?
6.在看门狗初始化之前系统异常了会怎样?
7.选项字节里开启硬件看门狗与软件代码开启有什么区别?
8.如果异常不可避免,有没有一个地方可以缓存设备状态,系统异常复位后状态不被清除?
二、看门狗分类
看门狗的分类,根据实现方式的不同,可以分为软件看门狗和硬件看门狗:
软件看门狗:通过软件实现的一种机制,通常由系统中的软件来设置和管理
硬件看门狗: 嵌入在处理器或芯片中的专用硬件模块
根据使用方式的不同,又可以区分为独立看门狗和窗口看门狗:
独立看门狗: 独立看门狗通常用于监控整个系统的运行状态,而不特定于某个任务或进程,当系统故障,死锁,无响应的时候,应用程序无法进行正常喂狗,看门狗超时从而产生复位。
窗口看门狗: 窗口看门狗更专注于监控特定任务或进程的运行状态,并在特定的时间窗口内完成。比如在某个任务中,它的执行时间要求非常高,可以使用窗口看门狗,它有一个时间窗口,如果太早喂狗和太晚喂狗,都会产生异常,正因为它喂狗时间有个时间窗口,所以才叫窗口看门狗。
我使用的普冉PY32F030系列MCU,它是32位Cortex-M0+的内核,里面带有一个独立看门狗IWDG和一个窗口看门狗WWDG。
其中,独立看门狗和窗口看门狗,还有软件和硬件的区别,主要差异是在看门狗的启动方式上不同。下面我们的介绍,主要针对独立看门狗。
三、启动看门狗
看门狗的启动有多种方式:
通过接口设置启动
直接设置寄存器启动
设置选项字节启动
1、通过接口设置
这里可以直接参考官方sample进行初始化:
IWDG_HandleTypeDef IwdgHandle; HAL_Init(); /*##-3- Configure & Start the IWDG peripheral #########################################*/ IwdgHandle.Instance = IWDG; IwdgHandle.Init.Prescaler = IWDG_PRESCALER_32;//T=1MS IwdgHandle.Init.Reload = (1000); //1ms*1000=1s IwdgHandle.Init.Window = IWDG_WINDOW_DISABLE; if(HAL_IWDG_Init(&IwdgHandle) != HAL_OK) { /* Initialization Error */ Error_Handler(); }
这里需要特别注意,因为IWDG是依赖于LSI时钟的,也就是在HAL_IWDG_Init 函数调用之前,必须先打开LSI时钟。
官方给的sample中,是在HAL_Init()中把LSI时钟打开了。当你把上面这段代码移植到你自己工程上,如果你LSI没有开启,或者是在HAL_IWDG_Init后面才开LSI时钟,你调用HAL_IWDG_Init就会一直失败,系统一直ERROR,整个MCU会启动不了。
2、通过寄存器直接设置
直接往 IWDG_SR,IWDG_RLR,IWDG_KR三个寄存器地址写入对应的参数,使能IWDG
void init_wtd(void) { volatileu int32_t *IWDG_KR_ADDR = (volatileuint32_t *)0x40003000UL; volatileu int32_t *IWDG_PR_ADDR = (volatileuint32_t *)0x40003004UL; volatileu int32_t *IWDG_RLR_ADDR = (volatileuint32_t *)0x40003008UL; *IWDG_KR_ADDR = 0x5555; *IWDG_PR_ADDR = 0x03; *IWDG_RLR_ADDR = 0xF40; }
实际IWDG是有四个寄存器,还有一个IWDG_PR,它与前面一样,如果不初始化时钟,看门狗会启动不了,就算是设置了,看门狗也是不会启动。
如果要使能时钟,可以添加时钟设置语句:
SET_BIT(RCC->CSR, RCC_CSR_LSION);
直接设置寄存器有一个好处,就是在boot中, 因为对代码量要求比较高,可以比较精简的实现功能。
3、通过选项字节配置
MCU上内部有一个小的flash,里面有个FLASH user option,在这里面可以设置MCU的一些配置参数。
这个参数是可以通过烧录器在烧录的时候就把参数配置进去,对于已经烧录的设备,可以通过写选项字节的方式把IWDG_SW置位或是清零。
void Option_config_NRST_to_gpio_hwwdg(void) { FLASH_OBProgramInitTypeDef OBInitCfg; /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ /* 初始化flash擦写时间参数 */ HAL_FLASH_Init(FLASH_PROGRAM_ERASE_CLOCK_8MHZ); /* 获取option bytes数据 */ HAL_FLASHEx_OBGetConfig(&OBInitCfg); //配置Nreset为GPIO if(((OBInitCfg.USERConfig & OB_RESET_MODE_GPIO) != OB_RESET_MODE_GPIO)||((OBInitCfg.USERConfig & OB_IWDG_SW) == OB_IWDG_SW)) { /* 修改 USER(RESET , WWDG, IWDG) 配置值 , 注意一定要3个一起配置*/ OBInitCfg.OptionType = OPTIONBYTE_USER; MODIFY_REG(OBInitCfg.USERConfig, (OB_RESET_MODE_GPIO|OB_WWDG_SW|OB_IWDG_SW), (OB_RESET_MODE_GPIO | OB_WWDG_SW | OB_IWDG_HW)); /* 启动option byte编程 */ HAL_FLASHEx_OBProgram(&OBInitCfg); /* 产生一个复位,option byte装载 */ HAL_FLASH_OB_Launch(); } }
通过选项字节配置了硬件看门狗之后,芯片会自动开启LSI时钟,这个时候,软件要关闭LSI时钟是关闭不了的。
软件独立看门狗与硬件独立看门狗的区别:
1.软件独立看门狗通过软件初始化,可以通过关闭时钟的方式把它关闭了。
2.如果在设备上电到看门狗初始化之前系统异常了,看门狗是不生效的,这种情况比较多的出现在软件初始化的时候异常卡死。
3.硬件独立看门狗通过烧录器烧录的时候配置,或者是通过软件程序,修改选项字节里面参数进行修改。
4.硬件独立看门狗一但配置上,它从上电的时候就会开始生效,停止不了,除非重新修改配置项参数。
5.硬件独立看门狗开启之后,LSI时钟会自动开启,并且关闭不了。
四、休眠唤醒喂狗
在低功耗设备中,MCU更加多的时候是在深度睡眠的模式,以达到省功耗的目的。在深度休眠模式下,看门狗还是在正常运行的。
也就是说,在深度休眠模式下,还是需要定时唤醒设备进行喂狗,喂完狗之后,设备再重新进入休眠。
1、常规方式
官方补充文档上有介绍,在PY32F030、PY32F003、PY32F002A系列上,在休眠前,需要进行下面几个操作:
1.关闭非唤醒源中断
2.关闭系统滴答 HAL_SuspendTick();
3.保证RTC稳定 while(RTC->DIVL<2);
实际在使用的时候,我们比较常用的方式是,使用RTC的秒中断,在休眠的时候,每秒唤醒一下设备,然后进行喂狗操作,最后再休眠下去。
2、异常情况
实际测试的时候发现,在普冉030使用RTC唤醒喂狗的方式,随着时间的推移,设备会出现异常导致看门狗复位。
我们升级五百台设备,24小时内,会有几台设备偶尔出现该问题,36小时后,大部分的设备基本上都会出现这个异常。
普冉官方的解释是,它们RTC作为唤醒源确实是会存在这个问题,没有好的解决方案,只能是改用LPTIM来做唤醒源。出现这类问题的根本原因是如果休眠的stop指令与唤醒源中断同一时间触发,那么他们芯片就会挂死。
实际使用的时候,使用LPTIM的方式,还是会存在上面的内容,只是出现的概率会比较低而已。
3、补救方案
上面的异常情况,是设备在产线上才发现的,那要怎么解?客户肯定也是接受不了这种频繁重启的情况,特别是在低功耗设备上。
最后的方式是将RAM进行分区,分出一个IRAM2区,将一些状态位保存在IRAM2区,该区启动的时候不进行初始化,看门狗复位的时候,该区的数据也不会被清除掉。
如果是检测到看门狗异常导致的复位,可以通过保存在状态位信息恢复到复位前的状态。
使用IRAM2区不初始化的方式需要注意一点:如果程序分为boot和app两个部分,需要在boot和app上同时设置该区域,否则可能在boot运行阶段,IRAM2区的数据就被清除掉了。
将变量定义到IRAM2分区可以使用下面这种方式指定地址:
uint8_t myVariable __attribute__((at(0x20000FFC))) ;
五、结尾
针对普冉PY32F030 MCU,如果要使用独立看门狗,需要注意几点:
1.最好是在烧录的时候就直接配置启动硬件看门狗
2.不要使用RTC作为休眠唤醒源进行喂狗
3.最好预留一个IRAM分区,以备不时之需
有些坑,没踩之前并不知道这是一个坑,对于做嵌入式应用软件的工程师而言,他并不知道芯片设计上会存在什么样的缺陷。
如果一颗芯片,价格比别人便宜很多倍,那么在使用的时候就需要特别注意了,为啥它可以做到这么便宜?是不是哪里有坑我们不清楚?就算时间再紧急,最好也要小批量试产之后才能批量使用。
审核编辑:黄飞
全部0条评论
快来发表一下你的评论吧 !