低消費電力設計向けArduino Guide

Arduinoハードウェアとソフトウェアを使った低消費電力設計の基本を学びます。


AUTHOR: Taddy Chung, José Bagur、LAST REVISION: 2022/10/05 22:00


低消費電力の目的は、デバイスのふるまいを制御し、消費電力を削減することで、動作時間を長くすることです。家庭用電源のような電源から直接給電される電子機器は、通常、動作時間を延ばすために低消費電力や類似の技術を実装することは要求されません。一方、バッテリーのような電源で動作するデバイスの動作時間を延ばすには、消費電力を削減することが必要になります。

低消費電力システムを実現するこのガイドは、全てのArduinoボードに適用することができます。例えば、Arm Cortex-M0 32-bit SAMD21プロセッサベースのArduinoボードは、低消費電力の機能を活用できます。Murata CMWX1ZZABZを搭載したMKR WAN 1310のような、LoRaWAN®ネットワーク技術に基づく無線プロトコルを実現するArduino SAMD21ボードは、低消費電力技術を活用して長時間動作させることができます。電源ガイドや自己放電率設計などの高度な技術は、高電力効率システム設計をするために、全てのArduinoボードに適用できます。Arduinoボードに関する情報は、Arduino Documentation Hardwareを参照してください。

低電力ライブラリ

Arduinoボードで低電力機能を有効にするには、Arduino IDEのライブラリマネージャ―からArduino Low Powerライブラリをダウンロードします。このライブラリは、MKRファミリーボードで低電力機能を有効化します。

  • ライブラリを管理するには、Arduino IDEで、「スケッチ」→「ライブラリをインクルード」→「ライブラリを管理」を選び、“Arduino Low Power"を検索し、最新バージョンを選びます。
  • ボードコンポーネントを更新するには、「ツール」→「ボード」→「ボードマネージャ」を選び、関連するArduinoボードを検索します。

以下のリンクから、異なるArduino IDEバージョンをダウンロードできます。

Arduino IDEについてさらに学ぶには、以下のリンクに従ってください。

低電力設計技術

マイクロコントローラの電力消費量を削減するには、いくつかの選択肢があります。

  • スリープモード
  • 外部イベント
  • ADC
  • スタンバイ

スリープ技術

低電力機能を有効にする最善の方法は、プロセッサをスリープ状態にすることです。ディープスリープモードは、デバイスの様々な内部モジュールの電源を切ることで、ほとんどの電力消費を抑えます。ディープスリープモードはタイマーを設定でき、設定した時間が経過した後、起動します。さらに、ライトスリープモードというのものあります。このモードでは、必要なタスクを起動したままにするために、いくつかの内部モジュールの電源を入れたままにします。通常、ほとんどの消費電力を節約するためにディープスリープモードが適用されます。一方、ライトスリープモードは、再起動したときに外部モジュールの状態に追従するために、いくつかの設定を起動したままにするときに役立ちます。

スリープモードでは、マイクロコントローラーを低電力モードにする際に、スタンバイモードとアイドルモードという異なるレベルの設定があります。デバイスの要件により、スリープモードでも、いくつかのコンポーネントやモジュールは動作し続ける必要があります。SAMD21ベースのボードでは、スタンバイモードが最も消費電力の少ないモードです。これはマイクロコントローラーの全てのクロックソースを停止し、電圧レギュレータを低電力状態に設定します。発振器は、停止(stop)、実行(run)、周辺機器からの要求による実行(run on behalf of peripheral request)、の3種類の状態のいずれかになります。その後、WFI(Wait For Interrupt)が有効な間、デバイスはディープスリープモードになります。割り込みやWDT(Watchdog Timer: ウォッチドッグタイマー)が、デバイスをスリープモードから復帰させます。

アイドルモードもスタンバイモードと同様です。アイドルモードでは、WFI(Wait For Interrupt)が有効な時でも、周辺機器は起動したままです。しかし、デバイスはスリープモードではありません。つまり、スタンバイモードよりは消費電力は多くなります。一方、クロックソースは、起動したままにするのか、消費電力を下げるために停止するのかは、ソフトウェア設計者次第です。このモードでも同様に、割り込みやWDT(Watchdog Timer: ウォッチドッグタイマー)が、デバイスをスリープモードから復帰させます。

