workshop/ArduinoBasis/sleep

Arduino のSleep機能

ArduinoにはSleep機能があります.例えば電池駆動をしている状況下ではこのスリープ機能を実装しておかないと,2,3日で電池交換,充電の必要がでてしまい,制作物のユーザビリティが著しく低下してしまいます.ここではC言語ライブラリで提供されている一般的な機能から,0.4uAまで消費電流をさげる所謂Deep Sleep機能に関してAtmega328を具体例として解説します.なお,Deep Sleepに関する記事はideea lab工場長の須田氏による解説になります.

Sleepとは

ノートパソコンやスマートフォン等でもSleep機能は頻繁にユーザは利用しています.パソコンを使い終わったら電源を切る,スマートフォンを使い終わったら電源を切る.このようなことはあまり一般的ではなく,パソコン,スマートフォンを使い終わったらスリープにして,すぐまた使えるようにすることが多いかと思います.パソコンやスマートフォン等の高機能なものだけでなく,身の回りの電子機器のほとんどにはこのスリープ機能が実装されています.中には電源スイッチを使い,使い終わったらスイッチを切ることで電力消費を抑えることができますが,ユーザは基本的にヒューマンエラーや物忘れが多く,電源をつけっぱなしで放っておくことはみなさんも経験あると思います.

Sleepとは電源ON状態であるにも関わらず,マイコンの機能制約を行い,極力消費電力を落とすことです.その為Sleep中は通常の動作はできず,基本的にはユーザが利用するときだけSleepから復帰し,使用後にSleepに戻る.というのが一般的です.ではSleepの復帰にはどのような方法があるでしょうか? Sleep復帰にはタイマー(一定時間間隔で自動復帰),割り込み(PINのHIGH/LOW変化,シリアル通信),Resetピンを使った物理的な電源の入れ直し,といった種類があります.

基本的なSleep利用方法

基本的なSleepの利用方法はすでにArduino公式ウェブサイトにて紹介されていますので,そちらを参照してください.日本語で個人ユーザが解説しているサイトもありますが,まずは公式の情報を理解してください. Arduino Playground - ArduinoSleepCodeこのページでは割り込み(インタラプトピン,シリアル受信)によるSleep復帰を具体的なコードと共に紹介しています.まずはこのページをよく読み,次の解説を読んでください.

Sleepの種類と消費電力と電池持ちと

先のページに記述されていますが,Sleepの種類には下記があります.

SLEEP_MODE_IDLE         -the least power savings
SLEEP_MODE_ADC
SLEEP_MODE_PWR_SAVE
SLEEP_MODE_STANDBY
SLEEP_MODE_PWR_DOWN     -the most power savings

これらのSleepモードがどの程度の消費電力なのかを調べたページがありますので, Arduinoの消費電力|Home, Smart Homeを良く読んでください.

以上から,例えば内部発信8MHzであれば,90uAまで消費電力を落とすことができます.ただしこのページの計測方法では割り込みを利用していないので,スリープ復帰はリセットか電源の入れ直しになります.ここにタイマー(Watch dog)やピン割り込み,シリアル割り込み等のSleep復帰を利用するともう少し電流量が情報します.例えば,3.7V(400mAh)のリチウム充電池を150uAでスリープさせた場合,計算上の継続時間は 400/0.15=2666 となり,2666[h]電池が持続することになります.つまり2666/24 = 102 [day]という計算です.これでも十分な電池節約にはなり,かつC言語で記述ができるのでそれ程気にならなければここまでのやり方で十分かと思います.一方,以後説明では0.1uA - 0.4uAまで電流量を落とすことができるので,さらに電池節約をしたい場合,下記解説をよく読んでください.先ほどの例と比較して,0.4uAでスリープさせた場合,400/0.0004 = 1,000,000[h],1,000,000/24 = 4,1666[day]となり,4,1666/365=114[year]電池が持つことになります.ただし実際には自然放電があるので,こんなに電池が持つことはもちろんありません.一般的にリチウム充電池であれば1ヵ月で5-10%では自然放電してしまうため,このように計算上は非常に電池持ちをよくしても,実際のところ,消費電流を限りなく小さくしても,2-5年程度で電池は切れる.と考えていたほうがよいです.

