Archive
お久ぶりでございます。

記事を書かな過ぎて、FC2のログインパスワードを失念していたゆってぃです!

今月に入り、話題のWio LTEを買いました。
このモジュールの素晴らしさは言うまでもありませんが、Wio LTE + SORACOMという組み合わせで
誰でも手軽にIoTを楽しめるようになります。

でも複数台買うには高いんだよね…。
seeedさん…!今後のディスカウント、期待しています!
LTEモジュールとアンテナが高いのはわかっていますし、マイコンも豪華だし、すでにかなり頑張ってる価格なのは承知していますけど…でもほらcat.1だし、何とか6000~7000円台で…!(笑)

さて、そんな素晴らしいWio LTEの最大の欠点は、その消費電力にあります。
モバイルバッテリーで長時間動かしたいのに、これじゃあ…と思っていました。
皆さんも、せっかくのIoT機器なのに、バッテリーが数時間や1日2日しか持たないのは物足りないですよね?
僕も物足りないです。

<ゆってぃが作りたいもの>
・ 据え置き型のセンサーデバイス(屋外使用を想定。AC100V電源の確保は不可)
・ 15分に一度、MQTTでサーバにセンサ値を送信する(JSONで100Byte程度)
・ 1日の稼働時間はAM6時~AM0時(1日あたり18時間)
・ 10000mAhのモバイルバッテリーで、1カ月稼働(目標)

少し調べてみるとseeed jpの人のブログで、Wio LTEを低消費電力で動かす手段の紹介をしてくれています。
消費電力の大半はLTEモジュールなので、この方法でもかなりの省電力化がみこまれます。
ただ、測定したところ、非通信時でも85~95mA程度の電流が流れ続けていました。
常時通信と比較すると消費電力は大きく減りましたが、それでも5000mAのモバイルバッテリーでは、約55時間(2日半)の駆動が限界という計算になります。

IoT機器の多くは、数十分(あるいは数時間)毎にちょこっとデータを送るケースが多く、大半は通信をしていない待機状態です。
必然的に、この待機状態の消費電力をいかに減らすかと言うことが、長寿命化のポイントとなります。
(Seeedの人も、マイコンをスリープさせる方法は次のステップで書いてくれるようですが、ゆってぃは待ちきれませんでした)

ラッキーなことに、メインマイコンのSTM32F405には、複数のスリープモードがついています。
まずは下記のドキュメントを参照してください。

STM32F405のマニュアルページ
データシート
リファレンスマニュアル
リファレンスマニュアル(日本語)

「まずは」じゃねーよ!
1700ページあるぞ馬鹿野郎!


ひーーーーーー!!!ごめんなさい!

ですので、掻い摘んでポイントだけ説明します。
かくいう私も、RTCと低消費電力モードの章以外は全く読んでいません。

マニュアルよると、低消費電力モードには

・スリープモード:CPUの電源は切るが、ペリフェラルは動いている。割り込みでwake可能。
・ストップモード:SRAMの状態を保持したまま、マイコンを停止するモード。発振子も止める。
・スタンバイモード:SRAM保持もせず、マイコンを停止するモード。内部レギュレータ(1.2V系)を完全にOFFする。

の3種類があり、スタンバイモードが最も低消費電力のようです。
なんか語感的にはストップモードが一番低消費電力モードな気がしますが、細かいことは気にしないでいきましょう

リファレンスマニュアルに詳しい説明がありますが、この機能自体はCortex-M4のdeepsleepモードをベースとしています。
それに加え、バックアップ系以外のペリフェラル電源および発振部を、すべてOFFにしている訳ですね。
この時、RTCのレジスタ部はOFFされないため、こことバックアップSRAMだけは生き続けるという仕組みです。

スタンバイモードからの復帰方法は
①外部リセット
②内部ウォッチドッグリセット
③WKUPピンの立ち上がりエッジ
④RTCイベントの発生
の4種類です。

