google-site-verification: google3bd66dd162ef54c7.html

消費電流が激減! delayWDT関数を作ってみた

 引き続きArduinoの話題です。

 前回の記事ではdelay関数を動かす時に SLEEP_MODE_IDLE に入れて消費電流を減らしました。これは手軽に出来るのですが、消費電力が半分になる程度の効果しかありません。

 そこで、思いっきり深いスリープである SLEEP_MODE_PWR_DOWN に入れて、消費電流を減らしてみます。

▼測定の様子
CPUの消費電流測定
 CPUの消費電流を直列に入れた1Ωのシャント抵抗で測定します。

 こうやってCPUの消費電流を正確に測定できるようにしてプログラムを仕上げていきます。元のプログラムは、電池で動くアナログ気圧計を作った時のもので、これを整理して delayWDT という関数に仕上げました。 

 完成した、delayWDTの動作確認デモ用のスケッチ ← 旧版(バグあり:ADCがONにならない)
 完成した、delayWDTの動作確認デモ用のスケッチ ← 新版(バグ修正済み)

 このプログラムの中の delayWDT( ) という関数を呼ぶことで SLEEP_MODE_PWR_DOWN モードに入った状態のディレイを実行します。

 この関数の引数に0から9までの値を入れることで、16msから8secまでの10種類のディレイが得られます。ディレイ時間は飛び飛びの値しか指定出来ないので、欲しい値が無い場合は、ループなどを使って自分で調整する必要があります。
 
 この関数ではパワーダウンモードでスリープさせたCPUを、ウォッチドッグタイマー(WDT)割り込みで再起動させています。関数名のdelayWDTはそういう由来で命名しました。

 delayの時間はウオッチドッグタイマー用のオシレーターで作られています。このオシレーターはCPUチップ内のCR発振器(周波数は約128kHz)で動いているので精度はあまり良くありません。でも小さな回路が低い周波数で動いているのでものすごく小さな電流で動かすことが出来ますきます。これがウォッチドッグタイマー割込みを使ったスリープが消費電流が少ない理由です。

 なお、最初は delayDeepSleep なんてかっこいい名前で引数にミリセカンド単位の値を直接指定することも考えたのですが、プログラムがややこしくなるので止めました。実は妥協して16msくらいの粒度で動かすことも試したのですが、消費電流がやや多くなってしまうので止めました。

 で、この delayWDT関数の効果ですが、

▼普通のdelay関数の消費電流
通常の消費電流
 消費電流は16.264mA。16MHz動作のAtmega328Pとしては普通の値です。

▼delayWDT関数の消費電流
delayWDTの消費電流
 消費電流はたった28μA。

 効果は圧倒的です。ArduinoのCPUを単体で電池駆動する時に役立つと思います。

 但し、この関数ではCPUのクロックを完全に止めて深いスリープ状態に入れるので、タイマーなどの機能は停止します。タイマーの中断が許されない用途ではこの関数は使えません。他にも注意が必要な点がいろいろありますが、使い方によって変わってくるので全部書くのは無理です。

 なお、この関数のベースとなったアイディアはArduino Sleep_Watchdog_Batteryからいただきました。

 また、こうしたらもっと消費電力が減らせたよ、という情報があればコメントで教えていただくと嬉しいです。

【2016年4月15日追記】
本文中に「注意事項はいろいろある」と書いておいたのですが、下記は特に勘違いしやすいのでご注意ください。
1) この関数で消費電力が減るのはCPUのチップ(UNO なら ATmega328P)だけです。Arduino UNO の基板にはCPU以外にUSBシリアル変換チップなどが同居していて結構な電流を消費しているのですが、これらの同居人の消費電流を減らすことは出来ません。つまり、この関数を使っても、UNOなどのボードの状態では劇的に消費電流を減らすことは出来ません。本気で省エネ運転したい場合は、CPUのチップだけで単独動作させる必要があります。

2) delayWDT()関数を呼んだ瞬間にCPUのほとんどの機能は停止します。タイマーはもちろんですが、シリアルへのデーター出力も強制的に中断されます。つまり、もしシリアル通信を行っている最中にこの関数を呼び出すと文字化けします。これを防ぐにはdelay()などでちょっと待つ、あるいはSerial.flush関数で送信完了まで待つ必要があります。

【随時追記】
1) この関数が使えるCPUは Arduino UNOなどに使われている ATmega328Pだけです。アーキが同じmega8シリーズのCPUならたぶん大丈夫だと思いますが、やったことがないのでどういう動作になるか判りません。

 また、ATmega328以外のCPUにこの関数を使うと最悪の場合、ヒューズビットの書き換えなどの手段を講じないとチップが使えなくなることがあるのでご注意ください。
関連記事

tag : スケッチ 省エネ

コメントの投稿

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

ADCの電源ONのコードについて