Deep Sleep

以上のやり方で電力を落とすことで十分消費電力を落とすことができますが,やはり半年位で電池がきれてしまうよりも,数年はもって欲しい気持ちもあります.そこで以下では更に消費電流を落とすSleep(Deep Sleep)を紹介します.以下の記事ではスリープ復帰には電源入れ直し,リセット,タイマー(Watch dog)での復帰を対象として記述しています.また本学卒業生(研究員,ideea lab工場長)の須田氏による解説になります.

AVRマイコン(ATmega328P)のスリープ時消費電力の最小化

Arduinoで開発可能なAVRマイコンは,必要に応じてスリープ状態に移行させることで消費電力を抑えることができます.スリープ機能はライブラリを利用することで簡単に使うこともできますが,より詳細に設定をすることでさらに省電力化が可能です.ここで報告するのは,Arduinoで書き込みを行ったATmega328P-PUを5V,16MHzで単体動作させたときのものです.通常のパワーダウンモードでスリープ状態へ移行したときスリープ時の消費電流は約126[μA]でしたが,各種設定を行うことで約0.1μAまで削減できました.以下にその解説とサンプルコードを記述します.ライブラリは使用せず,レジスタ操作と一部アセンブラで記述しています.他のAVRマイコンでも,対応するレジスタを操作することで同様の省電力化が行えると思います(ATtiny44Aでは確認済み).

Sample01.ino
void setup() {
  // write all digital I/O LOW
  for(int i=0; i<20; i++){
    pinMode(i, OUTPUT);
  }
}

void loop() {
  // set power-down mode
  SMCR |= (1 << SM1);
  SMCR |= 1;

  // disable ADC
  ADCSRA &= ~(1 << ADEN);

  // disable BOD
  MCUCR |= (1 << BODSE)|(1 << BODS);
  MCUCR = (MCUCR & ~(1 << BODSE))|(1 << BODS);
  asm("sleep");
}
Sample01.inoの解説
・各ピンの状態設定
各ピンは動作状態が設定されていないとき浮いている状態となり,不要な電力が消費されてしまいます.
スリープ状態に入る前に,すべてのピンを最小電力状態に設定すべきです.
ArduinoではpinMode関数ですべてのピンを出力状態(OUTPUT)に設定します.

・スリープモードの設定
AVRマイコンのスリープ機能にはいくつかのモードが用意されていますが,最も消費電力を抑えられるパワーダウンモードに設定します.以下のようにレジスタを操作して,モードの選択とスリープ機能の許可を行います.(データシート10.11.1参照)
  SMCR |= (1 << SM1); // パワーダウンモードに設定
  SMCR |= (1 << SE);  // スリープ機能の許可

・A/D変換器(ADC:Analog-to-Digital Converter)の停止
AVRマイコンには,アナログ入力をデジタル値に変換するA/D変換機能があります.
この機能は通常のスリープ移行ではONのままなので,スリープ状態に入る前にOFFにします.
スリープ状態から復帰後,アナログ入力を使用する場合はONに戻します.
以下のようにレジスタを操作することで,ON/OFFが可能です.(データシート24.9.2参照)
  ADCSRA |= (1 << ADEN);  // ON
  ADCSRA &= ~(1 << ADEN); // OFF


・低電圧検出器(BOD:Brown-Out Detecter)の停止
AVRマイコンには,電圧の急降下による誤作動を防ぐ低電圧検出機能があります.
この機能も通常のスリープ移行ではONのままなので,スリープ状態に入る前にOFFにします.
以下のように,決められた手順に従ってレジスタを操作することで設定します.
この操作はスリープ移行の直前でのみ有効です.(データシート10.11.2参照)
  // BOD設定変更のため,BODSとBODSEに同時に1を出力.
  MCUCR |= (1 << BODSE)|(1 << BODS);
  // その後4クロック周期内にBODSに1,BODSEに0を出力してBODをOFFにする.
  MCUCR = (MCUCR & ~(1 << BODSE))|(1 << BODS);
  // その後3クロック周期内にスリープ状態へ移行.
  asm("sleep");

Reference

  • (データシート)http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf
  • (参考動画)https://www.youtube.com/watch?v=urLSDi7SD8M

