像普通的ISR一样使用AVR看门狗


17

我试图把头放在ATTinyX5系列的看门狗定时器上。因此,我读过的东西似乎使您可以使用它使程序在N秒内执行特定的操作,但从未真正展示过如何执行。其他人则认为它只会重置芯片,除非同时进行代码重置(这似乎是“正常”用法)。

有没有像TIMER1_COMPA_vect或类似方法那样使用WDT的方法。我注意到它有一个1秒的超时模式,我真的很想能够使用它使代码中的每1秒发生一次(并且最好在两者之间休眠)。

有什么想法吗?

* 更新:*自从被询问以来,我指的是ATTinyX5数据表的 8.4节。不是我完全理解它,这是我的问题...


1
+1跳出框框思考。如果您添加指向AVR数据表的链接,请多多指教。
jippie

Answers:


23

您当然可以。根据数据表,可以设置看门狗定时器以复位MCU或在触发时引起中断。似乎您对中断可能性更感兴趣。

实际上,WDT实际上比普通定时器更容易设置,原因是它的用处不大:选项更少。它以内部校准的128kHz时钟运行,这意味着其时序不受MCU主时钟速度的影响。它也可以在最深的睡眠模式下继续运行,以提供唤醒源。

我将介绍几个数据表示例以及一些我使用过的代码(在C语言中)。

包含的文件和定义

首先,您可能需要包括以下两个头文件才能正常工作:

#include <avr/wdt.h>        // Supplied Watch Dog Timer Macros 
#include <avr/sleep.h>      // Supplied AVR Sleep Macros

另外,我使用在标准AVR标头之一中定义的宏<_BV(BIT)>如下(对您来说可能更熟悉):

#define _BV(BIT)   (1<<BIT)

代码开始

首次启动MCU时,通常会初始化I / O,设置计时器等。这里是一个很好的时机,可以确保WDT不会引起复位,因为它可以再次复位,从而使程序保持在不稳定的循环。

if(MCUSR & _BV(WDRF)){            // If a reset was caused by the Watchdog Timer...
    MCUSR &= ~_BV(WDRF);                 // Clear the WDT reset flag
    WDTCSR |= (_BV(WDCE) | _BV(WDE));   // Enable the WD Change Bit
    WDTCSR = 0x00;                      // Disable the WDT
}

WDT设置

然后,在设置完其余芯片后,重做WDT。设置WDT需要“定时序列”,但这确实很容易...

// Set up Watch Dog Timer for Inactivity
WDTCSR |= (_BV(WDCE) | _BV(WDE));   // Enable the WD Change Bit
WDTCSR =   _BV(WDIE) |              // Enable WDT Interrupt
           _BV(WDP2) | _BV(WDP1);   // Set Timeout to ~1 seconds

当然,在此代码期间应禁用中断。之后一定要重新启用它们!

cli();    // Disable the Interrupts
sei();    // Enable the Interrupts

WDT中断服务程序 接下来要担心的是处理WDT ISR。这样做是这样的:

ISR(WDT_vect)
{
  sleep_disable();          // Disable Sleep on Wakeup
  // Your code goes here...
  // Whatever needs to happen every 1 second
  sleep_enable();           // Enable Sleep Mode
}

MCU睡眠

我建议不要在ISR的末尾启用睡眠模式,而不是将MCU置于WDT ISR内部,然后让MAIN程序使MCU进入睡眠状态。这样,程序实际上会在进入睡眠状态之前离开ISR,它将醒来并直接返回WDT ISR。

// Enable Sleep Mode for Power Down
set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // Set Sleep Mode: Power Down
sleep_enable();                     // Enable Sleep Mode  
sei();                              // Enable Interrupts 

/****************************
 *  Enter Main Program Loop  *
 ****************************/
 for(;;)
 {
   if (MCUCR & _BV(SE)){    // If Sleep is Enabled...
     cli();                 // Disable Interrupts
     sleep_bod_disable();   // Disable BOD
     sei();                 // Enable Interrupts
     sleep_cpu();           // Go to Sleep

 /****************************
  *   Sleep Until WDT Times Out  
  *   -> Go to WDT ISR   
  ****************************/

   }
 }

