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
*/