ウォッチドッグタイマによるスリープ状態からの復帰

スリープ状態のAVRマイコンは,外部から割り込みやリセットをかけることでスリープ状態から復帰できますが,ウォッチドッグタイマ機能を使って一定時間経過後に復帰させることも可能です.

Sample02.ino
void setup() {
  // write all digital I/O LOW
  for(int i=0; i<20; i++){
    pinMode(i, OUTPUT);
  }
}

void loop() {
  // set power-down mode
  SMCR |= (1 << SM1);
  SMCR |= 1;

  // disable ADC
  ADCSRA &= ~(1 << ADEN);

  // set watchdog timer
  asm("wdr");
  WDTCSR |= (1 << WDCE)|(1 << WDE);
 // WDTCSR = (1 << WDIE)|(1 << WDP3)|(1 << WDP0);     // 8 sec
  WDTCSR  = (1 << WDIE) | (1 << WDP2) | (1 << WDP0);    // 0.5 sec

  // disable BOD
  MCUCR |= (1 << BODSE)|(1 << BODS);
  MCUCR = (MCUCR & ~(1 << BODSE))|(1 << BODS);
  asm("sleep");

  // stop watchdog timer
  asm("wdr");
  MCUSR &= ~(1 << WDRF);
  WDTCSR |= (1 << WDCE)|(1 << WDE);
  WDTCSR = 0;

  // enable ADC
  ADCSRA |= (1 << 7);
}

//watchdog interrupt
ISR(WDT_vect){
  digitalWrite(9, HIGH);
  delay(100);
  digitalWrite(9, LOW);
}

Sample02.inoの解説
・ウォッチドッグタイマの設定
ウォッチドッグタイマは,CPUと独立して動作するタイマです.タイマが設定時間に達したときCPUに割り込みやリセットをかけるので,スリープ状態からの復帰に使うことができます.以下のように,決められた手順に従ってレジスタを操作することで,タイマの時間と時間経過時の動作を設定することができます.(データシート11.9参照)
  // ウォッチドッグタイマをリセット
  asm("wdr");
  // ウォッチドッグタイマ設定変更のため,WDCEとWDEに同時に1を出力.
  WDTCSR |= (1 << WDCE)|(1 << WDE);
  // その後4クロック周期内にタイマの時間と時間経過時の動作を設定.
  WDTCSR = (1 << WDIE)|(1 << WDP3)|(1 << WDP0); // 8sec, interrupt

タイマの時間は対応ビット(WDP0~WDP3)への出力を操作することで,最長約8秒まで設定できます.設定時間経過時に行われる動作は対応ビット(WDE,WDIE)への出力を操作することで,リセットまたは割り込みに設定できます.リセットを設定した場合,最初からプログラムが実行されますが,割り込みのみを設定した場合,割り込み関数ISR(WDT_vect)が呼び出されたあとスリープ命令の次の行からプログラムが再開されます.また,ウォッチドッグタイマの割り込みによるスリープ状態からの復帰後も,ウォッチドッグタイマは設定時間が経過するごとに割り込みを繰り返します.必要なければ以下の手順でタイマを停止します.
  // ウォッチドッグタイマをリセット
  asm("wdr");
  // ウォッチドッグリセットフラグを解除
  MCUSR &= ~(1 << WDRF);
  // ウォッチドッグタイマ設定変更のため,WDCEとWDEに同時に1を出力.
  WDTCSR |= (1 << WDCE)|(1 << WDE);
  // その後4クロック周期内にタイマ停止操作.
  WDTCSR = 0;

ウォッチドッグタイマの設定と合わせて割り込み関数ISR(WDT_vect)を効果的に使うことで,スリープ状態からの復帰の挙動を制御することができます.例えば,割り込み関数が呼び出される度にカウントアップされる変数を用意し,その変数がある数に達するまでは繰り返しスリープ状態に移行させることで,長時間スリープさせておくことができます.

sample01の設定でスリープ時の消費電流を約0.1μAまで抑えることができましたが,ウォッチドッグタイマを使用する場合のスリープ時の消費電流は約6μAとなります(データシート31.8.4参照).


Copyright (c) 2015 Tetsuaki BABA all rights reserved.