スリープ状態から復帰させる外部イベント

アプリケーションは、スケジュールされたタスクの完了後、常にスリープ状態を維持するわけではなく、トリガーに反応するための必要なタスクを処理するために、環境の変化を認識する必要があります。このように設計条件を満たした場合はいつも、外部イベントはマイクロプロセッサがスリープ状態から復帰するための要因です。これらの外部イベントは、通常、ADC(Analog to Digital Converter)や、UARTなどの周辺機器、利用可能なI/O(Input and Output)ポートにより発出されます。

このため、信号の変化を認識した場合はいつも、マイクロプロセッサーは起動し、設計されたタスクの初期化に進みます。例えば、振動の強さや、低レベル、高レベル、空中の気体成分などの定量化したリソースの検出があげられます。

ADC(Analog to Digital Converter)により引き起こされる起動

起動のためにADCによって引き起こされたレスポンスは、外部イベントによる起動手順の一部ととらえられます。ボードのアナログピンを通じて電圧の変化の認識に基づき引き起こされるからです。電圧レベルの変化は多くの異なる意味に解釈されますが、通常は、アナログピンを通じて接続した何らかのデバイスの電源が切れ、電圧が下限の閾値を下回ったと理解されます。

一方で、接続されているデバイスの電源が入ると、電圧が上限の閾値を上回ります。必要なタスクを実現しつつ長時間の電池の寿命を監理するために、起動信号としてこの振る舞いの使い方を決めることは、ソフトウェア開発者の仕事です。

電源ガイドと自己放電率

電源ガイドは、Arduinoボードを制御して消費電力を節約する以外に、電源自身に自己放電率があることを理解することが重要です。電源自身がデバイスの低電流以上に自己放電してしまうと、これらの技術を使って電力消費を節約することに意味はなくなります。

電池は、デバイスの電源を入れるので、電源として用いられます。異なる容量や一律ではない自己放電率を持つ様々な電池があります。最初は速く自己放電します。これは通常熱により影響されるもので、利用環境により異なる振る舞いをします。これはガイドとして考える必要があり、効率的な低電力デバイスを設計するには考慮しなければならないことです。

CR2032のような電池は、210mAHの容量で、1か月あたりの自己放電率は1%です。単4型のニッケル水素電池は、CR2032よりも多い900mAHの容量ですが、1か月あたりの自己放電率は30%です。単三の場合は、2400mAHの容量で、自己放電率は同じです。リチウムイオン電池は、4400mAHの容量で、1か月あたりの自己放電率は10%です。

デバイスに利用できる電源にはいくつかの種類があり、設計要素として利用できる様々な特性があります。具体的な設計要件によっては、電源に厳密な化学成分が求められる場合もあります。しかし、電源容量と自己放電率を、省電力デバイスの設計に必要な主要な指標とします。というのも、化学成分に対する要件は、厳密に監理された環境にデバイスがとどまるための、安全確保のためだからです。

自己放電率は、以下の数式で表されます。

この自己放電率の式で、1時間当たりどのくらい電源が放電するか知ることができます。この値で、継続的な消費率としても同様の近似値を知ることができます。

省電力デバイスを作成するには、いくつかの設計要素が関係します。ここで、電源はこれらの条件に影響を受けるので、設計時に考慮する必要があります。これは、前述したように、実際のデバイスの消費以上に電力を消費するというリスクを避けるためです。

しかし、電源の物理要素を知ることも重要です。物理要素は、デバイスの大きさを決定するので、デバイスを小さくしようとすると、電源の選択が限られてしまうためです。このため、高度な省電力技術が、この省電力ガイドを完全に活用するための設計上の考慮事項を通して、あなたを案内してくれるかもしれません。

低電力アプリケーションの例

以下の簡単な例を通じて、低電力モードを理解し実装することができるようになります。

標準的な低電力の例

  • 必要なハードウェア: 任意のSAMD21ベースのArduinoボード(MKRファミリー)

これはもっとも単純な低電力モードの実装方法です。LEDを利用し、デバイスがアクティブ状態化スリープ状態か示します。デバイスは5秒間スリープ状態になります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include "ArduinoLowPower.h"

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

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
  LowPower.sleep(5000);
}

ここでデバイスをディープスリープモードを実行するコードを変更することができます。

