Hello,
I've changed the title of this post to reflect the actual issue.
The now interesing part starts with contribution #8 (scroll down).
I'm working on a project using the Arduino Pro Minit (ATmega328). I've stripped the program down to the essential part to understand the problem.
The program uses two interrupt sources to wake up from sleep: external interrupt INT0 and TMR2 interrupt. Basically this works but...
When entering the main loop, the program goes to sleep with the external interrupt enabled only. When the interrupt occurs it wakes up and enables the TMR2 interrupt + said external interrupt before it goes to sleep again.
But the TMR2 interrupt doesn't occur and the microcontroller doesn't wake up from it.
Another external interrupt causes a wakeup and the TMR2 interrupt is enabled again. This time it runs as expected and causes a wakeup.
From then on the device wakes up every time the TMR2 interrupt is used as a wakeup source.
My question: Why doesn't the TMR2 interrupt work when it is enabled first?
What I have tried so far:
- I've reordered the setup of TMR2 as this might cause problems otherwise.
- When changing the initial interrupt source in main() from EXTERNAL_INTERRUPT to TIMEOUT_INTERRUPT the timer interrupt works even the first time
- I've added some debug output to see what's going on.
Here's the complete code:
#include <avr/sleep.h>
// **************** Definitions ****************
// Push Button
#define BUTTON_INT 2
#define BUTTON 7
// Timer
#define TIMER_INTERVAL 3
// **************** Global Variables ****************
volatile bool buttonPress = false;
unsigned int timeoutSeconds = 0;
volatile bool timeoutFlag = false;
enum wakeupSources
{
EXTERNAL_INTERRUPT,
TIMEOUT_INTERRUPT
};
// **************** Sleep and Wakeup, Interrupts and Timer2 ****************
// Interrupt INT0 for push button
void ISR_BUTTON(void)
{
detachInterrupt(digitalPinToInterrupt(BUTTON_INT));
buttonPress = true;
Serial.print("iB"); Serial.flush(); // DEBUG
}
// Interrupt Timer2
ISR(TIMER2_COMPA_vect)
{
static unsigned long counter = 0;
counter++;
if (counter >= ((unsigned long)(timeoutSeconds) * 40UL))
{
timeoutFlag = true;
counter = 0;
TIMSK2 = 0; // disable interrupt
}
if (counter & 0x02) Serial.print("."); Serial.flush(); // DEBUG (only show every 4th occurence)
return;
}
// Sleep
void enterSleep(wakeupSources source, unsigned int timeout_sec)
{
Serial.print("\r\nenter sleep, wait for ");
if (source == EXTERNAL_INTERRUPT)
{
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // wakeup by ext. interrupt + WDT
Serial.print("EXT INT... ");
buttonPress = false;
}
else if (source == TIMEOUT_INTERRUPT)
{
set_sleep_mode(SLEEP_MODE_PWR_SAVE); // also wakeup by TMR2
Serial.print("TMR INT... ");
// Setup TMR2: interrupt every 25ms
TCCR2B = 0; // set TCCRXB register to 0
TCCR2A |= (1 << WGM21); // enable timer2 CTC mode
OCR2A = 194; // set compare match register of timer 2
TCNT2 = 0;
TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20); // 1:1024 prescaling for timer 2
TIMSK2 |= (1 << OCIE2A); // enable timer2 compare interrupt
timeoutSeconds = timeout_sec;
timeoutFlag = false;
}
Serial.flush();
EIFR |= (1 << INTF0); // clear pending external interrupts
sleep_bod_disable();
do // loops when timer interrupts
{
cli();
attachInterrupt(digitalPinToInterrupt(BUTTON_INT), ISR_BUTTON, LOW); // power down accepts level-interrupts only
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
Serial.flush();
}
while ( (buttonPress == false) && (timeoutFlag == false));
Serial.println("-Wakeup");
Serial.flush();
return;
}
// **************** Setup ****************
void setup(void)
{
Serial.begin(115200);
Serial.println();
Serial.println("\r\nHello!");
// Button
pinMode(BUTTON_INT, INPUT); // external pull-Up
pinMode(BUTTON, INPUT);
}
// **************** Main ****************
void loop(void)
{
enum wakeupSources wakeupSource;
unsigned int wakeupTimeout;
wakeupSource = EXTERNAL_INTERRUPT; // initial wakeup source
// -------- Push Button
if (buttonPress == true)
{
buttonPress = false;
Serial.println("button");
while(digitalRead(BUTTON) == LOW)
delay(100);
wakeupSource = TIMEOUT_INTERRUPT;
wakeupTimeout = TIMER_INTERVAL;
}
// -------- Timeout
if (timeoutFlag == true)
{
static unsigned char timeoutCount = 0;
timeoutFlag = false;
timeoutCount++;
Serial.print("timeout #"); Serial.println(timeoutCount);
if (timeoutCount <= 2)
{
wakeupSource = TIMEOUT_INTERRUPT;
wakeupTimeout = TIMER_INTERVAL;
}
else
{
timeoutCount=0;
wakeupSource = EXTERNAL_INTERRUPT;
}
}
// -------- Sleep
enterSleep(wakeupSource, wakeupTimeout);
};
This is the debug output:
Hello!
enter sleep, wait for EXT INT... iB-Wakeup
button
enter sleep, wait for TMR INT... iB-Wakeup
button
enter sleep, wait for TMR INT... ............................................................-Wakeup
timeout #1
enter sleep, wait for TMR INT... ............................................................-Wakeup
timeout #2
enter sleep, wait for TMR INT... ............................................................-Wakeup
timeout #3
enter sleep, wait for EXT INT...
It can be seen that first the external interrupt is used and the device wakes up from it.
Then the timer interrupt is enabled but never executed. Instead the wakeup is triggered manually by another external interrupt.
Then the timer interrupt is enabled again but this time it is actually executed.
It would be great if someone could point out what I am missing.
Thank you very much!
Cheers
Arlenne