Arduinoで割り込みとウォッチドッグタイマを使ったスリープ復帰

マイコン全般に言えることですが、スリープさせると大分消費電力は抑えられます。
車載とか電池とかで使うときには必要な事だと思います。
ウォッチドッグタイマによる復帰については[Jean Rabault website - UiO-Using the Arduino Uno watchdog](http://folk.uio.no/jeanra/Microelectronics/ArduinoWatchdog.html)、
外部割り込みによる復帰は[playground.arduino.cc--Sleep](http://playground.arduino.cc/Learning/ArduinoSleepCode)に詳しく書かれています。

#ちょっと説明
* Arduinoのボードをそのまま使用すると省電力は望めません。AVR単体で動作させてください。
* 16Mhzでの動作となりますので、8Mhzで動作させるときは考慮してください。
* スリープ中はウォッチドッグタイマのオーバーフロー時のリセットを無効にして、割り込みのみでの使用となります。

```C++:wdt_int_.ino
#include <avr/wdt.h> 
#include <avr/sleep.h>

//LED_PIN
#define LED_PIN (13)
//interrupt pin
#define ACC_IN (2)

volatile int sleep_flg=0;
volatile int counter;

int count_max=15;//4second * 15 = 60 second 
int wait_minutes=1;//wait_time (minutes)

//arduino wakeup interrupt
void wakeUp()
{
  //wdt_reset();
  //sleep end
  sleep_flg = 0;
  //counter reset
  counter = 0;
}

//watch dog timer setup
void wdt_set()
{
  wdt_reset();
  cli();
  MCUSR = 0;
  WDTCSR |= 0b00011000; //WDCE WDE set
  WDTCSR =  0b01000000 | 0b100000;//WDIE set  |WDIF set  scale 4 seconds
  sei();
}

//watch dog timer unset
void wdt_unset()
{
  wdt_reset();
  cli();
  MCUSR = 0;
  WDTCSR |= 0b00011000; //WDCE WDE set
  WDTCSR =  0b00000000; //status clear
  sei();
}

//watch dog timer call
ISR(WDT_vect)
{
  //wdt_reset();
  if(sleep_flg == 1)
  {
    counter++;
    if( counter >= (count_max * wait_minutes))
    {
      //sleep end
      sleep_flg = 0;
      //counter reset
      counter = 0;
    }
  }
  else
  {
  }
}

//status reset
void init_status()
{
  counter=0;
}

//sleep arduino
void sleep()
{
  wdt_set(); //watch dog timer set
  sleep_flg=1; //enable on sleep flag
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); //set sleep mode
  attachInterrupt(0,wakeUp, RISING); //set level interrupt
  while(sleep_flg){
    noInterrupts();  //cli();
    sleep_enable();
    interrupts();    //seii();
    sleep_cpu();  //cpu sleep
    sleep_disable();
  }
  wdt_unset(); //watch dog timer unset
  detachInterrupt(0); //unset level interrupt

}

//set up
void setup()
{
  init_status();
  pinMode(LED_PIN,OUTPUT);
  pinMode(ACC_IN, INPUT);
}

//main loop
void loop()
{
  digitalWrite(LED_PIN,HIGH);
  delay(5000);
  if(!digitalRead(ACC_IN))
  {
    digitalWrite(LED_PIN,LOW);
    sleep(); //sleep
  }
}
```

#構成図
![wtd_interrupt_arduino_ブレッドボード.png](https://qiita-image-store.s3.amazonaws.com/0/133915/007cee5d-1120-f98b-4457-2b031bca2463.png)




* 単体で動作と言いつつ思いっきりArduino unoをそのまま使ってますが、検証はこっちのが良いと思います。
* 割り込みのトリガになるのは2番ピンへの入力なので、適当な抵抗(1K~10KΩとか)を介してプルダウンしておきます。
* 起動後に5秒ほど本体の緑色のLEDが点灯してから消灯、スリープに移行します。
* スリープから復帰後は、ウォッチドッグタイマを無効にしていますが、本来の使い方ではここでウォッチドッグタイマのオーバーフロー時のリセットを有効にして、ループ内でタイマのリセットを行い、ハングした時にリセットが掛かるようすべきだと思います。

■割り込み
トリガピンに5Vの電圧が掛かるとスリープから解放されLEDが点灯します。トリガピンの入力がなくなると再びスリープへ

■ウォッチドッグタイマ
スリープの間隔ですが、基本となるスリープの時間が4秒程度(最大8秒)としているため、ウォッチﾄドッグタイマ復旧時に呼ばれるISR内でカウンタを使用しています。
スリープは割り込みが無くても(4*15*2=)120秒毎にスリープから復帰してLEDが5秒点灯します。
`` `count_max` ``変数と`` `wait_minutes` ``変数で時間を合わせてます。
検証した所、数秒ラグがあります。

#メモ
## ウォッチドッグタイマのインターバルの設定

WDTCSR =  0b01000000 | 0b100000;　←　これの右側の値で設定します。

| インターバルタイム |    セットビット      |
|:----------------:|:------------------:|
| 16 ms            |     0b000000       |
| 500 ms           |     0b000101       |
| 1 second         |     0b000110       |
| 2 seconds        |     0b000111       |
| 4 seconds        |     0b100000       |
| 8 seconds        |     0b100001       |

##レジスタ
###MCUSR 
MCUがどの要因によってリセットされたかの情報が格納されているレジスタ。

|  7   |  6   |  5  |  4  |   3  |   2  |  1  |  0  |
|:-----|:-----|:----|:----|:-----|:-----|:----|:----|
| ---- | ---- | ----|---- | WDRF | BORF |EXTRF| PORF|

WDRF ウォッチドッグシステムリセット
BORF ブラウンアウトリセット
EXTRF 外部リセット
PORF パワーオンリセット

###WDTCSR
ウォッチドッグタイマの設定を行うレジスタ

|  7   |  6   |  5  |  4  |   3  |   2  |  1  |  0  |
|:-----|:-----|:----|:----|:-----|:-----|:----|:----|
| WDIF | WDIE | WDP3|WDCE | WDE  | WDP2 |WDP1 | WDP0|

WDP0~3　ウォッチドッグタイマーインターバル(分周比）セット
WDCE ウォッチドッグ変更許可
WDE ウォッチドッグ・システムリセット許可
WDIE ウォッチドッグ・割込み許可
WDIF ウォッチドッグ・割込みフラグ