. Arduino Guide
Arduino Guide
Arduino Guide

低電力設計技術

スリープモードでは、マイクロコントローラーを低電力モードにする際に、スタンバイモードとアイドルモードという異なるレベルの設定があります。デバイスの要件により、スリープモードでも、いくつかのコンポーネントやモジュールは動作し続ける必要があります。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)により引き起こされる起動

電源ガイドと自己放電率

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

標準的な低電力の例

  • 必要なハードウェア: 任意のSAMD21ベースのArduinoボード(MKRファミリー)
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); > //LowPower.sleep(5000); LowPower.deepSleep(5000);

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

  • 必要なハードウェア: 任意のSAMD21ベースのArduinoボード(MKRファミリー)
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は、 INPUT か OUTPUT 、 INPUT_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 ++; > uint16_t lo = max(value - margin, 0); uint16_t hi = min(value + margin, UINT16_MAX); LowPower.attachAdcInterrupt(pin, repetitionsIncrease, ADC_INT_OUTSIDE, lo, hi); LowPower.detachAdcInterrupt();

LoRa送信機での低電力の例

  • 必要なハードウェア: MKR WAN 1300/1310 (On-Board Murata Module)
Murataモジュールを搭載したMKR WAN 1310を使ったLoRa®に関する詳細や例は、Send Data Using LoRa® with MKR WAN 1310を参照してください。 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 #include // 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モジュールのような外部モジュールは、そのモジュールをスリープモードにするコードを別に書く必要があることを意味します。

簡単な低電圧検出の例

  • 必要なハードウェア: 任意のファミリーの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); >
  • analogReference()は、アナログ入力の参照電圧を設定するのに使われます。
  • analogReadResolution()は、analogRead()が返す値の精度を決めるのに使われます。
  • analogRead()は、アナログピンから値を読むのに使われます。
  • 最後に、それぞれの分周器の分解能です。この例では、4095が使われ、MKR WAN 1310では12ビットの精度です。10ビットのような異なる分解能を使う場合は、1023に設定する必要があります。

Arduinoでの高度な低電力技術

低周波数と低電圧

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

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

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

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

外部デバイス

Low-Dropoutレギュレータ

Power Budgeting

電力消費測定方法

低電圧検出

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

このために、例えばAtmega328PベースのArduino NanoやClassic 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 #include #include 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); > 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 () >
📎📎📎📎📎📎📎📎📎📎