google-site-verification: google3bd66dd162ef54c7.html
FC2ブログ

ArduinoのCPUをスリープさせて消費電流を減らす関数 delayWDT2

 Arduinoのタイマーで、CPUを深いスリープ状態に入れて消費電流を減らすdelayWDT という関数を以前作りました。この関数を使うと、スリープ状態の消費電流を27μAまで減らすことが出来るので今でも便利に使っています。

 Arduinoではこれくらいが限界と思っていたのですが、もっと消費電流を減らすことが出来るという記事を見つけました。それは、
 tetsuaki baba さんのWebの、workshop/ArduinoBasis/sleep Arduino のSleep機能 という記事です。

 その記事には、ブラウンアウト検出(BOD)機能を止めることで、もっと省電流化する方法が書かれていて、何と 6.5μA まで減らすことが出来るそうです。なるほど、そういう手があったのかと感心しました。

 これはものすごい効果なので、delayWDT 関数にこの方法を組み込むことにしました。

 まずは仕組みのおさらいです。

▼ATmega328Pのスリープ機能一覧表(データーシートから抜粋)
ATmega328Pのスリープ機能一覧
 この表はCPUのスリープ機能の種類を表したものです。delayWDT関数では、いちばん消費電流が少なくなるパワーダウンモードを使っています。表の赤い横線部です。

 この表の右端にソフトウエアBOD禁止と言う欄がありますが、実は私、この意味が理解出来ていませんでした。調べてみると、ソフトウエアBOD禁止と言うのは、「ヒューズビットでBODを使うことが指定されていても、スリープ中はこの機能を解除するように、プログラムから指定出来ます」 という機能でした。

 深いスリープに入れた場合、クロックは止まっているので何も動いていません。ということは、たとえBODを検出したとしても、すぐに何かのアクションを起こさなくてもいいはずです。別の言い方をすると、クロック停止は安全側、つまりフェールセーフ設計にするのが定石なので、BOD状態になったとしてもすぐに何かしなくても大丈夫なずです。

 だったら、スリープ中はBODを止めても大丈夫。それに、BODを動かすためには20μAの電流が必要ですが、これがもったいない、ということなんだと思います。

 ちなみに、ソフトウエアBOD 禁止機能は ATmega328P など末尾(など)に P が入っている CPU に入っているようです。P は消費電流がピコアンペアオーダーになる、ということから付けられた記号だったと思いますが、なるほど、BODを有効にしていたのでは pA オーダーの消費電流にはなりません。ちなみに、ウォッチドッグタイマー (WDT) まで止めれば、消費電流はピコアンペアの領域に入るはずです。

▼ソフトウエアBOD禁止設定レジスタ
BOD設定
 MCUCR レジスタから操作するようです。但し、誤って設定されることを防止するためと思われますが、設定後 3クロック以内にスリープに入れないといけません。この条件を C 言語だけでクリアするのは難しいので、アセンブラ命令を併用する必要があるようです。

 具体的なやり方は、上記の tetsuaki baba さんのサイトで解説されていますが、その内容を delayWDT 関数に組み込んで使い易くしちゃいましょう、というのが今回の記事の目的です。

 なお、従来の関数 (delayWDT) との関係がややこしくなるので、新しい関数は、delayWDT2 という名前にしました。

 前置きが長くなりましたが、プログラムは以下の通りです。
/*
delayWDT2関数のデモプログラム。
パワーダウン中のCPUの消費電流は約6.5μA(Atmega328P@16MHz,5V)
このプログラムはdelayWDT関数にスリープ中はブラウンアウトを禁止することで
消費電流を更に下げたものです。勝手に使っていただいてかまいませんが、無保証です。
BODを無効化するための設定は下記を参考にさせていただきました。ありがとうございます
http://tetsuakibaba.jp/index.php?page=workshop/ArduinoBasis/sleep
fileName:_20180418_dalayWDT2.ino
2018/04/18 ラジオペンチ http://radiopench.blog96.fc2.com/
*/

#include <avr/sleep.h> // スリープライブラリ
#include <avr/wdt.h> // ウォッチドッグタイマー ライブラリ

int led = 13; // LEDピン
//volatile int wdt_cycle = 0; // 必要ならコメントアウトを削除

void setup() {
pinMode(led, OUTPUT);
}

void loop() {
digitalWrite(led, LOW); // LED off
// 低電流でスリープ
delayWDT2(7); // 引数でスリープ時間指定(詳細はdelayWDT_setup参照)
// 通常モードに復帰
digitalWrite(led, HIGH); // LED on
delay(2000); // 普通のdelay(比較用)
// delayWDT2(7);
}