Arduinoを省電力化できないかと事例を探していたらこちらの記事を見つけました。
たいへん参考になりました。
ありがとうございました。

ところで1点、私はADCを使っているのですが
こちらのコードではスリープ復帰後ADCが使えなかったため、
以下の修正をすることでADCが使えるようになりました。

修正前
ADCSRA != (1 << ADEN); // ADCの電源をON

修正後
ADCSRA |= (1 << ADEN); // ADCの電源をON

おそらくADCパワービットを立てる処理をさせていると思うので
修正後で問題ないとは思っています。

ADCの電源ONのコードについて

す〜さん、今晩は。

ご指摘ありがとうございます。確かにコード間違ってます。
ビットを立てなくてはいけないのに、何やってんだか・・、でした。

早速修正版を記事の方に追記しておきました。

analogWriteで困っています

こんばんは。教えていただきたいことが有ります。
いまソーラーガーデンライトの改造で点灯制御を
arduinoで作っているのですが、こちらのプログラムが凄い省エネなので、使用したいのですが、何故か単にanalogWriteでLEDをpwm調光させようとしているのですが、上手く行きません。digitalWriteで点灯させると問題なくLEDが点灯します。analogWriteだと規則があるのか分かりませんが、不定期に点滅してしまいます。この、「delayWDT」の使用はanalogWritewでは無理なのでしょうか?

re:analogWriteで困っています

hiroさん、今晩は。

この記事の内容を試されているんですね。Arduinoの使い方としては初心者のレベルを完全に脱していると思います。

それで、この記事のdelayWDT関数とanalogWriteの併用ですが、結論を先に言うとうまくいかないです。

analogWriteはPWMで確か500Hzくらいの速度でポートのON/OFFを繰り返して、そのデユーティ比、つまり波形の平均値でアナログの値を表現します。500HzでOn/OFF繰り返すためにはCPUを止めてはいけません。
一方で、このdelyWDT関数はCPUを完全に止めてしまうのでCPUが止まった間はPWMは出来ないので、analogWriteはうまく動かなくなります。たぶんその時のタイミングによってONかOFFいずれかの状態が出力されると思います。

とはいっても、CPUの消費電流を出来るだけ減らしてAnalogWriteを使いたいということでしょうから、手は無くは無いと思います。

ソフトだけでやるなら、
0.5ms周期で割込みかけて擬似的なPWMさせる。
あるいは、ハードをいじってCPUのクロックを下げて省エネ化とか。

このあたりになると私は経験が無いのでアドバイス不能です。

ということですみません。お役に立てそうにないです。

何かほかに情報がありましたら書き込んでいただければ、と思います

納得しました。

丁寧な説明ありがとうございます。そのような事だったんですね。
arduinoの内部動作の理解ができてないので、このような所で躓くんですよね。しかし、「delayWDT」は本当に素晴らしい省エネです。CPUクロック8MHzの内部動作にしていたのですが、それでも比べると天と地の差です。
実は、計算上よりソーラーライトの点灯時間が短く、LEDの抵抗の選択がうまく行かなくて、、ハンダ付けもしてしまったので、analogWriteで消費電流を自由に調整が出来れば良いなとの理由でした。

まだ、割り込みなどわからない状態なので、勉強してみます。この先、必ず必要になる機能ですよね。
たくさんたくさん、疑問だらけでいっぱい聞きたい事だらけです。いつもこんな感じで、電気・プログラムに強いラジオペンチさんを、羨ましく尊敬してしまいます。

はじめまして

Sekitakovichと申します。Arduinoでちょっとした携帯機器を作るべく、LiPoで長時間動作の試験をしていたのですが、sleep_modeがどうやっても思い通りにならず困っていてこの記事にたどり着きました。感動です! 早速使わせて頂きます。ありがとうございました。

re:はじめまして

Sekitakovichさん今晩は、お役に立てたなら嬉しいです。

そちらのブログ拝見しました。
http://sekitakovich.klabo.co.jp/

面白そうなことやられてますね。時々巡回させていただきますのでよろしくお願いいたします。

お恥ずかしい限りです(ぽ)

Pro Trinket3V-12MHzでも動作確認できました。なるほどこれは驚異的ですね。SLEEP_MODE_PWR_SAVEとすることでMsTimer2と併用可能か、これから試してみようと思っています。どうか今後ともよろしくお願いいたします。

# ちなみに私も毎朝、若葉台駅を通過して上京しています

Re:お恥ずかしい限りです(ぽ)

Sekitakovichさん今晩は。

時間精度が必要な場合はこの関数は使えないので、CPUのクロック精度で動くタイマー割り込みを使うのがいいでしょうね。ただ消費電流がどれくらいになるのか興味があります。

あるいは外部のRTCなどからハード割り込み掛ける手がありますが、大げさすぎますね。

