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

电器工程 avr 服装 看门狗
2022-01-24 23:47:15

我正试图围绕 ATTinyX5 系列上的看门狗定时器。所以我读过的东西看起来好像你可以用它来让程序在 N 秒内做一些特定的事情,但从来没有真正展示过如何做。其他人则认为它只会重置芯片,除非代码中的某些东西同时重置它(这似乎是“正常”使用)。

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

想法?

*更新:*既然有人问过,我指的是ATtinyX5 数据表的第 8.4 节。不是我完全理解它,这是我的问题......

3个回答

你当然可以。根据数据表,看门狗定时器可以设置为复位 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 在 WDT ISR 内进入睡眠状态,我建议在 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   
  ****************************/

   }
 }

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

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

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

只要WDTON保险丝未编程(默认情况下未编程),那么您只需...

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

这是一个每 16 毫秒执行一次 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