復帰時に外部から信号が入ることは想定していないので、必然的に②か④ですが、④が妥当でしょう。

スタンバイモードへの入り方

無駄話導入部が終わったので、さっそくスタンバイモードへの入り方を説明します。
リファレンスマニュアルによると、スタンバイモードへは下記の方法で入ります。

次の条件下での WFI(Wait for Interrupt)または WFE(Wait for Event) – Cortex™-M4 with FPU システム制御レジスタの SLEEPDEEP ビットをセット – 電源制御レジスタ(PWR_CR)の PDDS ビットをセット – 電源制御/ステータスレジスタ(PWR_CSR)の WUF ビットをクリア – 選択されたウェイクアップソース(RTC アラーム A、RTC アラーム B、RTC ウェ イクアップ、タンパ、タイムスタンプフラグ)に対応する RTC フラグをクリア

ただし、マニュアルには「安全なウェイクアップフラグクリアシーケンス 」として、下記シーケンスが推奨されています。

PWR ウェイクアップフラグ(WUTF)がクリアされる前に選択された RTC 代替機能がセットされた 場合、検出は立ち上がりエッジで一度だけ行われるため、次のイベントで検出されることはありませ ん。
RTC 代替機能がマッピングされているピンでの検出ミスを避け、STOP モードおよび STANDBY モー ドを正しく終了するには、STANDBY モードに入る前に以下の手順に従うことを推奨します
RTC アラームを使用してデバイスを低電力モードからウェイクアップさせる場合は、次の設定 が必要です。
 a) RTC アラーム割り込みを無効にします(RTC_CR レジスタの ALRAIE または ALRBIE ビット)。
 b) RTC アラーム(ALRAF/ALRBF)フラグをクリアします。
 c) PWR ウェイクアップ(WUF)フラグをクリアします。
 d) RTC アラーム割り込みを有効にします。
 e) 再び低電力モードに移行します。


まぁ、当たり前のことを言っているだけですが、多くの割り込み系の不具合というのはこういうイージーな所から発生し、原因がわからず猛烈な残業を何日も強いられるハメになる訳です。


RTCの設定

スタンバイモードからの復帰には、RTCイベントを使用します。
STM32F4シリーズの内蔵RTCは、以下のマスク可能なRTCを有しています。

– アラーム A
– アラーム B
– ウェイクアップ割り込み
– タイムスタンプ
– タンパ検出

今回は、日中は15j分毎に起動を繰り返し、深夜は翌朝まで再起動しないというものが作りたいので、アラーム機能を使いましょう。
これは、指定した時間になるとRTCのアラーム割り込みが発生するという、名前の通りなんの捻りもない機能です。
RTC関連機能は、RTClockというライブラリが提供されているので、これを使えば簡単に制御できます。

サンプル
以上を踏まえて、低消費電力モードへの入り方を説明します。
なお、僕が作成中のコード全部をここに乗せるのは難しいので、全体はGitHubに乗せました。
そっちは少しクラスが多くてわかりづらいので、ここではポイントだけお話しします。
下記コードのEnergyManager::EnterStandbyMode()は、低消費電力モードに入るための自作メソッドです。
なお、引数は次回起動時の時刻です。

ここで、変数のrtc_はライブラリにあるRTClockクラスのインスタンス、pwr_ctrl_は自作のPowerCtrlクラスのインスタンスです。


--- EnergyManager.cpp 抜粋 ---