1
2
//LowPower.sleep(5000);
LowPower.deepSleep(5000);

これは、デバイスの電源が入った時に、デバイスをディープスリープモードに設定します。この簡単なコード例では、loop関数内に書かれていることがわかります。しかし、この低電力コードはsetup関数内に書くこともできます。

setup関数は、様々な処理を実行する前にボードを適切に設定する場所なので、最初に実行されることに注意してください。このため、最初にソフトウェア構造を設計し、その後、実際にコードを書くことが推奨されています。これは、省電力デバイスの実現に役立ちます。

外部イベントに基づく低電力の例

  • 必要なハードウェア: 任意のSAMD21ベースのArduinoボード(MKRファミリー)

以下の例は、所定のピンで外部イベントが発生しない限り、10秒ごとにボードを起動する方法を示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include "ArduinoLowPower.h"

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pin, mode);
  LowPower.attachInterruptWakeup(pin, callback, mode);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000); 
  LowPower.sleep(10000);
}

void callback() {
  // This function will be called once on device wakeup
  // You can do some little operations here (like changing variables which will be used in the loop)
  // Remember to avoid calling delay() and long running functions since this functions executes in interrupt context
}

ここでは、LowPower.attachInterruptWakeup(pin, callback, mode)関数を定義する必要があります。引数は、操作するデバイスのpin、タスクを設計するcallback、定義したpinの変化を検出する遷移を定義するmodeです。modeは、FALLINGかRISING、CHANGEの3つのどれかを選ぶことができます。

FALLINGは、指定したピンが立ち下がる信号を意味します。RISINGは、立ち上がりを意味し、CHANGEは立下り・立ち上がりの双方を意味します。pinMode(pin, mode)では、modeは、INPUTOUTPUTINPUT_PULLUPのどれかです。低電力の例で定義された外部イベントを見るには以下のようにします。この例は、「スケッチ例」→「Arduino Low Power」→「ExternalWakeup」と進めばあります。

 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
32
33
34
35
#include "ArduinoLowPower.h"

// Blink sequence number
// Declare it volatile since it's incremented inside an interrupt
volatile int repetitions = 1;

// Pin used to trigger a wakeup
const int pin = 8;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  // Set pin 8 as INPUT_PULLUP to avoid spurious wakeup
  pinMode(pin, INPUT_PULLUP);
  // Attach a wakeup interrupt on pin 8, calling repetitionsIncrease when the device is woken up
  LowPower.attachInterruptWakeup(pin, repetitionsIncrease, CHANGE);
}

void loop() {
  for (int i = 0; i < repetitions; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }
  // Triggers an infinite sleep (the device will be woken up only by the registered wakeup sources)
  // The power consumption of the chip will drop consistently
  LowPower.sleep();
}

void repetitionsIncrease() {
  // This function will be called once on device wakeup
  // You can do some little operations here (like changing variables which will be used in the loop)
  // Remember to avoid calling delay() and long running functions since this functions executes in interrupt context
  repetitions ++;
}

ADC(Analog to Digital Converter)に基づく低電力の例

  • 必要なハードウェア: 任意のSAMD21ベースのArduinoボード(MKRファミリー)

先ほどの外部イベントに基づく低電力の例を少し変更すれば、定義した読み取り電圧の幅を入力とした、ADCによる起動に設定することができます。この例は、「スケッチ例」→「Arduino Low Power」→「AdcWakeup」で見ることができます。

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "ArduinoLowPower.h"

// Blink sequence number
// Declare it volatile since it's incremented inside an interrupt
volatile int repetitions = 1;

// Pin used to trigger a wakeup
const int pin = A0;
// How sensitive to be to changes in voltage
const int margin = 10;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pin, INPUT);
}

void loop() {
  for (int i = 0; i < repetitions; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }

  // Read the voltage at the ADC pin
  int value = analogRead(pin);

  // Define a window around that value
  uint16_t lo = max(value - margin, 0);
  uint16_t hi = min(value + margin, UINT16_MAX); 

  // Attach an ADC interrupt on pin A0, calling repetitionsIncrease when the voltage is outside the given range.
  // This should be called immediately before LowPower.sleep() because it reconfigures the ADC internally.
  LowPower.attachAdcInterrupt(pin, repetitionsIncrease, ADC_INT_OUTSIDE, lo, hi);

  // Triggers an infinite sleep (the device will be woken up only by the registered wakeup sources)
  // The power consumption of the chip will drop consistently
  LowPower.sleep();

  // Detach the ADC interrupt. This should be called immediately after LowPower.sleep() because it restores the ADC configuration after waking up.
  LowPower.detachAdcInterrupt();
}

