WatchDogタイマで対策

最終更新日 2021年4月26日

Arduino環境でWebServerを構築し、外部から適当に間違ったURLをCallすると無限ループに入ってしまい、メインループが止まってしまう事があります。この場合、ハードウエアリセットを実行するのが一般的ですが、マイコンの場合はWatchDogTimerによる監視を行い、規定時間でWatchDogTimerResetが掛からなければ自動でリセットが掛かる機能を採用するのが一般かと思います。
ablic社のWebSiteが詳しく書かれているように感じました。
https://www.ablic.com/jp/semicon/products/automotive/automotive-watchdog-timer/intro/

ArduinoのWDT機能の使い方は以下のように書かれていました。

//WDTのリセット動作はどこでも良いというわけでもなく、ハングアップを検出できる位置を慎重に選ぶ必要があります。
#include <avr/wdt.h>
void setup(){
  wdt_enable(WDTO_4S);  //WDTを4秒で設定し有効化
  //wdt_disable();      //WDTの無効化
}
void loop(){
  wdt_reset();  //WDTのリセット
}
//ウオッチドックタイマーの設定時間はヘッダーの中で次のように定義されており、適当なものを使用します。
#define    WDTO_15MS    0
#define    WDTO_30MS    1
#define    WDTO_60MS    2
#define    WDTO_120MS   3
#define    WDTO_250MS   4
#define    WDTO_500MS   5
#define    WDTO_1S      6
#define    WDTO_2S      7
#define    WDTO_4S      8
#define    WDTO_8S      9

WDTはハードウエア依存なので、STM32duinoの場合はコードは異なります。
STM32Fのchipには、ドキュメントによると7つのタイマが搭載されていることが判ります。そのうち2つがWDTです。
ライブラリで対応していないか調査したのですが、作者のRoger Clark氏がライブラリを作るべきと言うようなpostをしていました。残念ながらCoreでは対応していない様です。ただ、汎用タイマの流用は避けたいので、WDT用のタイマを使いたいです。

Mapleのライブラリを見ていたところ、#include <libmaple/iwdg.h>が有効であることが判りました。これでWDTが使えそうです。サンプルスケッチを検索しました。
leaflabs社は現在でもAPI等の資料提供しています。素晴らしいです。
https://docs.leaflabs.com/static.leaflabs.com/pub/leaflabs/maple-docs/0.0.12/libmaple/api/iwdg.html
独立したウォッチドッグを使用するには、最初に、アプリケーションに適切なプリスケーラーとIWDGカウンターのリロード値を指定してiwdg_init()を呼び出します。
その後、IWDGカウンターが0に達する前に、定期的にiwdg_feed()を呼び出して 、カウンターをリロード値にリセットする必要があります。そうしないと、チップがリセットされます。
一度開始すると、独立ウォッチドッグをオフにすることはできません。この点はAVRとは異なります。

STM32のWDTは2つ有ります。この点に触れておきたいと思います。

IWDG

STMicro社のサイトに詳細があります。
https://www.stmcu.jp/wp/wp-content/uploads/files/presentation-ja/STM32WB/41_STM32WB-WDG_TIMERS-Independent-Watchdog-(IWDG)_J.pdf

  • 独立型ウォッチドッグは、ソフトウェア障害による誤動作の検出と解決に使用されます。
  • 32kHz RC オシレータからクロック供給を受けます。
  • 独立型ウォッチドッグは、125マイクロ秒から 32秒までの広範囲なタイムアウト値に対応しています。
  • プログラム可能なタイムウィンドウ幅
  • 以下の時点でリセットを生成:
    • タイムアウト値に到達
    • ウィンドウ外でリフレッシュが発生
  • DEBUG、STOP、STANDBY の各モードで停止可能
  • 自動的に有効化されるように設定可能

LSE=32.768 kHz

IWDG_Prescaler 内容説明 最小時間[ms] 最大時間[ms]
IWDG_Prescaler_4 4分周 0.122 500
IWDG_Prescaler_8 8分周 0.244 999
IWDG_Prescaler_16 16分周 0.488 1999
IWDG_Prescaler_32 32分周 0.977 3998
IWDG_Prescaler_64 64分周 1.95 7995
IWDG_Prescaler_128 128分周 3.91 15991
IWDG_Prescaler_256 256分周 7.81 31982

※2021年4月26日:WebPage表示に異様に時間が掛かるためWDTを極力長くしたいと思いました。
iwdg_init_ms(N) iwdg_init(IWDG_PRE_256,((N)/6)) でN=30000にすれば30秒が実現すると思いました。しかし、30000にすると数秒でリセットが掛かります。原因を調べたところ、(N)/6)の部分は12bitカウンタで実現しているという事で、4095に押さえておく必要があるという情報がありました。その考えに従うと4095×6・・・24570となり、24秒までしか実現出来ないのかもしれません。
実際のところ24000に変更すれば、WDTのリセット間隔が延びました。この点は再調査が必要かもしれません。

