日々の生活を好き勝手につづったブログ(My blog is written by inoshita.jp)
WDT(ウッチドックタイマー)は設定したタイムアウト時間でシステムリセットを発生させるタイマーである
通常の使用方法は,メインの処理と並行してWDTがタイムアップする前にタイマーをリセット(初期化)する動作を繰り返す
そこでもしプログラムがハングアップまたはループなどの異常が起きた場合タイマーのリセットが行われなくなくなるためシステムリセットされるという仕組みとなっている(①システムリセット動作種別)
ATtiny13aのWDTではリセットさせずに割込みのみを発生させることができ,本来のWDT使用目的以外に単なるタイマーとしても利用できる(②割り込み動作種別)
また,システムリセット前に割込みを発生させリセット前の処理を行うことが可能となっている(③割り込み及びシステムリセット動作種別)
WDTはヒューズビットでONにしておかないといけないが,初期値としては(①システムリセット動作種別)しか選択できない
尚,WDTは独立した128kHz発振器で動作しているので通常のプログラムから影響を受けることはない
ビット3(WDRF)
ビット7(WDTIF)
ビット6(WDTIE)
ビット4(WDCE)
ビット3(WDE)
ビット5,ビット2~0
WDT開始
void WDT_set(int wt) {
cli(); //全割り込み禁止
WDT_reset(); //ウォッチドッグタイマーリセット
WDTCR |= _BV(WDCE)|_BV(WDE); //システムリセット動作設定
WDTCR |= wt; //タイマ設定
sei(); //全割り込み許可
}
WDT停止
void WDT_off() {
cli();
WDT_reset(); //ウォッチドッグタイマーリセット
MCUSR &= ~_BV(WDRF); //MCUSRレジスタのウォッチドッグフラグをクリア
WDTCR |= _BV(WDCE)|_BV(WDE); //WDCEとEDEに1をセット
WDTCR = 0; //ウォッチドッグ停止
sei();
}
WDT割込みルーチン
ISR(WDT_vect) {
//処理
}
リセット許可(WDE)の解除とタイムアウト時間の変更についての手順
同時にWDT変更許可(WDCE)とWDEをセットする(WDEは直前の値に拘らずセット)
次から4クロック内に1操作でタイムアウト値(WDP5,2~0ビット)を書き込むがWDCEビットは(0)にする
// ATtiny13Aのウォッチドックタイマー(WDT)の実験
//
// +-\/-+
// ADC0 (D 5) PB5 1| |8 Vcc
// ADC3 (D 3) PB3 2| |7 PB2 (D 2) ADC1
// ADC2 (D 4) PB4 3| |6 PB1 (D 1) PWM1
// GND 4| |5 PB0 (D 0) PWM0
// +----+
//
// WDTテスト項目
// (0)LEDの点灯点滅確認
// 点灯5秒>点滅・・・
// (1)WDTリセット設定して(普通の)リセット動作確認
// 点灯5秒>点滅2回>リセット
// (2)WDTリセット設定して4回リジェクトさせリセット動作確認
// 点灯5秒>点滅8回>リセット
// (3)WDT割込み+リセット設定して割込みで4回リジェクトさせリセット動作確認
// 点灯5秒>点滅>高速点滅>リセット
// (4)WDT割込み設定で動作確認
// 点灯5秒>点滅>高速点滅
//
// 起動時リセット動作が判るようにLEDを数(5)秒間点灯させ数秒後から点滅
// 状態遷移が判るように1秒間隔点滅と0.5秒間(高速)隔点滅を用意
//
#define WDT_TEST 3 //テストモード0,1~4
#define PIN_LED PB3 //動作LED
#define STUP_FLASH_TIME 5000 //起動時LED点灯時間
#include<avr/interrupt.h>
//WDT利用のための定義
#define WDT_reset() __asm__ __volatile__ ("wdr")
#define WDT_2S B000111
#define WDT_4S B100000
#define WDT_8S B100001
static boolean fastFlash = false; //LED高速点滅指定フラグ
// WDTタイマー開始(タイマー設定)
// WDCE: 動作タイマ変更許可bit
// WDE: リセット
// WDTIE: 割込み
void WDT_set(int wt) {
WDT_reset(); //ウォッチドッグタイマーリセット
#if WDT_TEST == 1 || WDT_TEST == 2
WDTCR |= _BV(WDCE)|_BV(WDE); //システムリセット動作設定
#elif WDT_TEST == 3
WDTCR |= _BV(WDCE)|_BV(WDE)|_BV(WDTIE); //割り込み後システムリセット動作設定
#elif WDT_TEST == 4
WDTCR |= _BV(WDCE)|_BV(WDTIE); //割り込み動作設定
#endif
WDTCR |= wt; //タイマ設定
}
// WDTタイマー停止
void WDT_off() {
WDT_reset(); //ウォッチドッグタイマーリセット
MCUSR &= ~_BV(WDRF); //MCUSRレジスタのウォッチドッグフラグをクリア
WDTCR |= _BV(WDCE)|_BV(WDE); //WDCEとEDEに1をセット
WDTCR = 0; //ウォッチドッグ停止
}
#if WDT_TEST == 3 || WDT_TEST == 4
static int isrCount = 0;
// タイマー割り込みで呼び出される関数
ISR(WDT_vect) {
isrCount++;
if(isrCount > 2) {
fastFlash = true; //2回割込み後は点滅を高速化
}
#if WDT_TEST == 3
if(isrCount < 5) {
//WDT_reset(); //これだけでは割込み後のリセットは回避できなかった
WDTCR |= _BV(WDE)|_BV(WDTIE); //割り込み後システムリセット動作設定(これだけで回避できる)
}
#endif
}
#endif
void setup() {
cli(); //全割り込み禁止
WDT_off(); //起動直後WDT停止
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, HIGH);
delay(STUP_FLASH_TIME);
#if WDT_TEST != 0
WDT_set(WDT_4S); //ウォッチドッグタイマ4秒でリセット
#endif
sei(); //全割り込み許可
}
void loop() {
#if WDT_TEST == 2
int rtcnt = 0;
#endif
for(;;) {
digitalWrite(PIN_LED, LOW);
fastFlash? delay(500): delay(1000);
//1秒後(通常)
digitalWrite(PIN_LED, HIGH);
fastFlash? delay(500): delay(1000);
#if WDT_TEST == 2
//2秒後(通常)
if(rtcnt++ < 5) {
WDT_reset(); // ウォッチドッグタイマリセット
}
#endif
}
}