若葉台、了解です。私の生活圏内です

大げさになりそうです

おかげ様でdelayWDT、大活躍して貰っています。が、その後今度はMsTimer2とi2Cの併用でハマりました。ここはどうやら「RTCを外付けしてINTピンでタイマー割り込みを拾う」ことになりそうです。そこでRTCの使いこなしについて調べていたら、またこちらのサイトにたどり着きました(笑)。これからもどうかよろしくお願いいたします。

re:大げさになりそうです

Sekitakovichさん、今晩は。

RTCを定周期の割り込みだけに使うのはもったいないので、たぶん時計としても使うことになりそうな予感が、w

そちらのブログ拝見しました。私、Timer1はグリッドタイインバーターを作る時に使いましたが、液晶表示とかやっている裏で正確なタイミングでハードを動かせるので個人的には気に入ってます。

でもWireライブラリと共存できるかは判りません。

WireとTimerOneの併用はやはりダメでした

ところで、atmega328u4でdelayWDTを使ってみたところsleep_mode()でハングしてしまいました。どうやらSLEEP_MODE_IDLE(とADC)以外全部こうなります。この件、何か心当たりはありますでしょうか?

re:WireとTimerOneの併用はやはりダメでした

併用は難しいんですね。

それと、ATmega32U4は使ったことが無いのでよく判らないです。というか、ATmega328P以外使ったことがありません。

ATmega32U4のデーターシートをちょっと見ましたが、WDTまわりのレジスタはATmega328とほとんど変わらないみたいです。
それに、プログラムも値の決め打ちではなく定義ファイルから値を持って来ているので、多少は互換性を考慮した作りになっています。

でもUSBの部分とかあるので、CPUクロックを止めると変なことが起きるんでしょうか。ということで、さっぱり判りません。

ループ処理について

arduinoの省電力を調べておりブログ主様のページにたどり着きました。
arduinoを使い始めたばかりで、まだ深く理解出来ていないのですが。。。

ブログ内の
```
この関数の引数に0から9までの値を入れることで、16msから8secまでのディレイが得られます。ディレイ時間は飛び飛びの値にしか設定できないので、欲しい値が無い場合は、ループなどを使って自分で調整する必要があります。
```

との事なのでforを使い対応しようかと考えておりますが考えあっておりますでしょうか?
delayWDT(7); = 2秒なので単純に10回まわすと20秒のdelayになるのかなーと。


実際書き込んで動いているのですが、どうなんだろうと。。。
```
void loop() {
digitalWrite(led, LOW); // LED off
for (int i=0; i<10; i++){
// 10回繰り返す。 2秒*10 = 20秒
delayWDT(7);
}
//delayWDT(7); // 引数は関数の資料参照
digitalWrite(led, HIGH); // LED on
delay(2000); // 普通のdelay(比較用)
// delayWDT(7);
}
```

一時間に一回の測定を行いたいのであれば単純にですが"delayWDT(9); = 8秒"を用いて450回をforで回すのかなーと。

re:ループ処理について

katoさん、おはようございます。使い方はその通りで大丈夫です。

この関数の使い方を誤解されている方が多いので、注意事項を本文に追記しておきました。ご承知かも知れませんが、念のためにそちらも一読下さい。

No title

いつも拝見しております。
delayWDT使わせていただきました。
ありがとうございます。

edyさん、おはようございます

小型I2C接続 128x64 OLEDディスプレイ、、の記事ですね、拝見してます。

delayWDT関数を使っていただいて嬉しいです。

delayWDT中のシリアル通信について

delayWDTの関数をありがたく使わせて頂いてます。
ATmega328Pを使い、SoftwareSerialでGPSのユニットを制御しています。delayWDTでスリープさせた際に、GPSユニットのシリアル通信があると強制的にスリープが解除されてしまいます。何か良い方法をご存知でしたら教えていただけますか?

re:delayWDT中のシリアル通信について

houさん、レポートありがとうございます。

申し訳ありませんが、お尋ねの現象の原因は、ちょっと心当たりがありません。 あと、すぐに調べる時間も無さそうなので、対策をお示しすることは難しそうです。


ソフトウェアシリアルを使ったことが無いもので想像ですが、ピンチェンジ割り込みが発生してスリープが解除されている気がします。でも自信はありません。

簡単な対策としては、Seria.end(); とやって通信を止めてしまう手もあると思いますが、どうでしょう。すでに試されたかも知れませんが。

上手くいきました

早速の返信ありがとうごいます。Seria.end();で解決しました!確認不足でした。
アドバイスのおかげでとても助かりました。ありがとうございます。

re:上手くいきました

houさん連絡ありがとうございます。

なるほど、ソフトウェアシリアルの割り込みでスリープがキャンセルされていたんですね。勉強になりました。
カレンダー
09 | 2017/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コード