// ここから下を全て使う
void delayWDT2(unsigned long t) { // パワーダウンモードでdelayを実行
Serial.flush(); // シリアルバッファが空になるまで待つ
delayWDT_setup(t); // ウォッチドッグタイマー割り込み条件設定

// ADCを停止(消費電流 147→27μA)
ADCSRA &= ~(1 << ADEN);

set_sleep_mode(SLEEP_MODE_PWR_DOWN); // パワーダウンモード指定
sleep_enable();

// BODを停止(消費電流 27→6.5μA)
MCUCR |= (1 << BODSE) | (1 << BODS); // MCUCRのBODSとBODSEに1をセット
MCUCR = (MCUCR & ~(1 << BODSE)) | (1 << BODS); // すぐに(4クロック以内)BODSSEを0, BODSを1に設定

asm("sleep"); // 3クロック以内にスリープ sleep_mode();では間に合わなかった

sleep_disable(); // WDTがタイムアップでここから動作再開
ADCSRA |= (1 << ADEN); // ADCの電源をON(BODはハードウエアで自動再開される)
}

void delayWDT_setup(unsigned int ii) { // ウォッチドッグタイマーをセット。
// 引数はWDTCSRにセットするWDP0-WDP3の値。設定値と動作時間は概略下記
// 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
// 6=1sec, 7=2sec, 8=4sec, 9=8sec
byte bb;
if (ii > 9 ) { // 変な値を排除
ii = 9;
}
bb = ii & 7; // 下位3ビットをbbに
if (ii > 7) { // 7以上(7.8,9)なら
bb |= (1 << 5); // bbの5ビット目(WDP3)を1にする
}
bb |= ( 1 << WDCE );

MCUSR &= ~(1 << WDRF); // MCU Status Reg. Watchdog Reset Flag ->0
// start timed sequence
WDTCSR |= (1 << WDCE) | (1 << WDE); // ウォッチドッグ変更許可(WDCEは4サイクルで自動リセット)
// set new watchdog timeout value
WDTCSR = bb; // 制御レジスタを設定
WDTCSR |= _BV(WDIE);
}

ISR(WDT_vect) { // WDTがタイムアップした時に実行される処理
// wdt_cycle++; // 必要ならコメントアウトを外す
}
 delayWDT2 関数を呼び出すサンプルプログラムの形になっています。Loop()の部分はLチカと同じ作りなので、説明は不要でしょう。

 何かに組み込む時は、ライブラリをインクルードしておいて、// ここから下を全て使う、以降をコピペしておけば、あとは delayWDT2(8); などと呼ぶだけで動くはずです。

 ポイントになるのは43から47行で、ここでBODを停止する設定を行った後で素早くスリープに入れています。なお、スリープから復帰する時に BOD は自動的に再起動するので、プログラムは何もしなくても良いようです。

 プログラムがうまく動いているか確認するために、デジタルマルチメーターで CPU の電源電流を測定しました。

▼CPU の消費電流測定
ArduinoのCPUの電流測定
 以前作った、ATmega328P の消費電流測定用のゲタを使って電流を測りました。このゲタには電源電流測定用の1Ωのシャント抵抗が入っているのですが、流れる電流が小さいので分解能が不足します。ということでシャント抵抗は使わず、DMMの電流レンジ (分解能10nA) で電流を直接測定しました。なお、使った基板はArduino UNO R3 です。

 以下、スリープ時の消費電流の値を見て行きます。

▼以前の delayWDT の場合
27μA
 以前作ったdelayWDT 関数では、スリープ中の消費電流は27.4μAでした。普通に動いている時は、3桁くらい上の16mAは流れるので、ものすごい省エネ効果ではあります。

▼今回作ったdelayWDT2の場合
6.5μA
 delayWDT2 関数を使うと消費電流は6.57μAまで減りました。delayWDT と比べると 1/4 以下の消費電流になっています。ここまで減ると、Arduino をコイン電池で動かすことも視野に入ってきそうです。

◆まとめ
 昔 delayWDT 関数を作った時の話ですが、CPUのデーターシートではもっと消費電流が減らせそうな感じなのですが、あと一歩追い込めなくて、妥協しました。そんなことで、ちょっと気になっていたことが、解決出来て良かったです。これからはdelayWDT2 を使って行こうと思います。また、貴重な情報を公開していただいた tetsuaki baba さんに感謝します。

◆ご注意
 この関数を使う場合、色々な注意事項があります。そのあたりは、冒頭に紹介した delayWDT関数の記事の末尾に書いておきましたので、一読して頂くと幸いです。
 なお、今回の関数はATmega328P用で、ATmega328では動かないはずなのでご注意下さい。
関連記事

コメントの投稿

管理者にだけ表示を許可する

カレンダー
09 | 2018/10 | 11
- 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 - - -
プロフィール

ラジオペンチ

Author:ラジオペンチ
電子工作を中心としたブログです。たまに近所(東京都稲城市)の話題など。60過ぎて視力や器用さの衰えを感じつつ日々挑戦!
コメントを入れる時にメールアドレスの記入は不要です。なお、非公開コメントは受け付けていません。

記事が気に入ったらクリックを!
最新記事
カテゴリ
最新コメント
リンク
FC2カウンター
検索フォーム
月別アーカイブ
RSSリンクの表示
QRコード
QRコード