Arduinoで実験 (スリープと復帰)

2013/04/19


◆ スリープ機能

Arduino では終了の概念が無く電源が入っている限りひたすらループ内を実行する。 delay関数などで待機しているようでもマイコン本体は動作を停止しているわけでは無いので電力が消費されている。 終了させるには電力の供給を遮断し停止するしかない。

電源スイッチで開始/終了をコントロールしても構わないがメモリを初期化されてると困る場合がある。 また、バッテリー動作などで省電力を心掛ければならない場合、作業が必要な場合のみ自動で電源が入り、不要になれば自分で電源を切るような動作が求められる場合がある。

本当に電力が遮断されると自分自身で電源を入れることは難しくなるが、外部から見て電源が落ちているように見えるならばオフ状態といってもよい場合がある。

外部から見て停止しているような状態がスリープ状態で、 Arduino の心臓部である ATmega328P 本体でもこの機能が使用できる。

スリープ状態と一言でいっても複数のモードがあり、各モードでスリープ時の動作や復帰条件が変わってくる。

Arduino で使用できるスリープ機能は次の通りだ。

アイドルモード idle
ADCノイズ低減モード noiseReduction
パワーセーブモード powerSave
スタンバイモード standBy
パワーダウンモード powerDown
パワーダウン+外部割り込みによる復帰モード powerDownAndWakeupExtermalEvent

スタンバイモードが最も消費電流が大きく、以下ADCノイズ低減モード、パワーセーブモード、パワーダウンモードの順で消費電力が小さくなる。

◆アイドルモード
アイドルモードでは、システムクロックが停止されるが、内蔵タイマーや、外部割り込み、シリアルポートなどのインターフェイス機能は動作する。
外部割り込み、ウォッチドッグ・タイマー、ADCの入力変化、RISETピンによる復帰ができる。
◆ADCノイズ低減モード
調べたがなんかよくわからん、アイドルモードとなんかちょっと違うらしい。
◆パワーセーブモード
アイドルモード時で有効だったインターフェイス機能も停止する。ただし、タイマー用の外部発振器は動作する。
/INT端子への割り込みやウォッチドッグ・タイマー、RESETピンによる復帰が可能。 ADCの入力変化や外部からの通信による復帰はできない。
◆スタンバイモード
ほぼパワーダウンモードと同じだが、外部発振器は動作する。
◆パワーダウンモード
パワーセーブモード時では有効だった外部発振器も停止する。。
外部割り込み、ウォッチドッグ・タイマー、RESETピンによる復帰が可能。
◆パワーダウン+外部割り込みによる復帰モード
これもわからんが、外部割り込みによる復帰しかできないモードなのかしら?

正直、判らないことだらけだが実験して検証してみよう。


◆ Sleep機能を使用する

とりあえずスリープさせてみる。

//
// 11-01 スリープ実験スケッチ
//

#include <avr/sleep.h>      // IDE に同封されているライブラリをインクルード

int count = 0;

void setup() {
  Serial.begin(9600);      // モニタ用にメッセージを出力
  Serial.println("Sleep Test Start");
  Serial.println("");
  Serial.println("i IDLE");
  Serial.println("a ADC");
  Serial.println("p PWR_SAVE");
  Serial.println("s STANDBY");
  Serial.println("d PWR_DOWN");


  pinMode(13, OUTPUT);          // 動作確認用 LED
  pinMode( 2, INPUT_PULLUP);    // 後の項で使用
}


void loop() {
  Serial.println(count);  // カウンタの送信
  count ++;
  byte cmd;

  digitalWrite(13, HIGH);  // LED を点滅
  delay(10);
  digitalWrite(13, LOW);
  delay(990);

  if (Serial.available()) {
    cmd = Serial.read();
    setSLEEP(cmd);  // コマンドを受信したらスリープ動作へ
  }
}

void setSLEEP(byte c) { // スリープ動作
    switch (c) {
      case 'i':                     // アイドルモードの処理
        Serial.print(" - IDLE -");
        set_sleep_mode(SLEEP_MODE_IDLE);
        goodNight(0);
        break;
      case 'a':                     // ADC低減モードの処理
        Serial.print(" - ADC -");
        set_sleep_mode(SLEEP_MODE_ADC);
        goodNight(1);
        break;
      case 'p':                     // パワーセーブモードの処理
        Serial.print(" - PWR_SAVE -");
        set_sleep_mode(SLEEP_MODE_PWR_SAVE);
        goodNight(1);
        break;
      case 's':                     // スタンバイモードの処理
        Serial.print(" - STANDBY -");
        set_sleep_mode(SLEEP_MODE_STANDBY);
        goodNight(1);
        break;
      case 'd':                     // パワーダウンモードの処理
        Serial.print(" - POWER_DOWN -");
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
        goodNight(1);
        digitalWrite(13, LOW);
    }
}

void goodNight(int i) {
  delay(100);    // シリアル通信のメッセージを送信するための待機時間
  sleep_mode();  // ここでスリープ
}

上記スケッチを実行しても IDLE のみ実行できないような形になる。 多分、外部発振器等で一度スリープしたものがすぐに復帰しているからだと思う。

LED を点灯したままスリープ状態に入っても LED は消灯しなかった。 開いているポートは開いたままでスリープ状態にはいるようだ。 スリープに入る前に十分省電力動作のための処理を行う必要がある。


◆ 外部割り込みで復帰

外部割り込みでスリープからの復帰を試みる。 外部割り込みには D2 ピンを使用する。

goodNight 関数の修正と、外部割り込みでの復帰用関数 wakeup を追加します。

//
// 11-02 外部割り込みでの復帰実験スケッチ
//

#include <avr/interrupt.h>  // 外部割り込みライブラリのインクルード


setup()、loop() setSLEEP()は変更なし


void goodNight(int i) {
  Serial.println("  Good Night");
  delay(100);
  if (i) attachInterrupt(0, wakeup, RISING);  // D2 LOW → HIGH で割り込み
  sleep_mode();
}

void wakeup() {
  Serial.println("Good Morning");
  delay(100);
  detachInterrupt(0);  // 割り込み停止
}

自分は RTC モジュールの /INT 端子を利用して実験をしてみましたが、全てのモードでの復帰を確認しました。


◆ ウォッチドッグ・タイマ (WDT)

ウォッチドッグとは定期的にアプリケーション間で信号を送り、正常に動作しているかどうかを監視するデバイスのこと。 watch は監視するで意味がわかるのだが、なぜドッグなのかはよくわからん。

各種センサーを用いデータ収集を行う場合。 データ取得にかかる時間は1秒にも満たないが1時間に1回や、1日に1回のデータ収集をする際、待機時間中に消費する電力の方が大きくなる。 そのために、定期的に発生する信号でスリープ状態から再起動することが狙い。

ウォッチドッグ・タイマはマイコン自体の機能としてあるので、ヒューズビットで動作を許可させなければならない。 また、ウォッチドッグ・タイマでの復帰はリセットでの復帰なので。メモリ上の変数は消去される。 PGM を使用してフラッシュ上に記憶するか、EEPROM等の記憶領域の変数を利用する、