I have an ATMega324PA and I want to power down and only wake on CAN receive (working on int 0 successfully), on a schedule, on pcints on 6 digital inputs, and on serial receive - either serial0 or serial1.
I have read Nick Gammon's forum post and have tried to adapt his code to my 324. It will go into sleep, wake on a digital input but then stays awake. I've obviously got something wrong. Also I'm not clear on how to wake on serial. This is some basic test code, the CAN code is not included.
/*
ATMega324PA
*/
#include <avr/sleep.h> // Sleep Modes
#include <avr/power.h> // Power management
#include <avr/wdt.h> // Watchdog timer
const byte LED1 = 18; // pin DO1
const byte DI1 = 28; // pin DI1 / PCINT4
const byte DI2 = 29; // pin DI2 / PCINT5
const byte DI3 = 30; // pin DI3 / PCINT6
const byte DI4 = 31; // pin DI4 / PCINT7
const byte DI5 = 22; // pin DI5 / PCINT22
const byte DI6 = 23; // pin DI6 / PCINT23
ISR(PCINT0_vect) {
PCICR = 0;
}
ISR(PCINT1_vect) {
PCICR = 0;
}
ISR(PCINT2_vect) {
PCICR = 0;
}
// watchdog interrupt
ISR(WDT_vect) {
wdt_disable(); // disable watchdog
} // end of WDT_vect
// sleep bit patterns for WDTCSR
enum {
WDT_16_MS = 0b000000,
WDT_32_MS = 0b000001,
WDT_64_MS = 0b000010,
WDT_128_MS = 0b000011,
WDT_256_MS = 0b000100,
WDT_512_MS = 0b000101,
WDT_1_SEC = 0b000110,
WDT_2_SEC = 0b000111,
WDT_4_SEC = 0b100000,
WDT_8_SEC = 0b100001,
}; // end of WDT intervals enum
void resetWatchdog() {
// 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) | WDT_8_SEC; // set WDIE, and 8 seconds delay
// pat the dog
wdt_reset();
} // end of resetWatchdog
void setup() {
// disable JTAG for pins 18 to 21
MCUCR |= (1 << JTD);
MCUCR |= (1 << JTD);
resetWatchdog(); // do this first in case WDT fires
pinMode(LED1, OUTPUT);
pinMode(DI1, INPUT_PULLUP);
pinMode(DI2, INPUT_PULLUP);
pinMode(DI3, INPUT_PULLUP);
pinMode(DI4, INPUT_PULLUP);
pinMode(DI5, INPUT);
pinMode(DI6, INPUT);
// pin change interrupt masks
PCMSK2 |= bit(PCINT22); // pin 22
PCMSK2 |= bit(PCINT23); // pin 23
PCMSK0 |= bit(PCINT4); // pin Din1
PCMSK0 |= bit(PCINT5); // pin Din2
PCMSK0 |= bit(PCINT6); // pin Din3
PCMSK0 |= bit(PCINT7); // pin Din4
// clear any outstanding interrupts
PCIFR |= bit(PCIF0) | bit(PCIF1) | bit(PCIF2);
// enable pin change interrupts
PCICR |= bit(PCIE0) | bit(PCIE1) | bit(PCIE2);
} // end of setup
void loop() {
digitalWrite(LED1, HIGH);
delay(500);
digitalWrite(LED1, LOW);
delay(500);
goToSleep();
} // end of loop
void goToSleep() {
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
ADCSRA = 0; // turn off ADC
power_all_disable(); // power off ADC, Timer 0 and 1, serial interface
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