void repetitionsIncrease() {
  // This function will be called once on device wakeup
  // You can do some little operations here (like changing variables which will be used in the loop)
  // Remember to avoid calling delay() and long running functions since this functions executes in interrupt context
  repetitions ++;
}

このコード深く見ていくと、デバイスのアナログピンA0が動作する値の範囲を決めていることがわかります。

1
2
uint16_t lo = max(value - margin, 0);
uint16_t hi = min(value + margin, UINT16_MAX);

ADC割り込みの設定はloop関数内で行っているので、デバイスがスリープモードに移行する直前に設定を変更することができます。

1
LowPower.attachAdcInterrupt(pin, repetitionsIncrease, ADC_INT_OUTSIDE, lo, hi);

ADC割り込みの範囲内でデバイスが起動した後は、ADC割り込みを切り離し、ADC設定を回復させます。

1
LowPower.detachAdcInterrupt();

設定された割り込みによりスリープ状態からシステムが起動したときにコールバック関数は呼ばれます。この関数内や、全体のソフトウェア設計では、通常、delay()の利用や、長時間実行する関数を避けることがいいとされています。これは、「ブロック操作」と呼ばれることを避けるためで、「非ブロック操作」を使って設計するということです。これは、このような設計の場合に役立ちます。このことは、省電力システムの設計や、また同時に、応答性の高いシステムの設計にも役立ちます。

LoRa送信機での低電力の例

  • 必要なハードウェア: MKR WAN 1300/1310 (On-Board Murata Module)
i
Murataモジュールを搭載したMKR WAN 1310を使ったLoRa®に関する詳細や例は、Send Data Using LoRa® with MKR WAN 1310を参照してください。

ここでは、遠隔送信デバイスとして、MKR WAN1300/1310が定期的に生存状態を示すビーコンを送信する例を示します。これは、一定時間ごとにビーコンデータをブロードキャストするデバイスをシミュレートするもので、長い動作時間が求められます。受信機は、受信塔として固定されています。遠隔送信デバイスはSAMD21をスリープモードにします。同時に、ボード上のMurataモジュールの不必要な電力消費も削減します。

i
LoRaライブラリに関する情報は、GitHub上のArduino LoRaリポジトリを参照してください。
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Low Power Library
#include "ArduinoLowPower.h"

// LoRa Library
#include <SPI.h>
#include <LoRa.h>

// LoRa Packet Content 
char* message = "Hello LoRa!";

void setup() {
  Serial.begin(9600);
  while (!Serial);

  // LoRa Setup
  Serial.println(F("LoRa Sender"));
  if (!LoRa.begin(868E6)) {
    Serial.println(F("Starting LoRa failed!"));
    while (1);
  } else {
    Serial.println(F("Starting LoRa Successful!"));
  }
}

void loop() {
  LoRa_Packet_Sender();
  GoToSleep();
}

// LoRa Task
void LoRa_Packet_Sender() {
  Serial.print(F("Sending packet: "));
  Serial.println(message);

  // send packet
  LoRa.beginPacket();
  LoRa.print(message);
  LoRa.endPacket();

  // Putting LoRa Module to Sleep 
  Serial.println(F("LoRa Going in Sleep"));
  LoRa.sleep();
}

// Sleep Task 
void GoToSleep(){
  Serial.println(F("MKR WAN 1310 - Going in Sleep"));
  LowPower.deepSleep(20000);
}

低電力タスクはマイクロコントローラーだけに適用されることを理解することが重要です。我々が使ったMKR WAN 1310ボード上のMurataモジュールのような外部モジュールは、そのモジュールをスリープモードにするコードを別に書く必要があることを意味します。

i
Murata(LoRa)モジュールやセンサーのような外部モジュールを使うときは、MCUをスリープ状態にする前に、モジュールをスリープ状態にすることを忘れないでください。そうでないと、デバイスは完全にはスリープモードにはならず、最大限の省電力化は不可能です。これには、例えば、TWIやSPIの外部機器の電源を切ることも含みます。

簡単な低電圧検出の例

  • 必要なハードウェア: 任意のファミリーのArduinoボード

これは、もっとも簡単な低電圧検出を実装する例です。これは、電源が少なくなってきたことを検出し、デバイスが電源断によりシャットダウンするのを防ぎます。これは単純な操作ですが、参照値として使用するパラメータを明確にする必要があります。そうしないと、間違った測定値が観測されます。以下の例は、MKR WAN 1310を使った設定で、アナログピンに直接接続された電源の残りパーセントを検出します。

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/*
Low Power - Low Voltage Detection - SAMD21 Specific Configuration Example
*/

// Manual Power Management 
#include "ArduinoLowPower.h"

float voltValue, battery_volt, battery_percentage;
float minimal_voltage = 1800;
float battery_voltage_ref = 3.3;

void setup() {
  Serial.begin(57600);
  delay(100);

  // Low Power Indicator Set 
  pinMode(LED_BUILTIN, OUTPUT);

  // Default Analog Reference of 3.3V
  analogReference(AR_DEFAULT);

  // Setting up for resolution of 12-Bits
  analogReadResolution(12);
}

void loop() { 
  // Reading from the Battery Pin
  voltValue = analogRead(A0);

  // Calculate current voltage level
  battery_volt = ((voltValue*battery_voltage_ref)/4095)*1000;
  
  // Battery level expressed in percentage
  battery_percentage = 100*abs((battery_volt - minimal_voltage)/((battery_voltage_ref*1000) - minimal_voltage));

  Serial.print(F("Battery: "));
  Serial.print(battery_percentage);
  Serial.println(F(" % "));
  
  if (battery_volt <= minimal_voltage){
    //LED Notification for low voltage detection
    lowBatteryWarning();
  }

  delay(2000);
  
  // Going into Low Power for 20 seconds 
  LowPower.deepSleep(20000);
}

// Low battery indicator
void lowBatteryWarning(){
  digitalWrite(LED_BUILTIN, HIGH);  
  delay (1);             
  digitalWrite(LED_BUILTIN, LOW);    
  delay (999);             
}

4つの重要な設定を示します。

  • analogReference()は、アナログ入力の参照電圧を設定するのに使われます。
  • analogReadResolution()は、analogRead()が返す値の精度を決めるのに使われます。
  • analogRead()は、アナログピンから値を読むのに使われます。
  • 最後に、それぞれの分周器の分解能です。この例では、4095が使われ、MKR WAN 1310では12ビットの精度です。10ビットのような異なる分解能を使う場合は、1023に設定する必要があります。

分圧回路を使うときは、正しい値を取得するために、コード内で抵抗の関連を定義・設定する必要があります。

低電圧検出に関する詳細は、このガイドの最後のセクションである、Arduinoでの高度な低電力技術を参照してください。

Arduinoでの高度な低電力技術

以下の、高度な低電力技術は、全てのArduinoボードに適用することができます。ボード全体を活用することで、電力消費を可能な限り削減します。これらの方法は、どのようなArduinoボードでも、創造的で柔軟な低電力システムの設計に役立ちます。

低周波数と低電圧

より高度なユースケースでは、さらに電力消費を削減する手段があります。プロセッサを低周波数・低電圧にする方法です。低周波数に関しては、WiFiのような通信モジュールが利用できる最低限のレベルの周波数が要求されるので、デバイスの機能に依存します。また、3.3Vという低電圧でデバイスを動作させることができることもあり、全体の電力消費を抑えることができます。これは、スケッチをデバイスにアップロードする前に、Arduino IDEで背亭することができます。

マイクロコントローラーの電圧と周波数の設定

マイクロコントローラーの電圧と周波数の設定

プロセッサは、通常、負荷に応じて周波数を変更します。このプロセッサ周波数は、計算を最小時間で高速に実行するために変化します。Wi-FiやBluetooth® Low Energyのようないくつかのモジュールでは、最低限の周波数が要求されます。一方、低すぎる周波数ではモジュールが正しく動かなくなることや、全く動作しなくなることもあります。また、さらに低周波数にする場合は、適した発信機が必要になるので、外付けの水晶発振器が必要になることもあります。

