1、模块简介
看门狗/Wdg模块是一个独立的定时器,可用于系统使用。它的作用是提供安全功能以确保软件按计划执行,并且CPU不会陷入无限循环或执行意外的代码。如果Wdg模块在一定时间内未被触发/刷新/喂狗,它将复位MCU。这是一个非常常用、非常重要的一个模块。
AUTOSAR MCAL Wdg模块主要提供以下服务/API:
- 初始化/Wdg_Init
- 设置模式/Wdg_SetMode
- 设置触发条件(设置超时时间/喂狗)/Wdg_SetTriggerCondition
Wdg通常有两种,一种是芯片内部自带的片内看门狗;还有一种是在芯片外部通过SPI这种接口连接的片外看门狗。MCAL只负责第一种片内看门狗,片内看门狗的特点是Wdg模块是直接访问相关硬件寄存器。片外看门狗属于板级设备抽象层负责,通常需要使用MCAL提供的其他模块(比如SPI等)来访问/控制外扩看门狗芯片。这种不能直接访问硬件寄存器。
由于其特殊性,该模块的代码除了可以在ROM里面运行外,也可能会在RAM里面运行。比如在Bootloader刷写Flash时,Wdg模块可能作为二进制文件里面的一部分在RAM上运行。
本文内容基于目前最新版协议:R19-11
注意:
该模块不提供DeInit函数。如果的确需要关闭该模块,可通过调用Wdg_SetMode()函数,参数为OFF Mode来实现。
不提供DeInit函数的原因主要有两点:
1、因为很多硬件不支持关闭功能。
2、一些安全性要求高的系统不允许关闭看门狗。
片内和片外这两种体系结构差异导致的问题,在未来的版本中有计划得到解决。
实际情况,可能有些厂商在Wdg模块里面已经扩展了,也就是在里面也可以配置外挂看门狗。如果已经扩展了,通常片内和片外在使用上没有区别,API这些都是一样的。
2、模式简介
MCAL Wdg模块定义了三种模式,具体定义见下表:
模式定义 模式描述
Off Mode |
该模式下,Wdg硬件关闭。有时候我们可能需要将整个ECU关闭,而不需要看门狗提供周期复位功能,这种情况下可使用该模式。但对于一些安全性要求很高的系统,通常关闭Wdg是不允许的,这种情况下,必须配置Wdg模块以防止切换到该模式。 |
Slow Mode |
该模式用于监控那些具有较长超时时间的场景,比如系统启动阶段/初始化阶段。 |
Fast Mode |
对于那些必须在很短超时时间内完成触发看门狗复位的场景则需使用此模式。比如ECU正常运行期间。又或者看门狗配置为窗口模式时(因为窗口模式必须在设置的最小和最大边界内去触发,通常这个窗口比较窄/时间短)。 |
3、实现原理
内部看门狗服务程序可以通过独立于其他外设的内部硬件计时器(Timer)中断来实现,也可以通过GPT驱动模块的回调函数来实现。如果不通过GPT实现,而通过中断(一类/二类中断)实现,则需要查看厂商提供的文档描述,通常它遵循一类/二类中断的使用条件/注意事项。但是不管哪种实现方式,标准里面并没有具体规定,由用户自行决定。
在之前的版本中,看门狗服务例程是由上层软件来调用的,这会有一个问题,就是很难保证针对窗口式看门狗这种严格的时间约束。新版协议对这部分做了优化。优化的基本思想是将用于维护看门狗硬件时序的服务例程与逻辑控制分开。触发看门狗的时基可以通过硬件来提供,这样可确保最小的时序抖动。而控制看门狗硬件的程序可直接在定时器中断函数里面实现以确保最小延迟。这两个条件(最小时序抖动、最小延迟)确保可以满足窗口式看门狗的时间窗口(也就是上一段提到的实现方法)。
为了保持上层模块(比如WdgM/Wdg管理器)的基本概念不变,Wdg驱动模块期望看门狗的控制逻辑(是否需要被触发/喂狗)由环境环境(WdgM)负责。
想想我们自己平时写代码直接去操作看门狗硬件寄存器,那么我们所能设置的触发时间/喂狗就完全由硬件决定了,通常这个时间不会太长,很多芯片在100ms这种级别。但是文章一开始说了Wdg模块提供3种模式,其中两种是Slow,Fast模式,如果这样的话,我们直接去操作硬件寄存器这种方法是不是就很难处理了,因为触发/喂狗周期都是固定的且比较短,但很多时候应用层可能并不需要那么严格的时间监控,可能秒级的周期即可(总之就是这个周期大于看门狗硬件寄存器的最大值了),这种情况是不是就需要应用层不断去喂狗,这对应用层的代码实现影响就很大了,他需要到处穿插喂狗函数。我想可能还有一个很重要的原因,现在大家对功能安全越来越重视,那么对这种频繁修改看门狗硬件寄存器的操作是不推荐的(本身他的操作性能就不高,通常需要一个序列才能进行修改)。
为了方便大家进一步理解,接下来我先说说我对实现方法的一种想法。其实实现原理也很简单。首先说一下Slow,Fast两种模式对时钟的要求,通常这两种模式Wdg所使用的参考时钟不一样,Slow模式使用的参考时钟频率比较小/慢,而Fast模式所使用的参考时钟频率比较大/快。这应该比较好理解吧。根据上一章节得知,因为通常Slow模式用于那种对超时时间比较长的场景。当然你说我就用同一个时钟源是否可以呢?技术上当然是可以的。但通常都是按照前面描述来实现。
除了上面提到的Wdg模块的参考时钟,还需要额外的GPT时钟(我们这里因为使用的GPT回调函数的实现方式),GPT这个时钟用来干嘛呢?实际上,真正喂狗就靠这个GPT了(喂狗时钟),也就是说应用层调用喂狗函数(Wdg_SetTriggerCondition)并不是直接去操作看门狗硬件寄存器的,而是去配置GPT,然后由GPT中断回调去操作看门狗硬件寄存器。
具体流程是这样的:在调用初始化函数的时候,初始化函数会自动根据你配置的模式、对应模式的参考时钟源频率以及喂狗时钟源频率来计算喂狗时钟的中断周期(为连续模式,而非一次性模式),如果为窗口式看门狗,则GPT的周期为窗口的中间点;如果为非窗口式,则周期等于看门狗超时时间(这里细节上的东西比较多,就不一一讲解了,比如由于运算导致的小数处理等。总之,GPT的周期中断需保证小于Wdg硬件超时时间,或者GPT中断优先级高于Wdg的中断优先级)。然后启动该GPT开始计时,Wdg也开始计时(注意,这里他们各自有各自的计时器)。GPT中断发生后调用GPT对应的回调函数,GPT回调函数调用Wdg模块相关的喂狗函数(这里才是真正去操作看门狗硬件寄存器进行喂狗的)。
举个例子,假如Wdg的超时时间是100ms。我们这里就讲普通(Toggle)模式哈,窗口模式处理思路一样,只是细节上有差别。现在用户在0时刻设置了超时时间是180ms(0时刻其实应该是初始化完成的,为了好理解,我们假设用户调用了喂狗函数),程序会记录这个180ms超时时间,你会发现用户设置的已经大于Wdg的超时时间了,当100ms到达时,GPT中断产生了,发现用户记录的超时时间(180ms)还没到,于是主动去喂狗了(不需要用户调用喂狗函数),同时会将之前记录的180ms超时时间减去100ms,变成80ms,减去的100ms正式已经花掉的时间,然后继续运行。
在下一次100ms到达前就有三种情况了,一种是用户在80ms之前调用了喂狗函数来更新。第二种情况是用户也喂狗了,但是调用函数的时间是在80ms之后,100ms之前。还有一种就是100ms中断发生了都还没有调用喂狗函数。
针对第一种情况,当再次调用喂狗函数时,程序会计算上一次中断发生到此刻的时间间隔(也就是花掉的时间),我们这里这个时间是小于80ms的,说明程序运行是正常的,用户按照约定来喂狗,于是程序以新传入的超时时间重新计算一个超时时间更新之前记录的超时时间(当然,正常情况下,这个新的值会大于100ms,如果小于100ms,那就和后面几种情况处理一样了,这里提重新计算而没有说直接覆盖,是由于里面会有一些时间补偿),然后继续正常运行。
第二种情况其实就是前面第一种情况里面提的那个间隔时间会大于80ms(假如用户是在90ms这个时间调用的函数),判断大于后,虽然用户在100ms中断前调用了,但是还是没有按照用户自己说的180ms来喂狗(加上第一次的的100ms中断时间,用户是在190ms才喂狗),用户还是违约了,所以程序立马停掉GPT Timer。当第二个100ms到后,这时Wdg的超时时间到了,但是由于GPT已经停掉,不会产生中断去喂狗,于是Wdg就复位ECU(严格来说,这里的行为与用户配置有关,可能是直接复位ECU,也可能是产生中断等等)。
第三种情况和第二种类似,区别在于100ms到达后,GPT的中断还是会产生,然后在中断回调里面去判断记录的超时时间80ms小于了中断的周期时间100ms,于是立马停掉GPT Timer,不喂狗。接下来Wdg也立马发现时间到了,于是复位ECU。
看出来没有,这就是为何前面说GPT的周期小于Wdg的超时时间/中断优先级高的原因。如果他大于Wdg的超时时间,那么他根本没有办法自主喂狗了,也没办法在第三种情况立即停掉Timer。同时ECU复位的时间其实也是有延时的,具体延时时间与不同时间组合有很大关系。
讲到这里应该已经明白了它的基本工作原理了。当然里面涉及到很多细节就无法一一展开了,比如模式切换的处理,初始化和运行期间独立的超时配置等等。
更直观一点,下图描述了上面的内容:
以上内容是我的一些想法,可能有些地方还不太完善,但大体上应该没问题。
**小插曲:**实际上以上的内容最开始是我分析NXP的代码得到的结论,但后面发现我忽略了其代码的一个很细节的地方,把NXP的代码理解错了,更扯的是,经过仔细对比后发现我的理解有些地方反而更有优势,索性就变成自己的想法了😄。比如用户设置的超时时间可以不受Wdg的最大时间限制。但也有一些地方实现起来可能比较麻烦,比如GPT和Wdg的Timer上面说的是同时开启,这个同步可能比较麻烦。就算如此,也可以在GPT中断那里稍微处理一下,比如适当减去一点时间。当然相比于NXP的代码也有劣势(这里指正确理解后,见下文),我理解的方法看门狗复位的误差可能更大。欢迎大家讨论。看看有没有其他不妥。
现在再来说说NXP的代码实现方式到底是怎么样的。再次强调一下,不同厂家实现方式可能不一样。NXP的代码是基于GPT回调函数的方式实现的,和我上面描述的差别在于:
Wdg的超时时间就是用户配置的超时时间,GPT周期中断的时间等于用户配置的超时时间的二分之一。这两者都是初始化就固定好,后面不会更改的。什么意思呢,也就是说用户配置的超时时间受Wdg的最大时间限制,你不能配置太长了。这一点和我的想法有区别,我的想法只是GPT中断在Wdg之前就可以了,对用户没限制。至于GPT的时间为何是二分之一,我想是因为我上面提到的同步原因,加上采样定理,所以配置了二分之一,如果配的更短,理论上是可以的,但是会加重系统负担(中断更多)。其他地方的思路都是一样的,毕竟原本我得想法来源于NXP的代码。
分析下来,如果我们不考虑功能安全,单从功能上来看,NXP的代码其实和我们直接去操作看门狗寄存器的方法没有优势,反而多用了GPT的资源以及复杂的处理逻辑。我的方法在对应用层配置的超时时间进行了扩展,不受Wdg的限制。
|