通常のISRのようにAVRウォッチドッグを使用する


17

私はATTinyX5シリーズのウォッチドッグタイマーに頭を包み込もうとしています。だから私が読んだことは、プログラムをN秒まで特定の何かをさせるためにそれを使用できるように見えましたが、実際にはどのように見せたのではありません。他の人は、コード内の何かがその間にカウントをリセットしない限り、チップをリセットするだけのように見せました(これは「通常の」使用法のようです)。

TIMER1_COMPA_vectまたは同様のWDTを使用する方法はありますか。私はそれが1秒のタイムアウトモードを持っていることに気付きました、そして私は本当に私のコードで1秒ごとに何かを起こさせるためにそれを使用することができるのが大好きです(そして好ましくはその間スリープします)。

考え?

* アップデート:*質問されたので、私が言及しているのはATTinyX5データシートのセクション8.4です。私がそれを完全に理解しているわけではなく、それが私の問題です...


1
枠を超えて考えるための+1。AVRのデータシートへのリンクを追加する場合、余分な親指を立てます。
ジッピー

回答:


23

間違いなくできます。データシートによると、ウォッチドッグタイマーをセットアップして、MCUをリセットするか、トリガー時に割り込みを発生させることができます。割り込みの可能性に興味があるようです。

実際には、WDTはあまり有用ではない同じ理由で、通常のタイマーよりもセットアップが簡単です。つまり、オプションが少なくなります。内部で較正された128kHzクロックで動作します。つまり、そのタイミングはMCUのメインクロック速度の影響を受けません。また、最も深いスリープモード中も実行を継続して、ウェイクアップソースを提供できます。

いくつかのデータシートの例と、(Cで)使用したいくつかのコードについて説明します。

含まれるファイルと定義

まず、動作するように次の2つのヘッダーファイルを含める必要があります。

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

また、標準のAVRヘッダーの1つで次のように定義されているマクロ<_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スリープ

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   
  ****************************/

   }
 }

すごい...非常に詳細。ありがとう!メインループを表示するスリープセクションに少し混乱しています(if(MCUCR&_BV(SE)){//スリープが有効になっている場合...など)メインルックでなぜそうなるのか混乱しています割り込みを継続的に無効および有効にします。そして、そのセクションの一番上の部分(set_sleep_mode(SLEEP_MODE_PWR_DOWN);)それはどこで実行されるのでしょうか?
アダム・ハイレ・

OK、 "set_sleep_mode(MODE)"部分はメインループの前のメイン、理想的にはI / Oポート、タイマーなどをセットアップする他の初期化コードにある必要があります。最初のWDTトリガーの後に行われるため、その時点で。メインループ内で、スリープコードが実行されるのはスリープコードが有効になっている場合のみであり、sleep_bod_disable()を実行している場合にのみ、そこで割り込みを無効化/再有効化する必要はありません。そのIFステートメント全体は、そこで実行している他のコードの後、MAINループの下部(ただし、まだ内部)にある可能性があります。
カートE.

わかりました...それは今より理にかなっています。私は上のあいまいだ唯一の最後のものは、この「タイミングシーケンスは、」...何であるかである
アダム・ハイレ・

サイドノート:私はあなたがスリープ状態にしたいと思う理由を知っているが、私はあなたがいないことを前提としてい WDTを使用したときに?
アダム・ハイレ・

データシートのこの小さなセクションを見てください:8.4.1。基本的に、WDTレジスタを変更するには、変更ビットを設定してから、非常に多くのクロックサイクル内で適切なWDTCRビットを設定する必要があります。WDTセットアップセクションで提供したコードは、最初にWD変更ビットを有効にすることでこれを行います。いいえ、WDTでスリープ機能を使用する必要はまったくありません。それは、あなたが望むどんな理由であれ、標準的なタイミング割り込みソースである可能性があります。
カートE.クロシエ

1

データシートによると可能です。割り込みとリセットの両方を有効にすることもできます。両方が有効になっている場合、最初のウォッチドッグタイムアウトにより割り込みがトリガーされ、割り込み有効ビットが設定解除されます(割り込み無効)。次に、次のタイムアウトでCPUがリセットされます。割り込みが実行された直後に割り込みを有効にすると、次のタイムアウトは(再び)割り込みのみをトリガーします。

リセットをまったく有効にせずに、割り込みを有効にすることもできます。割り込みがトリガーされるたびにWDIEビットを設定する必要があります。


うーん....それは理にかなっていると思います。試してみます。私はいつも:)彼らが意図していなかった何のために何かを使用してのような
アダム・ハイレ・

実際、私はそれが賢いデザインだと思います。ウォッチドッグ機能を維持しながら、タイマーを節約します。
トムL.

2
このWDTの初期タイムアウトと後続の割り込みは、実際のハングアップ回復のためにWDTを有効にしている一部のアプリケーションに有利に使用することもできます。WDT ISRのスタックリターンアドレスを見て、「予期しないタイムアウト」が発生したときにコードが何をしようとしていたかを推測できます。
マイケルカラス

1

これは上記および他の場所で提案されているよりもはるかに簡単です。

WDTONヒューズがプログラムされていない限り(デフォルトではプログラムされていません)、必要なのは...

  1. ウォッチドッグ制御レジスタでウォッチドッグ割り込みイネーブルビットとタイムアウトを設定します。
  2. 割り込みを有効にします。

16msごとに1回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

セットアップ中にタイミングシーケンスを実行しないと、コードが1行節約されます-読み取り-変更-書き込み操作。最初のシーケンスはデータシートで推奨されており、「ウォッチドッグが誤って有効になった場合、たとえば暴走ポインターまたは電圧低下状態」であり、残りのコードはWDTをスリープモードと組み合わせて使用​​することに固有です。 OP。あなたの解決策は単純ではなく、推奨/必須のボイラープレートコードを無視しただけです。
カートE.クロシエ

@ KurtE.Clothier申し訳ありませんが、最も単純な作業例を提供しようとしています!
bigjosh
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.