●Arduino スリープとウオッチドックタイマー

■外部割込でスリープ復帰

#include <avr/sleep.h>

void setup() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  //スリープモード設定

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2),func_int,FALLING);
}

void loop() {
  sleep_enable();     //スリープを有効化
  sleep_cpu();        //スリープ開始(ここでプログラムは停止する)
  sleep_disable();    //スリープを無効化
  digitalWrite(LED_BUILTIN,1);
  delay(100);
  digitalWrite(LED_BUILTIN,0);
}

//割り込み処理ルーチン(空だけど必要)
void func_int(){
}

デジタルピン2番とGNDの間にスイッチを取り付けて、押すとスリープから復帰しLEDが点灯します。
スリープを使うということは消費電力を抑えたいという意図があると思うので、 スリープモードは一番消費電力が少ないSLEEP_MODE_PWR_DOWNにしています。
スリープモードは次のものがあり各々機能が違います。

SLEEP_MODE_IDLE
SLEEP_MODE_ADC
SLEEP_MODE_PWR_DOWN 外部発振器停止 復帰方法(外部割込、ウオッチドックタイマー、RESETピン)
SLEEP_MODE_PWR_SAVE
SLEEP_MODE_STANDBY
SLEEP_MODE_EXT_STANDBY


■ウオッチドックタイマー

ウオッチドックタイマー(WDT)は、プログラムがハングアップしたときに自身をリセットするタイマーです。
いわゆる自分を見張る番犬みたいなもので、定期的に犬をなで続けないと自分が殺されるという感じでしょうか。
外部回路で動作する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


■ウオッチドックタイマーでスリープ復帰

ウオッチドックタイマーを利用してスリープ状態から復帰するようにします。
この場合、ウオッチドックタイマーとしては使用できませんが、定期的に目覚めて、再びスリープすることによりかなりの消費電力低減ができるはずです。

#include <avr/sleep.h>
#include <avr/wdt.h>

void setup(){
  pinMode(LED_BUILTIN, OUTPUT);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);

  wdt_enable_test(WDTO_2S);
}

void loop(){
  sleep_enable();
  sleep_cpu();
  sleep_disable();

  digitalWrite(LED_BUILTIN,1);
  delay(100);
  digitalWrite(LED_BUILTIN,0);
}

//引数にはWDTO_500MS WDTO_1S WDTO_2S WDTO_4Sなどと設定
void wdt_enable_test(byte b){
  //ウォッチドッグ・タイマー分周比設定ビットの4ビット目がWDTCSRレジスタで2ビットずれているための処理
  b = ((0b00001000 & b) << 2) | (0b00000111 & b);
  //WDCE ウォッチドッグ変更許可ビットを立てる
  b |= 0b00010000;
  //WDRF ウォッチドッグ・システム・リセット・フラグにゼロを書き込んでリセットの準備をする
  MCUSR &= 0b11110111;
  //WDE ウォッチドッグ・システム・リセット許可のビットを立ててリセット WDCE ウォッチドッグ変更許可により4サイクル以内は設定を変更可能
  WDTCSR |= 0b00011000;
  //事前に作成しておいた設定を流し込む
  WDTCSR = b;
  //WDIE ウォッチドッグ・割込み許可ビットを立てる
  WDTCSR |= 0b01000000;
}

//ウォッチドッグの割り込み処理ルーチン(空だけど必要)
ISR(WDT_vect) {
}

以上のプログラムでは、スリープ後に2秒でウオッチドックタイマーにより復帰します。
wdt_enable_test に渡す設定時間は、ウオッチドックタイマーの時間定義と同じであり、 8秒止めたい時は WDTO_8S 、500ミリ秒止めたい時は、 WDTO_500MS とします。
wdt_enable_test 関数内部では、通常はウオッチドックタイマーの動作としてマイコン自体のリセットがかかるところ、 割り込みモードに変更しており、マイコン自体のリセットはかかりません。
ただ、AVRマイコンにべったりでライブラリで見当たらないので直接レジスタを設定して機能を実現しています。


▼設定したレジスタの内容

MCUSR(MCU ステータス・レジスタ)
[ - ][ - ][ - ][ - ][WDRF][BORF][EXTRF][PORF]
WDRF ウォッチドッグ・システム・リセット・フラグ
BORF ブラウン・アウト・リセット・フラグ
EXTRF 外部リセット・フラグ
PORF パワーオン・リセット・フラグ

WDTCSR(ウォッチドッグ・タイマー・コントロール・レジスタ)
[WDIF][WDIE][WDP3][WDCE][WDE][WDP2][WDP1][WDP0]
WDIF ウォッチドッグ・割込みフラグ
WDIE ウォッチドッグ・割込み許可
WDCE ウォッチドッグ変更許可
WDE ウォッチドッグ・システム・リセット許可
WDP ウォッチドッグ・タイマー分周比

MCUSRレジスタとWDTCSRレジスタを操作することによりAVRマイコンの動きを変えて実現しています。


■さらなる低消費電力の為に

AD変換は使っていなくても常に動作しているため多少の電力を消費します、使っていないならスリープする前に無効にしちゃいましょう。

ADCSRA(ADCコントロール・ステータス・レジスタA)
[ADEN][ADSC][ADATE][ADIF][ADIE][ADPS2][ADPS1][ADPS0]
ADEN AD変換を有効にするには、"1"を設定。

ADCSRA &= 0b01111111;    //AD変換無効化
ADCSRA |= 0b10000000;    //AD変換有効化


▲トップページ > マイコンなど