1/32768Hz(30.5usec) × 256(8bitカウンタ)・・・・7.8msec × 4096(12bitカウンタ)・・・・32秒
このことから、マクロの定義の仕方は
iwdg_init(IWDG_PRE_256,((N)/8))が正しいのでは無いかと思います。

N<32000 30000/8・・・3750<4095

WWDG

STMicro社のサイトに詳細があります。
https://www.stmcu.jp/wp/wp-content/uploads/files/presentation-ja/STM32WB/42_STM32WB-WDG_TIMERS-System-Window-Watchdog-(WWDG)_J.pdf

  • ウィンドウ型ウォッチドッグは、ソフトウェア障害の発生検知に使用されます。
  • プログラム可能なタイムアウト値
  • プログラム可能なタイムウィンドウ幅
  • リセット生成:
    • タイムアウト値到達時
    • タイムウィンドウ外でリフレッシュされた場合
  • 早期ウェイクアップ割込み(EWI)
    • タイムアウト値に到達する前に生成

/*
 * 2021/01/07 T.Wanibe STM32でWDTを使う検証コードです。
 * 資料によると、wdtのBaseClockは40kHzだと有ります。
 * 最大131072バイトのフラッシュメモリのうち、スケッチが16124バイト(12%)を使っています。
 * 最大20480バイトのRAMのうち、グローバル変数が3128バイト(15%)を使っていて、ローカル変数で17352バイト使うことができます。
 */
#include <libmaple/iwdg.h>
#define LED1 PB8
#define LED2 PB9
#define iwdg_init_ms(N) iwdg_init(IWDG_PRE_256,((N)/6))
//---------------
void setup()
{
        Serial.begin(115200);
        pinMode(LED1,OUTPUT);
        pinMode(LED2,OUTPUT);
        iwdg_init_ms(5000);                             //ウォッチドッグをオンにして、オーバーフロー時間を設定します。
                                                        //実測値で5000だと6000m秒弱でリセットが掛かります。
                                                        //6000だと6000m秒ではリセットが掛かりません。
                                                        //25usec×256×5000/6=5.3秒でリセットが掛かるという計算になります。
                                                        //25usec×256×6000/6=6.4秒でリセットが掛かるという計算になります。
}
//--------------- 
void loop()
{
        Serial.println(millis());
        digitalWrite(LED1,HIGH);
        delay(1000);
        digitalWrite(LED2,HIGH);
        delay(1000);
        digitalWrite(LED1,LOW);
        delay(1000);
        digitalWrite(LED2,LOW);
        delay(1000);
        digitalWrite(LED1,HIGH);
        digitalWrite(LED2,HIGH);
        delay(1000);
        digitalWrite(LED1,LOW);
        digitalWrite(LED2,LOW);
        delay(1000);
        Serial.println(millis());
        iwdg_feed();                                    //ウォッチドッグタイマーをリセットするには、ドッグ操作をフィードします
}
/* Key register */
#define IWDG_KR_UNLOCK                  0x5555
#define IWDG_KR_FEED                    0xAAAA
#define IWDG_KR_START                   0xCCCC
/* Prescaler register */
#define IWDG_PR_DIV_4                   0x0
#define IWDG_PR_DIV_8                   0x1
#define IWDG_PR_DIV_16                  0x2
#define IWDG_PR_DIV_32                  0x3
#define IWDG_PR_DIV_64                  0x4
#define IWDG_PR_DIV_128                 0x5
#define IWDG_PR_DIV_256                 0x6
/* Status register */
#define IWDG_SR_RVU_BIT                 1
#define IWDG_SR_PVU_BIT                 0
#define IWDG_SR_RVU                     (1U << IWDG_SR_RVU_BIT)
#define IWDG_SR_PVU                     (1U << IWDG_SR_PVU_BIT)

ESP32の場合も調べました。Arduino core for the ESP32が正式にインストールされたArdinoIDE環境であれば、esp_task_wdt.h をインクルードすることで実現可能です。使い方例です。https://iotassistant.io/esp32/enable-hardware-watchdog-timer-esp32-arduino-ide/を参照しました。

#include <esp_task_wdt.h>
//3 seconds WDT
#define WDT_TIMEOUT 3
void setup() {
	Serial.begin(115200);
	Serial.println("Configuring WDT...");
	esp_task_wdt_init(WDT_TIMEOUT, true);		//enable panic so ESP32 restarts
	esp_task_wdt_add(NULL);				//add current thread to WDT watch
}
int i = 0;
int last = millis();
void loop() {
	// resetting WDT every 2s, 5 times only
	if (millis() - last >= 2000 && i < 5) {
		Serial.println("Resetting WDT...");
		esp_task_wdt_reset();
		last = millis();
		i++;
		if (i == 5) {
			Serial.println("Stopping WDT reset. CPU should reboot in 3s");
		}
	}
}


戯言(nonsense)に戻る


免責事項

本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。

本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。

商標・登録商標

Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。