2020年10月10日土曜日

AVRマイコンのArduinoでウォッチドッグタイマーを使う


背景

ウォッチドッグタイマーとは、計算機が可動し続けていることを確認するためのタイマーです。
具体的には定められた時間応答が無ければ計算機をリセットする機能です。

Arduinoでウォッチドッグタイマーを使う方法が気になっていましたが、難しそうだと想像して着手していませんでした。
取り組んでみると思っていた以上に少ない記述で実現できたので、備忘録を兼ねて方法を共有します。

使ったもの

  • Arduino Uno + USBケーブル
  • Arduinoにプログラムを書き込めるPC
    Arduino IDE(1.8.13)でもPlatformIO(5.0.1)でも動くことを確認しました。

書き方

ウォッチドッグタイマーを利用して、シリアルポートからの通信が4秒以上途絶えたら再起動を行うプログラムを共有します。
#include <avr/wdt.h>
#define PIN_LED 13

void setup() {
Serial.begin(9600);
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, HIGH);
delay(10);
digitalWrite(PIN_LED, LOW);
wdt_enable(WDTO_4S);
Serial.println("started");
}

void loop() {
while (Serial.available()) {
wdt_reset();
Serial.print("Received: ");
Serial.println(Serial.read(),);
Serial.println("at " + String(millis()));
}
delay(1);
}

要所を説明します。

avr-toolchainでwdt (watchdog timer の略)というライブラリが提供されているため、それをプロジェクトで読み込みます。
#include <avr/wdt.h>

起動したことが分かるように、起動後にLEDを光らせています。
#define PIN_LED 13

void setup() {
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, HIGH);
delay(10);
digitalWrite(PIN_LED, LOW);
}

wdt_enableで、ウォッチドッグタイマーを有効化できます。
void setup() {
wdt_enable(WDTO_4S);
}

今回は4秒(WDTO_4S)を利用しました。
wdt.hには下記の種類のマクロが定義されているため、プロジェクトに応じて使いたい長さを選んでください。
  • WDTO_15MS
  • WDTO_30MS
  • WDTO_60MS
  • WDTO_120MS
  • WDTO_250MS
  • WDTO_500MS
  • WDTO_1S
  • WDTO_2S
  • WDTO_4S
  • WDTO_8S

wdt_resetを呼ぶと、ウォッチドッグタイマーのカウントがリセットされます。
    wdt_reset();

シリアルポートから情報を受け取ったらwdt_resetを呼び出してカウンタをリセットしています。
void loop() {
while (Serial.available()) {
wdt_reset();
}
delay(1);
}

起動時に「started」を、受信時に受信した文字と起動からの経過時間を送信しています。
void setup() {
Serial.begin(9600);
// ..
Serial.println("started");
}

void loop() {
while (Serial.available()) {
Serial.print("Received: ");
Serial.println(Serial.read(),);
Serial.println("at " + String(millis()));
}
delay(1);
}

動作確認

Arduino IDEからUnoにプログラムを書き込み、シリアルモニタで出力を見たときのスクリーンショットを共有します。

何もしないと4秒毎に再起動されてstartedが表示されます。

LEDも4秒毎に光ります。

文字を送信するとカウンタがリセットされるので、startedが表示される間隔が4秒より長くなります。
下記の例では時々bという文字を送っています。

b以外の文字も受信しているのは、Arduino IDEのシリアルポートが自動で付与する改行コードであるCRとLRです。(余分な文字を送りたくない場合は、改行なしを選んでください。)

2秒ごとにbを4回送信すると、このようなログになりました。
21:36:54.135 -> started
21:36:58.619 -> started
21:37:02.605 -> Received: b
21:37:02.638 -> at 4012
21:37:02.638 -> Received: 21:37:02.671 -> at 4014
21:37:02.671 -> Received:
21:37:02.671 ->
21:37:02.671 -> at 4015
21:37:04.931 -> Received: b
21:37:04.964 -> at 6336
21:37:04.964 -> Received:
21:37:04.997 -> at 6337
21:37:04.997 -> Received:
21:37:04.997 ->
21:37:04.997 -> at 6338
21:37:07.923 -> Received: b
21:37:07.923 -> at 9275
21:37:07.923 -> Received:
21:37:07.957 -> at 9275
21:37:07.957 -> Received:
21:37:07.957 ->
21:37:07.957 -> at 9277
21:37:11.217 -> Received: b
21:37:11.217 -> at 12509
21:37:11.251 -> Received:
21:37:11.251 -> at 12510
21:37:11.251 -> Received:
21:37:11.284 ->
21:37:11.284 -> at 12511 21:37:15.735 -> started 21:37:20.221 -> started

受信する毎にウォッチドッグタイマーのカウントがリセットされるため、情報を定期的に送信した場合は約17秒(21:36:58.619から21:37:15.735)動き続けました。

まとめ

AVRマイコンを利用しているArduinoで、avr/wdt.hを通してウォッチドッグタイマーを利用できました。

参考

<avr/wdt.h>: Watchdog timer 操作

ソースコードはMicrochipのページからダウンロードできるファイルが大元だと思います。
ウォッチドッグタイマーを呼び出すavr/wdt.hに関する公式の説明ページは今の所見つけられていません。
公式解説ページがあったらコメントなどで教えてもらえると嬉しいです。

0 件のコメント :