5

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
 */ 
asked Nov 24, 2017 at 14:56
0

2 Answers 2

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).

answered Nov 24, 2017 at 16:46
1
  • Your second comment explains why I have seen light-on in sleep mode, and also why this is difficult to reproduce. Commented Nov 25, 2017 at 13:53
1

Just as a complement to Gerben's answer:

  1. You could use SLEEP_MODE_PWR_DOWN during the off period, but SLEEP_MODE_IDLE is the only way to do PWM while sleeping.
  2. When in SLEEP_MODE_IDLE, you can use the power_*_disable() function defined in <avr/power.h> in order to further reduce the power consumption.
answered Nov 24, 2017 at 19:14
1
  • 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! Commented Nov 25, 2017 at 13:54

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.