スリープモードでは、マイクロコントローラーを低電力モードにする際に、スタンバイモードとアイドルモードという異なるレベルの設定があります。デバイスの要件により、スリープモードでも、いくつかのコンポーネントやモジュールは動作し続ける必要があります。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)ポートにより発出されます。
#include"ArduinoLowPower.h"voidsetup(){pinMode(LED_BUILTIN,OUTPUT);pinMode(pin,mode);LowPower.attachInterruptWakeup(pin,callback,mode);}voidloop(){digitalWrite(LED_BUILTIN,HIGH);delay(1000);digitalWrite(LED_BUILTIN,LOW);delay(1000);LowPower.sleep(10000);}voidcallback(){// 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
}
#include"ArduinoLowPower.h"// Blink sequence number
// Declare it volatile since it's incremented inside an interrupt
volatileintrepetitions=1;// Pin used to trigger a wakeup
constintpin=8;voidsetup(){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);}voidloop(){for(inti=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();}voidrepetitionsIncrease(){// 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++;}
#include"ArduinoLowPower.h"// Blink sequence number
// Declare it volatile since it's incremented inside an interrupt
volatileintrepetitions=1;// Pin used to trigger a wakeup
constintpin=A0;// How sensitive to be to changes in voltage
constintmargin=10;voidsetup(){pinMode(LED_BUILTIN,OUTPUT);pinMode(pin,INPUT);}voidloop(){for(inti=0;i<repetitions;i++){digitalWrite(LED_BUILTIN,HIGH);delay(500);digitalWrite(LED_BUILTIN,LOW);delay(500);}// Read the voltage at the ADC pin
intvalue=analogRead(pin);// Define a window around that value
uint16_tlo=max(value-margin,0);uint16_thi=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();}voidrepetitionsIncrease(){// 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++;}
これは、もっとも簡単な低電圧検出を実装する例です。これは、電源が少なくなってきたことを検出し、デバイスが電源断によりシャットダウンするのを防ぎます。これは単純な操作ですが、参照値として使用するパラメータを明確にする必要があります。そうしないと、間違った測定値が観測されます。以下の例は、MKR WAN 1310を使った設定で、アナログピンに直接接続された電源の残りパーセントを検出します。
/*
Low Power - Low Voltage Detection - SAMD21 Specific Configuration Example
*/// Manual Power Management
#include"ArduinoLowPower.h"floatvoltValue,battery_volt,battery_percentage;floatminimal_voltage=1800;floatbattery_voltage_ref=3.3;voidsetup(){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);}voidloop(){// 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
voidlowBatteryWarning(){digitalWrite(LED_BUILTIN,HIGH);delay(1);digitalWrite(LED_BUILTIN,LOW);delay(999);}
// 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>constlongInternalReferenceVoltage=1062;// Adjust this value to your board's specific internal BG voltage
voidsetup(){Serial.begin(57600);delay(100);// Low Power Indicator Set
pinMode(LED_BUILTIN,OUTPUT);// Pre-eliminary Low Power
resetWatchdog();// In Case WDT fires
}voidloop(){// 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
voidManual_LowPower_Mode(uint8_tmultiplier){delay(70);// Requires at least 68ms of buffer head time for module booting time
for(inti=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.
intgetBandgap(){// 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
intresults=(((InternalReferenceVoltage*1024)/ADC)+5)/10;returnresults;}// end of getBandgap
/*
* Low Power Related Tasks
*/// Enabling Watchdog Timer
voidWatchdogEnable(){// 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();}voidresetWatchdog(){// 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
voidi2c_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
voidDeep_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
voidmanual_periph_ctrl(uint8_tselector){byteold_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
voidlowBatteryWarning(){digitalWrite(LED_BUILTIN,HIGH);delay(1);digitalWrite(LED_BUILTIN,LOW);delay(999);}
// Find internal 1.1 reference voltage on AREF pin
voidsetup(){ADMUX=bit(REFS0)|bit(REFS1);}voidloop(){}
analogReference関数とanalogRead関数を使う
1
2
3
4
5
6
7
8
// Find internal 1.1 reference voltage on AREF pin
voidsetup(){analogReference(INTERNAL);analogRead(A0);// force voltage reference to be turned on
}voidloop(){}