Wio LTEの消費電力を抑えるために試行錯誤した結果、
Standbyモードに入れることで10mA未満に抑えることができたので、
そこに至るまでの過程と、Standbyモードの注意点を書いておきます。
きっかけ
Wio LTEにUSB電圧チェッカーを挟んで、消費電力をチェックしてみたところ、
動作中に最大260mA消費していることが分かりました。
また、センサーをSleepさせ、次のデータ送信のタイミングまで待機させる間にも10mA〜170mA常時消費していました。
USB給電ではなく、バッテリー給電を考えていたため、これではすぐに電池が無くなってしまいます。
よく他のマイコンでは、待機中にSleepモードを利用していたため、
Wio LTEでもSleepモードといった省電力モードがあるか調べることになりました。
Wio LTEのライブラリで用意されているSleep関数を使ってみる
Wio LTEのWikiページを見たところ、Sleep関数があったので使ってみました。
# include <WioLTEforArduino.h>
WioLTE Wio;
void setup() {
Wio.Init();
SerialUSB.println("sleep");
Wio.Sleep();
}
void loop() {
}
上記のプログラムを実行すると、Sleep関数に入った後は電流が10mA以下〜50mAほどになります。
プロセッサーのStandbyモードを使う
もっと消費電力を抑えられる方法は無いか探していると、こんな記事を見つけました。
Wio LTEの本気を引き出す(低消費電力編)
プロセッサー(STM32F405)の低電力モードのStandbyモードを使用すると、1mA以下まで消費電力を抑えることができるそうです。
今までの数字が大きかった分「本当か!?」と疑いつつ、参考にしてやってみることにしました。
結果的には、常時10mA未満にまで電流を抑えることができました!
しかし、ここで結構長い時間はまりました……
西暦を下2桁しか取って来なくて混乱する
上記サイトのプログラムを実行してみると、シリアルモニタにはこのような文字列が表示されます。
1919年という謎の西暦が出てきました。
これは、STM32F4xxのRTClockライブラリ内のsetTime関数が原因です。
setTime関数は、1900年1月1日0時0分0秒から今日に至るまでに経過したtime_t型の秒数を、
日付の形式である構造体tm型に直してプロセッサーに時刻を設定する関数です。
しかし、RTClock.cppのsetTime関数の中身をよく見てみると……
何故かyearを100で割ってしまうため、西暦を下2桁しか保持できません。
現在が2019年なので、2019 - 1900 = 119という数字をyearは持っているのですが、
この関数によって下2桁のみ(=19)というデータになってしまい、
1900 + 19 = 1919年という奇妙な西暦が生まれてしまうのです。
更に、曜日wdayも、西暦に影響されてズレが生じ、1919年の当時の曜日が算出されてしまいます。
ひたすら過去の時間に復帰しようとするため、RTCのアラーム機能を使って復帰する方法は私は諦めてしまいました。
「こうしたらできたよ!」という方法があれば、後学のために教えていただけると幸いです。
main関数内に復帰するわけではない
RTClockライブラリに記載してある、setPeriodicWakeup関数で、
待機時間の秒数を指定するようにしました。
先のサイトのStandbyモード部分のコードを抜き出して少し手を加えただけですが、
一応載せておきます。
# include <WioLTEforArduino.h>
# include <RTClock.h>
# include <stdio.h>
# include <time.h>
typedef int IRQn_Type;
# define __NVIC_PRIO_BITS 4
# define __Vendor_SysTickConfig 1
# include <libmaple/libmaple_types.h>
# include <libmaple/usbF4/VCP/core_cm4.h>
# include <libmaple/usbF4/VCP/core_cmInstr.h>
# include <libmaple/pwr.h>
# include <libmaple/bkp.h>
WioLTE Wio;
static RTClock rtc(RTCSEL_LSI);
void setup() {
// put your setup code here, to run once:
Wio.Init();
}
void loop() {
// Standbyモードに入っているか確認
if (PWR_BASE->CSR & (1UL << PWR_CSR_SBF)) {
PWR_BASE->CSR |= (1UL << PWR_CR_CSBF);
}
SerialUSB.println("standby");
enterStandbyMode(60); // 60秒後に復帰する
}
void clearWUF(){
// WakeUp Flagビットをクリア
PWR_BASE->CR |= (1UL << PWR_CR_CWUF);
}
// Standbyモードへ移行
void setBitStandbyMode() {
delay(1000UL);
// PDDS(Power-Down DeepSleep)ビットをセット
PWR_BASE->CR |= (1UL << PWR_CR_PDDS);
// WakeUp Flagビットをクリア
clearWUF();
// WakeUp Flagビットがクリアされるまで待つ
while(PWR_BASE->CSR & (1UL << PWR_CSR_WUF)) {
}
// Cortex-M4にSLEEPDEEPビットマスクをかける
SCB->SCR |= (SCB_SCR_SLEEPDEEP_Msk);
// 割り込み待ち(Standbyモード)
__WFI();
}
void enterStandbyMode(uint16 sleep_sec){
rtc.setPeriodicWakeup(sleep_sec); // 待機時間(秒数)をセット
clearWUF();
setBitStandbyMode(); // Standbyモードに入る
}
しかし、ここでまた問題が発生します。
いつまで経っても、シリアルモニタに復帰後のメッセージが表示されないのです。
しかし、しばらくするとUSB電圧計の電流の値が変化していることに気付きます。
もしや?と思い、setup関数を実行している間にLEDを点けるようにしてみました。
すると、指定した待機時間を経過した後、LEDが光り始めました。
プロセッサーから直接弄った結果、復帰の経路がmain関数内ではなく、
setup関数からやり直す=再起動をする形になりました。
まとめ
かなり長時間データシートと向き合っていましたが、プロセッサーの扱い方を理解する良い機会だったと思います。
新しいデバイスは参考資料も少ないため、既存の資料を大切に読み込むことが大切だと実感しました。