umtkmの忘備録

【F401+mbed】ウォッチドッグタイマでロボットの暴走を防止する

 マイコンでロボットを制御する場合,プログラムが複雑になっていくと,バグで無限ループに突入するなどしてロボットが暴走してしまうことがあります.今回はこれを防ぐために,STM32F4のウォッチドッグタイマを使ってみようと思います.

(ハードウェア的なアプローチ.非常停止スイッチでマイコンを突き破るのかな…?)

環境

 今回はリンカスクリプトをいじったりしないので,多分オンラインコンパイラでも出来るのではないかと思います(未確認).

  • Nucleo F401RE
  • macOS

ウォッチドッグタイマって何やねん

 ウォッチドッグ(Watch Dog)は番犬を意味します.マイコンの動作を監視して,無限ループに陥るなどマイコンが動作を停止したときにマイコンをリセットします.

 ウォッチドッグタイマは起動するとダウンカウント/アップカウントを開始します.んで,このカウンタがアンダーフロー/オーバーフローするとマイコンがリセットされます.平常時は,常にこのタイマをリセットすることによりプログラムは動作し続けますが,無限ループするなどタイマをリセットすることができなくなると時間経過でマイコンそのものにリセットがかかります.

STM32のウォッチドッグタイマ

 STM32にはIWDG,WWDGの2種類のウォッチドッグタイマがありますが,今回はIWDG(独立型ウォッチドッグタイマ)を使っていこうと思います.IWDGはダウンカウントのウォッチドッグタイマであり,値が0になるとマイコンをリセットします.IWDGをリセットするときには0~0x0FFFまでの値をタイマにセットすることができ,これによりマイコンがリセットされるまでの時間を調整できます.クロックはLSIからプリスケーラ(後述)を挟んで供給されています.

 これらのことから,IWDGはマイコンがリセットされるまでの時間を調整することができます.そのためにはLSIの周波数,プリスケーラの調整が必要になります.

LSIの周波数を知る

 こんなものはF401のデータシートを読めば一瞬で出てきます.81ページです.Typicalな場合32kHzですが,個体差で±15kHzぐらいずれてしまうことを頭にとどめておいてください.HALのユーザーマニュアルにはタイマーでLSIの周期測れと書いてありましたが,面倒なのでやりません.

プリスケーラの調整

 LSIのクロックは直接IWDGに入力されず,プリスケーラを挟んでIWDGに入力されます.プリスケーラは日本語にすると分周器で,周波数を落とすのに使うことができます.LSIのクロックをそのままIWDGに突っ込むと一瞬でアンダーフローしてしまうので,プリスケーラで分周してからタイマに入力するわけです.

 さて,このプリスケーラはプログラマブルなので,4,8,16,32,64,128,256のいずれかの数値をセットできます.出力される周波数は入力された周波数をセットした数値で割ったものになります.セットする数値が大きければ大きいほどマイコンがリセットされるまでの時間が長くなります.

HALの関数でIWDGを起動,リセット

 構造体と関数の説明は省略して,とりあえずソースを貼り付けます(やっつけ).今回はclassで実装しました.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class WatchDog {
private:
IWDG_HandleTypeDef hiwdg;
public:
WatchDog(uint32_t prescaler = IWDG_PRESCALER_256, uint32_t reload = 0x0FFF) {
hiwdg.Instance = (IWDG_TypeDef*)0x40003000;
hiwdg.Init.Prescaler = prescaler;
hiwdg.Init.Reload = reload;
HAL_IWDG_Init(&hiwdg);
}
void kick() {
HAL_IWDG_Refresh(&hiwdg);
}
};

 ここで,

1
hiwdg.Instance = (IWDG_TypeDef*)0x40003000;

の部分の0x40003000は”Register base address”を貼れとのことだったんで適当に”register boundary addresses”のIWDGのところを貼りつけたらなんか動きました.データシートには,人生の大切な全てのことが詰まっているんだよ(54ページ).

サンプルプログラム的な何か

 上のWatchDog classを利用したサンプルプログラムを示します.Lチカを5回くらいすると力尽きてしまい光らなくなってしまうんですが,IWDGによるリセットで復帰します.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "mbed.h"

int main(void)
{
WatchDog wdg(IWDG_PRESCALER_8, 0x0FFF);

mbed::DigitalOut led(LED1);
int count = 0;
while (true) {
led = !led;
wait_ms(500);
wdg.kick(); // IWDGをリセット
if (++count == 10) while (true);
}

return 0;
}

 WatchDog classのコンストラクタの第一引数IWDG_PRESCALER_8の数字部分を4,16,32,64,128,256のいずれかに変更すると分周比を変更できます.また,第二引数(reload)に0から0x0FFF(=4095)までの値を与えることで細かい時間の調整ができます.リセットまでの時間は,

(時間) = ( (reload) / (LSIの周波数) ) * (分周比)

で与えられます.125usから32.76sの間で変更できるはずです(@32kHz).なお,前述した通り,LSIの周波数はガバガバなので余裕をもって設定しておくことを推奨します.余裕をもって優雅たれ.

まとめ

 STM32F4のIWDGは時間調整さえちゃんとすれば簡単に扱えます.ロボットの制御周期に合わせてメインのループに挟んだり割り込みでリセットしたりすれば暴走する危険性が減るでしょう.