7839

雑草魂エンジニアブログ

【Arduino】ウォッチドッグタイマー(WDT)

Arduino Unoで死活監視のために、ウォッチドッグタイマー(WDT)を実装したので備忘録として残しておく。

ウォッチドッグタイマー(WDT)とは

ウォッチドッグタイマー(Watchdog Timer:WDT)はマイコンのプログラムが暴走・停止していないかを監視するタイマーである。具体的には、一定時間を経過しても応答がない場合にシステムをリセットする機能である。

ウォッチドッグ(Watchdog)は英語で「番犬」という意味であり、マイコンを見張る役割を果たす。

上記の図では、外付けのWDTを想定している。実際にはMCUの異常を検知するWDT機能はMCU自体にも搭載されている。ただし、その場合にはMCU自体が異常を起こすと、WDT機能が正常に働かない可能性がある。そのため、MCU以外に外付けのWDTを設置して、WDT機能を独立させておくことでシステムの安全性を高めることができる。

ATmega328P

Arduino Unoには、ATmega328Pのマイコンが搭載されている。

ATmega328Pのデータシートを確認すると、「10.8 Watchdog Timer」に詳細な仕様が記載されている。

MCUSRレジスタとWDTCSRレジスタを操作することにより、WDT機能を実現している。

MCUSR(MCU Status Register)

[ - ][ - ][ - ][ - ][WDRF][BORF][EXTRF][PORF]
  • WDRF:Watchdog System Reset Flag
  • BORF:Brown-out Reset Flag
  • EXTRF:External Reset Flag
  • PORF:Power-on Reset Flag

WDTCSR(Watchdog Timer Control Register)

[WDIF][WDIE][WDP3][WDCE][WDE][WDP2][WDP1][WDP0]
  • WDIF:Watchdog Interrupt Flag
  • WDIE:Watchdog Interrupt Enable ウォッチドッグ・割込み許可
  • WDCE:Watchdog Change Enable ウォッチドッグ変更許可 設定を変更する場合に、HIGHにする必要がある。4クロック後にクリアされる。
  • WDE:Watchdog System Reset Enable ウォッチドッグ・システム・リセット許可
  • WDP3..0: Watchdog Timer Prescale Select ウォッチドッグ・タイマー分周比

avr/wdt.h ライブラリ

avr/wdt.hというライブラリをインクルードして使用する。

  • wdt_reset() : WDTの設定をリセットするための関数
  • wdt_enable(value) : WDTを有効にする関数(valueに定数を入れて時間を設定する)
  • wdt_disable() : WDTを無効にする関数

実装例

回路図

  • MCUのWDTを利用する
  • リセットするまでの間隔は、4sとする
  • D13をWDT-OUTとする
  • D12をWDT-INとする
  • 10msごとにloop処理を行い、WDT-OUTをHIGHにして、WDT-INでHIGH信号を受け取れたら、WDT_Reset_Counterをインクリメントする(+1する)
  • WDT_Reset_Counterが200になれば、wdt_reset()を実行する
    • 10ms x 200 = 2000ms(2s)
    • すなわち、リセット時間の半分の約2sごとにMCUにリセット信号を送る
    • 2sごとにリセット信号を送信できない場合、WDTのリセット時間4sを経過してしまい、リセット処理が実行される
#include <avr/wdt.h>
int WDTOUT = 13, WDTIN = 12;
int WDT_Count_Pin, WDT_Reset_Counter, WDT_Reset_Time = 200;

void setup() {
  Serial.begin(9600);
  pinMode(WDTIN, INPUT);
  pinMode(WDTOUT, OUTPUT);
  wdt_reset();
  wdt_enable(WDTO_4S);
  Serial.println("WDTstarted");
}

void loop() {
  digitalWrite(WDTOUT, HIGH);
  WDT_Count_Pin = digitalRead(WDTIN);
  if(WDT_Count_Pin == 1) {
    WDT_Reset_Counter = WDT_Reset_Counter + 1;
    Serial.println(WDT_Reset_Counter);
  };
  if(WDT_Reset_Counter  ==  WDT_Reset_Time) {
    WDT_Reset_Counter = 0;
    Serial.print("WDT_NORMAL");
    wdt_reset();
  };
  digitalWrite(WDTOUT, LOW);
  delay(10);
}

まとめ

Arduinoで無事にWDT機能を実現することができた。 意外に簡単に実装できて、嬉しい限りであった。