無理に周波数を低くしても、モジュールや操作に支障をきたすので、役に立たなくなります。一方、周波数を制限しないでおくと、長い電池寿命が実現できません。設計したタスクを完遂しつつ電力消費を管理するには、ソフトウェアアーキテクチャに依存した周波数に設定することが推奨されます。周波数レベルはボードのデータシートを参照すればわかるので、ソフトウェアアーキテクチャの設計や消費電力の改善に利用できます。

電圧に関しては、通常、ボードは5Vか3.3Vで動作するように設計されています。この動作電圧は、通常、Arduinoボードと連携する外部構成要素に依存します。しかし、外部構成要素や類似の部品が常に高電圧を必要とするとは限りません。このような場合は、動作電圧を3.3Vにすることができます。これにより、より省電力のデバイスを作ることができます。この2つの要素から始めると、全体の消費電力を削減、さらには、半減させることも可能です。

マイクロコントローラーレベルの電源管理

ほとんどの場合、ライブラリクラスを使うことで低消費電力を実現することができます。しかし、マイクロコントローラー内の個別の要素の電源を切る必要がある設計をする場合は、電力管理モードもしくはレジスタがこの要件を達成するのに役立ちます。

SAMD21マイクロコントローラー - 電力削減モード

MKR FamilyボードにあるSAMD21マイクロコントローラーには、電力削減モードがあります。

電力削減モードは、不揮発メモリコントローラーと関連し、CTRLBレジスタがモードの制御に使われます。不揮発メモリコントローラーとブロックが電力削減モードのときは、電力削減モードビットは状態レジスタ(STATUS.PRM)で設定されます。不揮発メモリコントローラーとブロックの設定には、電力削減モードの設定と解除コマンドが使われます。レジスタテーブルの詳細は、以下のリファレンスとデータシートを参照してください。

ATmega328Pマイクロコントローラー - 電力削減レジスタ

Atmega328Pマイクロコントローラーを利用する初期からのファミリーArduino Nanoには、電力削減レジスタがあります。

この電力削減レジスタはマイクロコントローラー内のいくつかの機能を止めることができます。これは、TWIやSPI、USART0、タイマーカウンター、ADCを止めることができます。ADCビットをオフにする場合は、ADCRSAを0にする必要があります。そうしないと、アクティブ状態でフリーズします。

周辺機器と内部モジュール

設計したアプリケーションにより、不要な内部モジュールを止めることは、さらに消費電力を削減するのに有用です。これらのモジュールは、SPIやI2C、シリアル、ADCです。これらのモジュールに加え、電圧低下検出(Brown-out Detection)とウォッチドッグタイマーがあります。

SPIやI2C,シリアル通信のような周辺機器インターフェイスは、センサーとの通信によく利用されます。全体設計をすることにより、そのアプリケーションでいくつかの周辺機器が実装されないことがわかります。周辺機器を浮かせたままにせず、消費電力を削減するためにこれらの使われない周辺機器を止めることはよいことです。さらに、センサーにも指示されると、自身を省電力状態になるものもあります。接続が確立した周辺機器を通じて、周辺機器をスリープ状態にする前に、センサーを省電力状態に移行するコマンドを送ることができます。

前述したように、ADCを通じて復帰させることができます。しかし、復帰のために使わない場合は、止めることもできます。この周辺機器は、時折、多くの電力を消費するので、止めることは電池寿命を延ばすのに有効な場合があります。

電圧低下検出とウォッチドッグタイマーは設定で制御できる内部モジュールで、必要であればより低消費電力を実現できます。電圧低下検出は、通常、プロセッサがいつ低電圧の検出を必要とする場合の、比較目的で使われます。ウォッチドッグタイマーはマイクロコントローラーの動作状態を監視する重要な機能です。

マイクロコントローラーがスタックし停止する、あるいは、不正な処理を行うと、マイクロコントローラーを元に戻すために、ウォッチドッグタイマーが起動されます。一方、hhhhhhhhhhhhhhhこの同じウォッチドッグタイマーは、消費電力を削減するために一時的に無効にすることもできます。これは、ソフトウェアが十分安定したことが確認されてから実装することが常に推奨されています。そうでなければ、消費電力削減に役立たず、システムを壊すかもしれません。

