I fail to use PWM in power saving mode. The output behaves erratically. This should be possible, I guess. The following is a minimal working example:
/* MWE: Cannot use PWM in any power saving mode.
A minimal working example (MWE) demonstrating my trouble in using
PWM while saving power.
Intention: Control one load to be on or off for longer periods of
time (hours, that is). The brightness of the load is set by PWM.
Wiring:
+5V
|
+------------------+ |
+5V---|Vin | |
| Arduino | C
| Pro Mini 9|-----/\/\/----B|< PN2222
| compatible | 1k E
| (ATmega328) | |
GND---|GND | | +
+------------------+ LED
| -
|
>
< 1k
>
|
|
GND
Implementation:
* Set watchdog to cause an interrupt in regular intervals (in
this MWE: 2s). Count down `cycles` (MWE: 2 or 3) on each
watchdog interrupt.
* If `cycles` is 0, toggle the output, set new `cycles` value.
The real program uses 7200 8s cycles for 16h before the next
toggle.
* Go to SLEEP_MODE_PWR_DOWN.
Expected: attached load is on for 4s, dimmed at 32/256. Then off
for 6s. Then repeat.
Observed: In most cases, there's no output in sleep mode, the LED
only blinks before sleeping. But sometimes it is on, so
PWM seems to be possible while sleeping!
The real program (from which this MWE is derived) uses adjustable
brightness, wakes up from sleep by external interrupts, stores
brightnes to EEPROM, ...
*/
/* Setting the following to 0 uses `digitalWrite` instead of
`analogWrite` to demonstrate that the watchdog and interrupt logic
do actually work. */
#define USE_PWM 1
#include <avr/sleep.h>
#include <avr/wdt.h>
#define outputPin 9 // where the PWM load is connected
#define onCycles 2 // num of watchdog cycles to stay on
#define offCycles 3 // num of watchdog cycles to stay off
int brightness = 32; // PWM duty cycle in 1/256, if the load is on
int toggle = 1; // current on/off state of PWM load
/* Number watchdog cycles until next toggle. This will be modified in
the watchdog interrupt handler. */
volatile unsigned long wdt_cycles = onCycles;
/* Watchdog interrupt handler called when watchdog expires, count down
remaining cycles. Does not underflow. */
ISR(WDT_vect) {
if (wdt_cycles > 0)
wdt_cycles--;
}
void setup(void) {
/* Setup watchdog. See [1] p54, [2] */
cli(); // BEGIN disable interrupts
MCUSR &= ~(1<<WDRF); // no system reset on watchdog timeout
WDTCSR |= 1<<WDCE | 1<<WDE; // allow watchdog configuration
WDTCSR
= 1<<WDIE // enable interrupts
| 1<<WDP2 | 1<<WDP1 | 1<<WDP0 // 2.0 seconds
;
wdt_reset(); // reset timer
sei(); // END disable interrupts
/* Setup output pins */
pinMode(outputPin, OUTPUT);
#if USE_PWM
analogWrite(outputPin, brightness);
#else
digitalWrite(outputPin, toggle ? HIGH : LOW);
#endif
}
void loop(void) {
if (wdt_cycles < 1) {
toggle = !toggle;
wdt_cycles = toggle ? onCycles : offCycles;
#if USE_PWM
analogWrite(outputPin, toggle ? brightness : 0);
#else
digitalWrite(outputPin, toggle ? HIGH : LOW);
#endif
}
/* Heartbeat: Short blink of builtin led. This verifies that we
are actually sleeping and waking up. */
digitalWrite(LED_BUILTIN, HIGH);
delay(50);
digitalWrite(LED_BUILTIN, LOW);
/* Got to sleep, see [3]. I want `SLEEP_MODE_PWR_DOWN`, but also
none of the others works. */
set_sleep_mode(
//SLEEP_MODE_IDLE
//SLEEP_MODE_ADC
//SLEEP_MODE_PWR_SAVE
//SLEEP_MODE_STANDBY
SLEEP_MODE_PWR_DOWN
);
sleep_mode();
}
/* References
[1] https://cdn.sparkfun.com/datasheets/Kits/doc8161.pdf
[2] https://forum.arduino.cc/index.php?topic=63651.0
[3] https://playground.arduino.cc/Learning/ArduinoSleepCode
*/
2 Answers 2
The timers are disabled during SLEEP_MODE_PWR_DOWN, so PWM doesn't work during SLEEP_MODE_PWR_DOWN. You could instead use SLEEP_MODE_IDLE
, but that only saves a bit of current.
When you put the MCU to sleep, the led will keep the state it was in during PWM. So around 12.5% if the time the leds will be on, and the other times it will be off (during those 2s PWM periods).
-
Your second comment explains why I have seen light-on in sleep mode, and also why this is difficult to reproduce.stefan– stefan2017年11月25日 13:53:06 +00:00Commented Nov 25, 2017 at 13:53
Just as a complement to Gerben's answer:
- You could use
SLEEP_MODE_PWR_DOWN
during the off period, butSLEEP_MODE_IDLE
is the only way to do PWM while sleeping. - When in
SLEEP_MODE_IDLE
, you can use thepower_*_disable()
function defined in<avr/power.h>
in order to further reduce the power consumption.
-
Really good info: "SLEEP_MODE_IDLE is the only way to do PWM while sleeping". I failed to find this in the ATmega docs. I'm using what you suggested, thanks!stefan– stefan2017年11月25日 13:54:45 +00:00Commented Nov 25, 2017 at 13:54
Explore related questions
See similar questions with these tags.