Go Down

Topic: Watchdog Timer Interrupt and External Interrupt (Read 5594 times) previous topic - next topic

pauldreed

Aah, that makes sense now!
Nick, just checked the Atmel datasheet (page 71), and it appears that INT0 & INT1 will trigger external interrupts.
Michael, INT0 & INT1 correspond to Digital pins 2 & 3, your memory serves you (us) well!

Paul

pauldreed

Despite my previous message, I still can't get it to wake from D3.
I have added;

const int BTN_PIN = 2; //wire a button from pin 2 to ground
const int BTN_PIN3 = 3; //wire a button from pin 3 to ground


and

pinMode(BTN_PIN, INPUT_PULLUP);
pinMode(BTN_PIN3, INPUT_PULLUP);


but, grounding pin 3 does not trigger a extInterrupt.
Any thoughts?

Paul

Nick Gammon

Post the complete code please, in code tags.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

Code: [Select]

const int BTN_PIN = 2; //wire a button from pin 2 to ground
...
pinMode(BTN_PIN, INPUT_PULLUP);


That's wrong, though. Pin 2 is interrupt 0.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

pauldreed


Code: [Select]

const int BTN_PIN = 2; //wire a button from pin 2 to ground
...
pinMode(BTN_PIN, INPUT_PULLUP);


That's wrong, though. Pin 2 is interrupt 0.

Yes, and PIN2 (interrupt 0) does cause a wake up.
It's PIN3 (interrupt 1) that doesn't, and which is causing me difficulties.

The full code is on page 2 of this thread by Jack Christensen.

Paul

Nick Gammon

That code does not define an ISR for INT1_vect.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

This also needs changing:

Code: [Select]

    EICRA = _BV(ISC01);            //configure INT0 to trigger on falling edge
    EIFR = _BV(INTF0);             //ensure interrupt flag cleared
    EIMSK = _BV(INT0);             //enable INT0


Why not use attachInterrupt? That's simpler.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

pauldreed

Nick, I'm punching above my weight here!!
I've just had a look at attachInterrupt and that does not seem straightforward either...
From your tone, I suspect that this would not be easy to implement, so maybe I need to take a step back and try and find a solution using just one pin with interrupt.

Paul

Nick Gammon

#38
Dec 13, 2013, 11:29 pm Last Edit: Dec 13, 2013, 11:47 pm by Nick Gammon Reason: 1
Not at all, it's simpler using that. Here:

Code: [Select]

//Using the watchdog timer, sleep for the given number of intervals,
//then print a message. Prints a dot at each WDT wakeup.
//A button is also connected to provide an external interrupt, print
//a message when the button is pressed.
//Finally, blink an LED after each wake-up regardless of source.
//Using an ATmega328P at 16MHz and 5V, draws ~6.3µA while sleeping, which
//is consistent with only the WDT running.
//
//Jack Christensen 19Nov2013
//CC BY-SA, see http://creativecommons.org/licenses/by-sa/3.0/

#include <avr/wdt.h>
#include <avr/sleep.h>
#include <util/atomic.h>
#include <Streaming.h>            //http://arduiniana.org/libraries/streaming/

const int BTN_PIN = 2;            //wire a button from pin 2 to ground
const int BTN_PIN2 = 3;            //wire a button from pin 3 to ground
const int LED_PIN = 13;           //wire an LED from pin 13 to ground through a proper current-limiting resistor
const int WDT_INTERVALS = 50;     //number of WDT timeouts before printing message
const unsigned long LED_ON_TIME = 200;  //turn on LED for this many ms after each wake-up
const long BAUD_RATE = 115200;

volatile boolean extInterrupt;    //external interrupt flag (button)
volatile boolean wdtInterrupt;    //watchdog timer interrupt flag

void wakeOnInterrupt ()
 {
 extInterrupt = true;
 // don't need the external interrupts any more
 detachInterrupt (0);  
 detachInterrupt (1);  
 }
 
void setup(void)
{
   pinMode(BTN_PIN, INPUT_PULLUP);
   pinMode(BTN_PIN2, INPUT_PULLUP);
   pinMode(LED_PIN, OUTPUT);
   Serial.begin(BAUD_RATE);

   ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
       wdt_reset();
       MCUSR &= ~bit(WDRF);                            //clear WDRF
       WDTCSR |= bit(WDCE) | bit(WDE);                 //enable WDTCSR change
       WDTCSR =  bit(WDIE) | bit(WDP3) | bit(WDP0);    //~8 sec
   }
   Serial << endl << F("Setup complete");
}

void loop(void)
{
   static int wdtCount;
   static int btnCount;

   if (wdtCount == 0 || extInterrupt) Serial << endl << F("Sleep");
   Serial.flush();
   Serial.end();
   gotoSleep();

   digitalWrite(LED_PIN, HIGH);    //blink the LED on each wakeup
   delay(LED_ON_TIME);
   digitalWrite(LED_PIN, LOW);

   Serial.begin(BAUD_RATE);
   if (wdtInterrupt) {
       Serial << '.';
       if (++wdtCount >= WDT_INTERVALS) {
           Serial << endl << F("WDT: ") << wdtCount;
           wdtCount = 0;
       }
   }
   if (extInterrupt) {
       Serial << endl << F("Button: ") << ++btnCount;
   }
}

void gotoSleep(void)
{
   byte adcsra = ADCSRA;          //save the ADC Control and Status Register A
   ADCSRA = 0;                    //disable the ADC
   noInterrupts ();     // timed sequences follow
   EIFR = bit (INTF0);  // clear flag for interrupt 0
   EIFR = bit (INTF1);  // clear flag for interrupt 1
   attachInterrupt (0, wakeOnInterrupt, FALLING);
   attachInterrupt (1, wakeOnInterrupt, FALLING);
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);
   wdtInterrupt = false;
   extInterrupt = false;
   sleep_enable();
   byte mcucr1 = MCUCR | bit(BODS) | bit(BODSE); //turn off the brown-out detector while sleeping
   byte mcucr2 = mcucr1 & ~bit(BODSE);
   MCUCR = mcucr1; //timed sequence
   MCUCR = mcucr2; //BODS stays active for 3 cycles, sleep instruction must be executed while it's active
   interrupts ();      // need interrupts now
   sleep_cpu();                   //go to sleep
   sleep_disable();               //wake up here
   ADCSRA = adcsra;               //restore ADCSRA
}

//handles the Watchdog Time-out Interrupt
ISR(WDT_vect)
{
   wdtInterrupt = true;
}


The wakeOnInterrupt function handles waking on both interrupts.

Compiled and tested OK. Some style changes made (sorry, Jack!).
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

pauldreed


Not at all, it's simpler using that. Here:

Code: [Select]

//Using the watchdog timer, sleep for the given number of intervals,
//then print a message. Prints a dot at each WDT wakeup.
//A button is also connected to provide an external interrupt, print
//a message when the button is pressed.
//Finally, blink an LED after each wake-up regardless of source.
//Using an ATmega328P at 16MHz and 5V, draws ~6.3µA while sleeping, which
//is consistent with only the WDT running.
//
//Jack Christensen 19Nov2013
//CC BY-SA, see http://creativecommons.org/licenses/by-sa/3.0/

#include <avr/wdt.h>
#include <avr/sleep.h>
#include <util/atomic.h>
#include <Streaming.h>            //http://arduiniana.org/libraries/streaming/

const int BTN_PIN = 2;            //wire a button from pin 2 to ground
const int BTN_PIN2 = 3;            //wire a button from pin 2 to ground
const int LED_PIN = 13;           //wire an LED from pin 13 to ground through a proper current-limiting resistor
const int WDT_INTERVALS = 50;     //number of WDT timeouts before printing message
const unsigned long LED_ON_TIME = 200;  //turn on LED for this many ms after each wake-up
const long BAUD_RATE = 115200;

volatile boolean extInterrupt;    //external interrupt flag (button)
volatile boolean wdtInterrupt;    //watchdog timer interrupt flag

void wakeOnInterrupt ()
  {
  extInterrupt = true;
  // don't need the external interrupts any more
  detachInterrupt (0); 
  detachInterrupt (1); 
  }
 
void setup(void)
{
    pinMode(BTN_PIN, INPUT_PULLUP);
    pinMode(BTN_PIN2, INPUT_PULLUP);
    pinMode(LED_PIN, OUTPUT);
    Serial.begin(BAUD_RATE);

    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        wdt_reset();
        MCUSR &= ~bit(WDRF);                            //clear WDRF
        WDTCSR |= bit(WDCE) | bit(WDE);                 //enable WDTCSR change
        WDTCSR =  bit(WDIE) | bit(WDP3) | bit(WDP0);    //~8 sec
    }
    Serial << endl << F("Setup complete");
}

void loop(void)
{
    static int wdtCount;
    static int btnCount;

    if (wdtCount == 0 || extInterrupt) Serial << endl << F("Sleep");
    Serial.flush();
    Serial.end();
    gotoSleep();

    digitalWrite(LED_PIN, HIGH);    //blink the LED on each wakeup
    delay(LED_ON_TIME);
    digitalWrite(LED_PIN, LOW);

    Serial.begin(BAUD_RATE);
    if (wdtInterrupt) {
        Serial << '.';
        if (++wdtCount >= WDT_INTERVALS) {
            Serial << endl << F("WDT: ") << wdtCount;
            wdtCount = 0;
        }
    }
    if (extInterrupt) {
        Serial << endl << F("Button: ") << ++btnCount;
    }
}

void gotoSleep(void)
{
    byte adcsra = ADCSRA;          //save the ADC Control and Status Register A
    ADCSRA = 0;                    //disable the ADC
    noInterrupts ();     // timed sequences follow
    EIFR = bit (INTF0);  // clear flag for interrupt 0
    EIFR = bit (INTF1);  // clear flag for interrupt 1
    attachInterrupt (0, wakeOnInterrupt, FALLING);
    attachInterrupt (1, wakeOnInterrupt, FALLING);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    wdtInterrupt = false;
    extInterrupt = false;
    sleep_enable();
    byte mcucr1 = MCUCR | bit(BODS) | bit(BODSE); //turn off the brown-out detector while sleeping
    byte mcucr2 = mcucr1 & ~bit(BODSE);
    MCUCR = mcucr1; //timed sequence
    MCUCR = mcucr2; //BODS stays active for 3 cycles, sleep instruction must be executed while it's active
    interrupts ();      // need interrupts now
    sleep_cpu();                   //go to sleep
    sleep_disable();               //wake up here
    ADCSRA = adcsra;               //restore ADCSRA
}

//handles the Watchdog Time-out Interrupt
ISR(WDT_vect)
{
    wdtInterrupt = true;
}


The wakeOnInterrupt function handles waking on both interrupts.

Compiled and tested OK. Some style changes made (sorry, Jack!).


That's great Nick, thank you very much!
This project has proved a steep learning curve, so I do appreciate your support.

Paul

Jack Christensen

#40
Dec 14, 2013, 01:56 am Last Edit: Dec 14, 2013, 01:57 am by Jack Christensen Reason: 1

Compiled and tested OK. Some style changes made (sorry, Jack!).


No worries, to each his own! Thanks for getting my back there.

I suppose attachInterrupt() is simpler, but not much. Guess I just like diddling with the registers -- that way if anything goes South, I only have myself to blame. :D
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

pauldreed

#41
Dec 18, 2013, 11:28 pm Last Edit: Dec 18, 2013, 11:36 pm by pauldreed Reason: 1
How long, precisely, is each sleep cycle?
My best guess is about 8.7 seconds, but is there a precise figure which could be used to determine reasonably accurate,  longer time periods by repeating the cycle, such as a re-occurring task exactly every 8hrs.
 

Nick Gammon

I suggest you add a real-time clock chip (for around $1) if you want more accurate time-keeping.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Jack Christensen


How long, precisely, is each sleep cycle?
My best guess is about 8.7 seconds, but is there a precise figure which could be used to determine reasonably accurate,  longer time periods by repeating the cycle, such as a re-occurring task exactly every 8hrs.


Some comments about the accuracy of the WDT:
http://forum.arduino.cc/index.php?topic=201460.msg1484665#msg1484665
http://forum.arduino.cc/index.php?topic=201460.msg1484794#msg1484794
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

pratto

Nick & Jack -

NG reply #38 & JC reply #24

I read this thread with great interest because i have been trying to figure out how to do exactly this for weeks. I have posted threads (questions) on this subject in this forum, and gotten no responses.

And in looking at your codes, I would not have figured it out in this lifetime.  But when i run either code, I get the same error message "in function 'void setup()'  end1 was not declared in this scope".

I am using UNO 1.0.1  I have all of the includes.h

Would either of you take a minute and help me to get your code running . it runs for you, but not for me.

thanks
pat



I

Go Up