外部デバイス

SDデバイスやセンサーなどの外部デバイスがあるとき、MOS-FETを間に入れることで、止めることもできます。MOSFETは、デバイスがディープスリープ状態になった場合に、消費電力削減のために、Arduinoボードに接続されたSDデバイスの電源を切るのに役立ちます。センサーの場合は、完全に消費電力を抑える必要があれば、MOSFETで止めることもできます。この方法はデバイスが定期的にスリープ状態になるときに有効で、消費電力を最大限に節約できます。

Low-Dropoutレギュレータ

デザインによっては、正しく動作させるために、電圧レギュレータが必要となる場合があります。設計で電圧レギュレータが必要になった場合は、自己消費電流が低いLow-Dropoutレギュレータを探してください。これにより、スリープ状態でのデバイスの消費電力レベルを大幅に削減することができます。この詳細は、レギュレータメーカーのデータシートを参照してください。

Power Budgeting

システムのパワーバジェットを記録し続けることは、いいことです。というのも、設計全体を通じてどのように電力分配が取り扱われるかを解析するために、概要や詳細がわかるからです。例えば、スリープ可能なタイマーが増える可能性があるかもしれないという情報が得られることもあるので、より良い電力消費設計に役立ちます。

電力消費測定方法

Arduinoボードでの低消費電力を実現するための全ての方法やコツを使ったときも、正しく機能するかを確認するために、システムの消費電力を測定する必要があります。電流を測定し、システムの実際の消費電力を知るには、マルチメーターの基本の電流測定セクションが使えます。

低電圧検出

設計したタスクが正しく動作し低消費電力を実現するデバイスは、既にいい全体設計となっています。しかし、デバイスがどんなに長く稼働しても、電源は切れます。確かに、正しい構成とコードアーキテクチャがあれば、丸1年動作するかもしれません。しかし、いつかは電圧低下供給されているかを知ることができます。それが、システムの低電圧検出です。

i
次の電池寿命計算機を使って、推定寿命を取得することができます: https://www.omnicalculator.com/other/battery-life

このために、例えばAtmega328PベースのArduino NanoClassic Familyが採用している、AVRマイクロコントローラーの電力管理手法を使います。Nick Gammonと Retrolefty、Coding Badlyによって開発された低電圧検出方法と、消費電力削減のために低電力状態への移行手段を組み合わせます。

  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Nick Gammon
// Code courtesy of "Coding Badly" and "Retrolefty" from the Arduino forum

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

const long InternalReferenceVoltage = 1062;  // Adjust this value to your board's specific internal BG voltage

void setup() {
  Serial.begin(57600);
  delay(100);

  // Low Power Indicator Set 
  pinMode(LED_BUILTIN, OUTPUT);
  
  // Pre-eliminary Low Power 
  resetWatchdog(); // In Case WDT fires
}

void loop() { 
  // Tuning on peripherals, timers, and ADC
  manual_periph_ctrl(1);
  
  // Voltage Level Detection
  Serial.println(getBandgap());
  // Low voltage detection at 3V (300)
  if (getBandgap() < 300){
    //LED Notification for low voltage detection
    lowBatteryWarning();
    
    // Turns off peripherals, timers, and ADC 
    manual_periph_ctrl(0);
  }

  // Low Level Handler
  i2c_switch_off();
  Manual_LowPower_Mode(1);    
}

// Fixed at 8 second variant - On the contrary IT TURNS EVERYTHING OFF
void Manual_LowPower_Mode(uint8_t multiplier){
  delay(70);                                              // Requires at least 68ms of buffer head time for module booting time
  for(int i = 0; i <= multiplier; i++){                   // Multiplier for Power Down Tick
    Deep_Sleep_Manual();
  }
}

/*
* Low Voltage Detection Task
*/
// Nick Gammon
// Code courtesy of "Coding Badly" and "Retrolefty" from the Arduino forum
// results are Vcc * 100
// So for example, 5V would be 500.
int getBandgap(){
  // REFS0 : Selects AVcc external reference
  // MUX3 MUX2 MUX1 : Selects 1.1V (VBG)  
   ADMUX = bit (REFS0) | bit (MUX3) | bit (MUX2) | bit (MUX1);
   ADCSRA |= bit( ADSC );  // start conversion
   while (ADCSRA & bit (ADSC)){ 
   }  // wait for conversion to complete
   int results = (((InternalReferenceVoltage * 1024) / ADC) + 5) / 10; 
   return results;
} // end of getBandgap