哇...非常详细。谢谢!我对sleep部分显示的主循环感到困惑(如果(MCUCR&_BV(SE)){//如果启用了Sleep ...等),我很困惑为什么在主外观中您会持续禁用和启用中断。以及该部分顶部的部分(set_sleep_mode(SLEEP_MODE_PWR_DOWN);)应该在哪里运行?
亚当·海尔

好的,“ set_sleep_mode(MODE)”部分应该位于主循环之前的主目录中,理想情况下应位于设置I / O端口,计时器等的其他初始化代码中。到那时,因为它将在您的第一个WDT触发之后完成。在主循环内部,只有启用睡眠功能后,睡眠代码才会执行​​,并且仅在执行sleep_bod_disable()时,并不需要完全禁用/重新启用中断。整个IF语句可能在您在其中执行的任何其他代码之后位于MAIN循环的底部(但仍在内部)。
Kurt E. Clothier

好吧...现在更有意义了。我不知道的最后一件事就是这个“定时序列”是什么...
Adam Haile

附注:我知道你为什么会想要去睡觉,但我认为你不具备使用WDT时?
亚当·海尔

看看数据表的这一小部分:8.4.1。基本上,要更改WDT寄存器,必须设置更改位,然后在这么多的时钟周期内设置正确的WDTCR位。我在WDT设置部分提供的代码通过首先使能WD更改位来实现。不,您不必在WDT中完全使用睡眠功能。无论出于何种原因,它都可能是标准定时中断源。
Kurt E. Clothier

1

根据数据表,这是可能的。您甚至可以同时启用中断和复位。如果两者都启用,则第一个看门狗超时将触发中断,从而导致中断使能位未置1(禁止中断)。然后,下次超时将重置您的CPU。如果在执行后立即启用中断,则下次超时(再次)将仅触发一个中断。

您也可以只启用中断,而根本不启用复位。每次触发中断时,您都必须将WDIE位置1。


嗯....我认为这是有道理的。我会试一试。我一直喜欢将东西用于他们不想要的地方:)
Adam Haile

其实我认为这是一个聪明的设计。在保留看门狗功能的同时为您节省了计时器。
汤姆L.13年

2
WDT的初始超时和随后的中断还可用于某些使WDT进行实际挂断恢复的应用程序。可以查看WDT ISR中的堆栈返回地址,以推断出发生“意外超时”时代码正在尝试执行的操作。
Michael Karas

1

这比上面和其他地方所建议的要容易得多。

只要WDTON未设置保险丝(默认情况下未设置保险丝),则只需要...

  1. 在看门狗控制寄存器中设置看门狗中断使能位和超时。
  2. 启用中断。

这是一个代码示例,它将每16ms执行一次ISR ...

ISR(WDT_vect) {
   // Any code here will get called each time the watchdog expires
}

void main(void) {
   WDTCR =  _BV(WDIE);    // Enable WDT interrupt, leave existing timeout (default 16ms) 
   sei();                                           // Turn on global interrupts
   // Put any code you want after here.
   // You can also go into deep sleep here and as long as 
   // global interrupts are eneabled, you will get woken 
   // up when the watchdog timer expires
   while (1);
}

就是这样 由于我们永远不会启用看门狗复位,因此我们永远不必弄乱定时序列来禁用它。调用ISR时,看门狗中断标志会自动清除。

如果您想要一个不同于每1秒的周期,则可以在此处使用这些值来设置...中的适当位WDTCR

在此处输入图片说明

请注意,您确实需要执行定时序列才能更改超时。这是将超时设置为1秒的代码...

   WDTCR = _BV(WDCE) | _BV(WDE);                   // Enable changes
   WDTCR = _BV(WDIE) | _BV( WDP2) | _BV( WDP1);    // Enable WDT interrupt, change timeout to 1 second

在安装过程中不执行定时序列会节省一行代码-读-修改-写操作。数据表中建议“如果看门狗被意外启用,例如由于指针失控或掉电情况而意外启用”的初始顺序,而我的其余代码则特定于WDT与睡眠模式结合使用,具体要求如下: OP。您的解决方案并不简单,只是忽略了建议/要求的样板代码。
Kurt E. Clothier '18

@ KurtE.Clothier抱歉,仅尝试给出最简单的工作示例!
bigjosh
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.