void EnergyManager::EnterStandbyMode(time_t wakeup_time) {
struct tm dbg = *gmtime(&wakeup_time);

// see STM32F405xx Reference manual

// アラームA割り込みを禁止
rtc_->turnOffAlarmA();

// アラームA割り込みフラグをクリア
rtc_enter_config_mode();
RTC_BASE->ISR &= ~(1 << RTC_ISR_ALRAF_BIT);
rtc_exit_config_mode();
*bb_perip(&EXTI_BASE->PR, EXTI_RTC_ALARM_BIT) = 1;

// WUFクリア
pwr_ctrl_->ClearWUF();

// 次回起動時間をセット
rtc_->setAlarmATime(wakeup_time);

// 次回起動時間の表示
SerialUSB.print("next wake up time: "); // for debug
SerialUSB.println(asctime(&dbg)); // for debug
delay(100); // for debug

// スタンバイモードへ遷移
pwr_ctrl_->EnterStandbyMode(); // enter standby mode
}



--- PowerCtrl.cpp 抜粋 ---

// UWFをクリアする
void PowerCtrl::ClearWUF() {
// clear WUF bit
PWR_BASE->CR |= (1UL << PWR_CR_CWUF);
}

// Standbyモードへ遷移
void PowerCtrl::EnterStandbyMode() {

// set PDDS bit
PWR_BASE->CR |= (1UL << PWR_CR_PDDS);

// clear WUF bit
ClearWUF();

// WUPがクリアされるまで待つ
while(PWR_BASE->CSR & (1UL << PWR_CSR_WUF)) {
}

// set sleep deep bit
SCB->SCR |= (SCB_SCR_SLEEPDEEP_Msk);

// 割り込み待ち(Standbyモード)
__WFI();
}


上記処理にてスタンバイモードに入れると、待機時の電流は5Vで40~50mAです。
実装前のおおよそ半分といったところですが、まだ結構食いますね…。
寿命が倍になったとはいえ、そもそも2~3日の倍ですから、まだまだ実用途には耐えられません。

個人的には、非通信時は1mAとかまで持っていきたいけど、外付けの電源回路作らないと難しいかな。。。
外付けで低消費電力(マイクロアンペアオーダー)のRTC ICを付けて、アラーム出力でMOS FETかませて電源制御して…ってすれば、リーク含めても1mA切れるかな。
でも、大きくなっちゃうのは嫌だし…悩ましいです。

注意点

ソフトを書いていてわかったのですが、
libmaple/pwr.h
にはインクルードガードがついておりません。
ほかのヘッダにはついているので、単なるつけ忘れか、それとも何か意味があるのかはわかりません。
ただ、ゆってぃは深く考えずにインクルードガードを付けました。
ビルドされる際はご注意ください。

以上です。
長くなってしまいましたが、最後までお読みいただいて、ありがとうございました!!
関連記事
スポンサーサイト
プロフィール

ゆってぃ

Author:ゆってぃ
経歴8年の組み込み系エンジニアです。
ソフトウェア開発経験ゼロの状態から、なんとか実務がこなせるようになってきた現在に至るまでの経験を、備忘録代わりに綴っていきたいと思います。
入門者の方、大歓迎!
(上級者の方、ごめんなさい…)

<守備範囲>
言語:C/C++(C++03/C++11)、一部アセンブラ
マイコン及びSoC:PIC、SH、FM3、R-Car
PoC基板:Arduino、RaspberryPi、Wio LTE
OS:OSレス、ITRON、Linux(Yocto)
サーバ技術:Docker、nginx
プロトコル:MQTT、DMX
その他:CAN、J1939

あと、ブログには全然関係ないですが、Bumpy Headというバンドのギターをやっています。
ライブ情報なんかも書いたりすることがあるので、その時に「行ってもいいよ~」といった感じのコメントを戴けると、泣いて喜びます(泣)
ブログ読んでくださってる方なら、チケット代サービスしちゃいます!

最後に…滅多に流用することは無いでしょうが、このブログに書かれているソースは、特に指定の無い限りMITライセンスとします。ただし、一部それ以外のものもございますのでご注意下さい。
※ブログのリンク先にあるコードに関しては、リンク先のポリシーに従ってください。

最新記事
最新コメント
カテゴリ
RSSリンクの表示
メールフォーム

名前:
メール:
件名:
本文:

twitter
リンク
ブロとも申請フォーム