/*
* Low Power Related Tasks 
*/
// Enabling Watchdog Timer 
void WatchdogEnable() {
  // clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset
  WDTCSR = bit (WDCE) | bit (WDE);
  // set interrupt mode and an interval 
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  wdt_reset();  // pat the dog

  // disable ADC
  ADCSRA = 0;  

  // ready to sleep
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  // Should throw ~0.4mA at best case 
  noInterrupts();
  sleep_enable();

  // turn off brown-out enable in software
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 
  interrupts();       // Guarantees next instruction executed
  sleep_cpu ();  

  // cancel sleep as a precaution
  sleep_disable();
} 

void resetWatchdog (){
  // clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset, clear existing interrupt
  WDTCSR = bit (WDCE) | bit (WDE) | bit (WDIF);
  // set interrupt mode and an interval (WDE must be changed from 1 to 0 here)
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  // pat the dog
  wdt_reset();  
}  // end of resetWatchdog

void i2c_switch_off(){
  // turn off I2C
  TWCR &= ~(bit(TWEN) | bit(TWIE) | bit(TWEA));

  // turn off I2C pull-ups
  digitalWrite (A4, LOW);
  digitalWrite (A5, LOW);
}

// Runs for 8 seconds aprox counting as a cycle
void Deep_Sleep_Manual(){
  // CORE State
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  ADCSRA = 0;            // turn off ADC
  power_all_disable ();  // power off ADC, Timer 0 and 1, serial interface

  // Interrupts are not counted as ADXL require
  noInterrupts ();       // timed sequence coming up
  resetWatchdog ();      // get watchdog ready
  sleep_enable ();       // ready to sleep
  interrupts ();         // interrupts are required now
  sleep_cpu ();          // sleep                
  sleep_disable ();      // precaution
  power_all_enable ();   // power everything back on
  
}  // end of goToSleep 

// Manual Peripheral controller
void manual_periph_ctrl(uint8_t selector){
  byte old_ADCSRA = ADCSRA;
  // disable ADC
  ADCSRA = 0; 
   
  if (selector == 0){
    power_adc_disable();
    power_spi_disable();
    power_timer0_disable();
    power_timer1_disable();
    power_timer2_disable();
    power_twi_disable();

    UCSR0B &= ~bit (RXEN0);  // disable receiver
    UCSR0B &= ~bit (TXEN0);  // disable transmitter
  } 
  if (selector >= 1){
    power_adc_enable();
    power_spi_enable();
    power_timer0_enable();
    power_timer1_enable();
    power_timer2_enable();
    power_twi_enable();
    
    UCSR0B |= bit (RXEN0);  // enable receiver
    UCSR0B |= bit (TXEN0);  // enable transmitter
  } 

  ADCSRA = old_ADCSRA;
}

// Low battery indicator
void lowBatteryWarning(){
  digitalWrite(LED_BUILTIN, HIGH);  
  delay (1);             
  digitalWrite(LED_BUILTIN, LOW);    
  delay (999);             
}

内部参照電圧を知るために、以下のどちらかのスクリプトを実行します。以下のスクリプトを実行しているときに、マイクロコントローラーのAREFピンの電圧をマルチメーターで測定し、内部参照電圧にするために測定値を1000倍します。

  • ADMUXレジスタを使う
1
2
3
4
5
6
7
// Find internal 1.1 reference voltage on AREF pin
void setup ()
{
  ADMUX = bit (REFS0) | bit (REFS1);
}

void loop () { }
  • analogReference関数とanalogRead関数を使う
1
2
3
4
5
6
7
8
// Find internal 1.1 reference voltage on AREF pin
void setup ()
{
  analogReference (INTERNAL);
  analogRead (A0);  // force voltage reference to be turned on
}

void loop () { }
i
低電力システムについてより詳細を読みたければ、以下のリンクを参照してください: https://www.gammon.com.au/power

オリジナルのページ

https://docs.arduino.cc/learn/electronics/low-power

最終更新日

March 2, 2023

inserted by FC2 system