I'm using sleep mode to turn off my device after the routine has finished executing and want to use interrupts to wake it up every 33 milliseconds. Basically, the idea is that if the routine completes in less than 33 milliseconds the processor can shutdown and save power for whatever remaining time, which will hopefully translate to reduced power consumption. I've currenlty managed to turn off my device with the sleepNow()
function you see below, but the interrupt I have programmed in doesn't seem to work. The program simply goes to sleep and never wakes up. What am I doing wrong?
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
const uint16_t PERIOD = 2150;
void setup()
{
noInterrupts();
TCCR1A = 0; // undo the timer config done...
TCCR1B = 0; // ...by the Arduino core library
TCNT1 = 0; // reset the timer
OCR1A = PERIOD - 1; // set the period
TIMSK1 = _BV(OCIE1A); // enable TIMERx_COMPA interrupt
TCCR1B |= (1 << CS12); // ...and set the prescaler
interrupts();
Serial.begin(9600);
}
ISR(TIMER1_COMPA_vect)
{
Serial.println(millis());
}
void loop()
{
Serial.println("Program is on");
Serial.flush();
sleepNow();
}
void sleepNow()
{
// Choose our preferred sleep mode:
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
// Set sleep enable (SE) bit:
sleep_enable();
// Put the device to sleep:
sleep_mode();
// Upon waking up, sketch continues from this point.
sleep_disable();
}
1 Answer 1
It is certainly tempting to go into a very deep sleep in order to save
power, but you still need to keep awake whatever peripheral you want to
use as a wakeup source. If you want to use a 16 bit timer, there is no
other choice than SLEEP_MODE_IDLE
.
But it may not be that bad. In IDLE mode you can selectively disable the
clock (and hence put to sleep) the peripherals you do not use. Or maybe
put them all to sleep, and then wake up the few ones you do need. This
is achieved with the power_*_disable()
and power_*_enable()
family
of functions.
In the edited code below: all peripherals but the Timer 1 are disabled while the CPU sleeps. The USART is enabled only for the time it needs to send its message:
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <util/delay.h>
const uint16_t PERIOD = 2150; // 34.4 ms
void setup()
{
// Disable all but the needed peripherals.
power_all_disable();
power_timer1_enable();
// This is for debug pulses.
pinMode(22, OUTPUT);
// Configure Timer 1 to wake us up every 34.4 ms.
TCCR1A = 0; // undo the timer config done...
TCCR1B = 0; // ...by the Arduino core library
TCNT1 = 0; // reset the timer
OCR1A = PERIOD - 1; // set the period
TIMSK1 = _BV(OCIE1A); // enable TIMERx_COMPA interrupt
TCCR1B = _BV(WGM12) // CTC mode, TOP = OCR1A
| _BV(CS12); // ...and set the prescaler /256
Serial.begin(9600);
}
ISR(TIMER1_COMPA_vect)
{
// Send a debug pulse.
digitalWrite(22, HIGH);
_delay_ms(2);
digitalWrite(22, LOW);
}
void loop()
{
// Enable the USART only while needed.
power_usart0_enable();
Serial.println("Program is on");
Serial.flush();
power_usart0_disable();
// And go to sleep in IDLE mode.
sleep_mode();
}
Notice also that the ISR does not print to the serial port: it just
sends a debug pulse to watch with the scope. This should be removed from
production code. The delay is achieved with the avr-libc function
_delay_ms()
because the standard Arduino delay()
would not work: it
relies on Timer 0, which has been disabled.
-
But wouldn't this affect the functionality of the program since you are disabling the peripherals in the start of the program with
power_all_disable();
since I'm not an expert on Arduino couldn't this introduce a whole bunch of new bugs? Lastly, if I am able to use very deep sleep and wake it up with the code I have above what would be the advantage of switching to the less power saving option ofIDLE
mode?Angel Lockhart– Angel Lockhart2016年06月16日 16:06:45 +00:00Commented Jun 16, 2016 at 16:06 -
Also why would using a 16 bit timer force me to use
IDLE
?Angel Lockhart– Angel Lockhart2016年06月16日 16:24:15 +00:00Commented Jun 16, 2016 at 16:24 -
@AngelLockhart: If you want to be on the safe side, you can disable only the peripherals you do not use. The nice thing about the IDLE mode is that you can wake up out of it. ;-) The datasheet has, on section 11.1, a table with the wakeup sources available in each sleep mode. You can see that most peripherals ("Other I/O"), including all the 16-bit timers, are only available in IDLE mode.Edgar Bonet– Edgar Bonet2016年06月16日 17:45:00 +00:00Commented Jun 16, 2016 at 17:45
SLEEP_MODE_PWR_SAVE
.ISR
and re-enters the loop. It just infinently prints out the time. It didn't use to do that before when I used IDLE and Timer 1.ISR
function the program runs just fine.