Pages: [1] 2 3 4   Go Down
Author Topic: Watchdog Timer Interrupt and External Interrupt  (Read 4072 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

I am trying to created a doorbell type device that will checkin every 4 hours so that I know the batteries are good. The following code seems to work fine separately if I have just the WDT or just the interrupt setup. When both are enabled the external interrupt on  pin 2 seems to hang the device. Are there any issues with running these 2 interrupts together?

Code:
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>


#define PIN_LED 13
#define PIN_WAKE 2


/**
 * @brief   Flag stating if sleep has been entered or not.
 */
volatile bool sleep_entered;

volatile int sleep_cycle = 0;

/**
 * @brief We need to enable the WDT for Interrupt mode only, so we can use it
 *        as a sleep wakeup timer as well as for detecting lockups.
 * @note  Inline function!
 */
inline void configure_wdt(void)
{
    /* A 'timed' sequence is required for configuring the WDT, so we need to
     * disable interrupts here.
     */
    cli();
    wdt_reset();
    MCUSR &= ~_BV(WDRF);
    /* Start the WDT Config change sequence. */
    WDTCSR |= _BV(WDCE) | _BV(WDE);
    /* Configure the prescaler and the WDT for interrupt mode only*/
    WDTCSR =  _BV(WDP0) | _BV(WDP3) | _BV(WDIE);
    sei();
}

/**
 * @brief Configures and enables the watchdog for the specified period. If this
 *        is not required please update the power management module.
 */
void app_sleep_init(void)
{
    /* Setup the flag. */
    sleep_entered = false;
    configure_wdt();
}

void buttonWakeup()        // here the interrupt is handled after wakeup
{
  // execute code here after wake-up before returning to the loop() function
  // timers and code using timers (serial.print and more...) will not work here.
  // we don't really need to execute any special functions here, since we
  // just want the thing to wake up
  loop();
  Serial.println("Button Pressed");
  delay(50);
}
 
/**
 * @brief   Enters the module into low-power sleep mode. Execution is blocked
 *          here until sleep is exited.
 */
void app_sleep_enter(void)
{
    Serial.println("Entering Sleep Cycle: " + String(sleep_cycle));
    delay(50);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_entered = true;
    sleep_enable();
    attachInterrupt(0,buttonWakeup, LOW); // use interrupt 0 (pin 2) and run function
                                       // wakeUpNow when pin 2 gets LOW *ONLY LOW CAN BE USED*
    sei();
    sleep_mode();
    /* Execution will resume here. */
   
   
    sleep_disable();
    if (sleep_cycle < 1800) //Wake up every 1800 cycles ~4 hours
    {
      //Serial.println("Cycle: " + String(sleep_cycle));
      ++sleep_cycle;
      app_sleep_enter();
    }
    else
    {
      sleep_cycle = 0;
      power_all_enable();
    }
   
}


/**
 * @brief   The watchdog interrupt. The watchdog has two functions: 1/ to detect
 *          if the application code has locked up. 2/ to wake up the module
 *          from sleep.
 */
ISR(WDT_vect)
{
   
}


void setup()
{
   pinMode(PIN_WAKE, INPUT);
   pinMode (PIN_LED, OUTPUT);
   
   Serial.begin(9600);
   Serial.println("Starting Setup");
   
   attachInterrupt(0,buttonWakeup, LOW); // use interrupt 0 (pin 2) and run function
                                         // wakeUpNow when pin 2 gets LOW *ONLY LOW CAN BE USED*
 
   app_sleep_init();
   
}  // end of setup

void loop()
{
 
  digitalWrite (PIN_LED, HIGH);  // awake
 
  delay(5000);
  digitalWrite (PIN_LED, LOW);  // asleep
 
  app_sleep_enter();

}  // end of loop
Logged

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4058
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

There should be no issues with those two interrupts.  But, printing and delays should never be used in ISRs.  And I can't imagine why an ISR would call loop().  If that's not what's hanging it, then it's the call to delay().  Much better to just set a flag in the ISR and let code in loop() handle it.  If the delay is a debounce tactic, that can also be handled in loop.  Another approach, disable the interrupt in the ISR, and wait 50ms to enable it again, from loop() of course.

Code:
void buttonWakeup()        // here the interrupt is handled after wakeup
{
    // execute code here after wake-up before returning to the loop() function
    // timers and code using timers (serial.print and more...) will not work here.
    // we don't really need to execute any special functions here, since we
    // just want the thing to wake up
    loop();
    Serial.println("Button Pressed");
    delay(50);
}
« Last Edit: November 17, 2013, 04:10:01 pm by Jack Christensen » Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4058
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
   attachInterrupt(0,buttonWakeup, LOW); // use interrupt 0 (pin 2) and run function
                                       // wakeUpNow when pin 2 gets LOW *ONLY LOW CAN BE USED*

If you're using an ATmega328P (Uno or similar), the datasheet is in error; rising or falling interrupts will wake the MCU.  I've been in contact with Atmel on this issue and they tell me it should be corrected in the next revision of the datasheet.  I might prefer to use an edge-triggered interrupt in this case, as the low-level interrupt triggers continuously as long as the button is held down.
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Global Moderator
Online Online
Brattain Member
*****
Karma: 480
Posts: 18720
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
void buttonWakeup()        // here the interrupt is handled after wakeup
{
   loop();  <----- don't do this
  Serial.println("Button Pressed");  <----- don't do this
  delay(50);  <----- don't do this
}

Apart from that, the function is fine. Oh yes, DO do this:

Code:
  detachInterrupt (0);      // stop LOW interrupt

http://www.gammon.com.au/interrupts
Logged


Global Moderator
Online Online
Brattain Member
*****
Karma: 480
Posts: 18720
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

 I've been in contact with Atmel on this issue and they tell me it should be corrected in the next revision of the datasheet.

Did they really say that? Did you put that in the thread about this issue (wherever that is)?
Logged


Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4058
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I've been in contact with Atmel on this issue and they tell me it should be corrected in the next revision of the datasheet.

Did they really say that? Did you put that in the thread about this issue (wherever that is)?

Yes, they said next datasheet update.  Didn't update the thread.  I asked them one more very direct question just so there was no doubt, didn't get a reply to that.  And I probably don't have the emails any longer.  Although I think it was a ticket on their site so the whole conversation is there I hope.

My theory is that someone copied/pasted the wrong text regarding interrupts (likely from some ATtiny).  I checked several other ATmega MCUs, they all say they will wake on edge-triggered external interrupts.
« Last Edit: November 17, 2013, 05:58:02 pm by Jack Christensen » Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4058
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Found the email, updated the thread regarding the interrupt.
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Awesome! I updated the ISR and removed serial print and delay. Also, calling loop was just from my testing.

 The problem I am now running into is that the interrupt is now just incrementing the sleep cycle. Would I just set a flag when the the first interrupt is triggered that would bypass the counter and run the loop?
Logged

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4058
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Awesome! I updated the ISR and removed serial print and delay. Also, calling loop was just from my testing.

 The problem I am now running into is that the interrupt is now just incrementing the sleep cycle. Would I just set a flag when the the first interrupt is triggered that would bypass the counter and run the loop?

Post the current code, please?
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is the current code. I am thinking I need to add a statement that skips the counting logic.
Also, Serial.print('Running Loop'); prints to the console as "28528". Is there some other type of encoding that needs to be used?

Code:
#include <avr/wdt.h>
//#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>

#define PIN_LED 13
#define PIN_WAKE 2

/**
 * @brief   Flag stating if sleep has been entered or not.
 */
volatile bool sleep_entered;
volatile bool interrupt1 = false;
volatile bool interrupt2 = false;
volatile int sleep_cycle = 0;

/**
 * @brief We need to enable the WDT for Interrupt mode only, so we can use it
 *        as a sleep wakeup timer as well as for detecting lockups.
 * @note  Inline function!
 */
inline void configure_wdt(void)
{
    /* A 'timed' sequence is required for configuring the WDT, so we need to
     * disable interrupts here.
     */
    cli();
    wdt_reset();
    MCUSR &= ~_BV(WDRF);
    /* Start the WDT Config change sequence. */
    WDTCSR |= _BV(WDCE) | _BV(WDE);
    /* Configure the prescaler and the WDT for interrupt mode only*/
    WDTCSR =  _BV(WDP0) | _BV(WDP3) | _BV(WDIE);
    sei();
}

/**
 * @brief Configures and enables the watchdog for the specified period. If this
 *        is not required please update the power management module.
 */
void app_sleep_init(void)
{
    /* Setup the flag. */
    sleep_entered = false;
    configure_wdt();
}

void buttonWakeup()        // here the interrupt is handled after wakeup
{
  // execute code here after wake-up before returning to the loop() function
  // timers and code using timers (serial.print and more...) will not work here.
  // we don't really need to execute any special functions here, since we
  // just want the thing to wake up
  interrupt1 = true;
}
 
/**
 * @brief   Enters the module into low-power sleep mode. Execution is blocked
 *          here until sleep is exited.
 */
void app_sleep_enter(void)
{
    Serial.println("Entering Sleep Cycle: " + String(sleep_cycle));
    delay(25);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_entered = true;
    interrupt1 = false;
    interrupt2 = false;
    sleep_enable();
    attachInterrupt(0,buttonWakeup, LOW); // use interrupt 0 (pin 2) and run function
                                       // wakeUpNow when pin 2 gets LOW *ONLY LOW CAN BE USED*
    sei();
    sleep_mode();
    /* Execution will resume here. */
   
    Serial.println("Interrupt1: " + String(interrupt1));
    delay(25);
   
    sleep_disable();
    if (sleep_cycle < 8)
    {
      //Serial.println("Cycle: " + String(sleep_cycle));
      ++sleep_cycle;
      app_sleep_enter();
    }
    else
    {
      sleep_cycle = 0;
      power_all_enable();
    }
   
}


/**
 * @brief   The watchdog interrupt. The watchdog has two functions: 1/ to detect
 *          if the application code has locked up. 2/ to wake up the module
 *          from sleep.
 */
ISR(WDT_vect)
{
   
}


void setup()
{
   pinMode(PIN_WAKE, INPUT);
   pinMode (PIN_LED, OUTPUT);
   
   Serial.begin(9600);
   Serial.println("Starting Setup");
   
   attachInterrupt(0,buttonWakeup, LOW); // use interrupt 0 (pin 2) and run function
                                         // wakeUpNow when pin 2 gets LOW *ONLY LOW CAN BE USED*
 
   app_sleep_init();
   
}  // end of setup

void loop()
{
  Serial.print('Running Loop');
  digitalWrite (PIN_LED, HIGH);  // awake
 
  delay(5000);
  digitalWrite (PIN_LED, LOW);  // asleep
 
  app_sleep_enter();

}  // end ofloop
Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 480
Posts: 18720
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Also, Serial.print('Running Loop'); prints to the console as "28528". Is there some other type of encoding that needs to be used?

http://www.gammon.com.au/forum/?id=12153#trap22
Logged


Global Moderator
Online Online
Brattain Member
*****
Karma: 480
Posts: 18720
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The problem I am now running into is that the interrupt is now just incrementing the sleep cycle.

Again:

Oh yes, DO do this:

Code:
  detachInterrupt (0);      // stop LOW interrupt

http://www.gammon.com.au/interrupts
Logged


Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4058
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Couple more questions.

1. Is there an external pullup resistor on the button?

2. Do I understand the intended operation correctly: Illuminate the LED for 5 sec, then sleep the MCU.  Wake when the button is pushed OR when the WDT interrupt occurs (8 sec).

And a couple observations:

Access to variables shared by an ISR must be atomic.  For instance, sleep_cycle is a 2-byte int so it could become corrupted if the mainline code (i.e. loop or app_sleep_enter or whatever) accesses it while the ISR is updating it.  I usually inhibit interrupts, copy the variable of interest to another variable, enable interrupts, then work with the copy.  AVR Libc has ATOMIC_BLOCK macros to facilitate this.

String variables are evil and will chew up all your SRAM when you're not looking.  They're actually probably OK in this sketch, but I highly recommend against using them.



Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4058
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Did you mean trap #23?
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Global Moderator
Online Online
Brattain Member
*****
Karma: 480
Posts: 18720
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

No, #22.

Code:
Serial.print('Running Loop');

Should be double quotes.
Logged


Pages: [1] 2 3 4   Go Up
Jump to: