Go Down

Topic: Watchdog Timer Interrupt and External Interrupt (Read 5 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.
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.
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.
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.
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!).
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.
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