Which external interrupt to wake 328P?

Hoping someone can point out where I'm misinterpreting or making an error here. I was innocently reading the datasheet when I realized that some code I had previously written shouldn't work.

From the following sections of the datasheet, my interpretation is that an external interrupt (INT0 or 1, I'm using INT1) will only wake the MCU from power-down mode when configured for a level-based interrupt, not a falling or rising edge. And here's the problem, my code had INT1 configured for a falling edge and it was working. See the test code below, it works either for a low-level interrupt (ISC11=ISC10=0) or for falling edge (ISC11=1, ISC10=0).

Thanks in advance. Not sure which is worse, something that should work but doesn't, or something that shouldn't work but does :astonished:

Notice Note (3):

Note that edge-triggered interrupts require clkIO, which is inactive in power-down mode:

/*----------------------------------------------------------------------*
 * Sleep demo for ATmega328P.                                           *
 * Wire a button from pin D2 (INT0) to ground.                          *
 * Wire an LED with an appropriate dropping resistor from pin D13 to    *
 * ground.                                                              *
 * Pushing the button wakes the MCU.                                    *
 * After waking, the MCU flashes the LED, then waits 10 seconds before  *
 * going back to sleep.                                                 *
 *                                                                      *
 * Jack Christensen 07May2013                                           *
 *                                                                      *
 * Developed with Arduino 1.0.4 and an Arduino Uno.                     *
 * Test conditions for all results below:                               *
 *   5V regulated power supply, fed to the Vin pin                      *
 *   16MHz system clock                                                 *
 *   Fuse bytes (L/H/E): 0xFF / 0xDE / 0x05                             *
 *   Optiboot bootloader                                                *
 *                                                                      *
 * Uno R1                                                               *
 *   38mA active, 26mA with MCU in power-down mode.                     *
 *                                                                      *
 * Uno SMD edition                                                      *
 *   42mA active, 31mA power-down.                                      *
 *                                                                      *
 * Adafruit Boarduino                                                   *
 *   Power select jumper set to "USB", USB (FTDI) not connected.        *
 *   15mA active, 3mA power-down.                                       *
 *                                                                      *
 * Adafruit Boarduino without power LED                                 *
 *   12mA active, 0.1µA power-down.                                     *
 *                                                                      *
 * Breadboarded ATmega328P-PU                                           *
 *   12mA active, 0.1µA power-down.                                     *
 *                                                                      *
 * This work is licensed under the Creative Commons Attribution-        *
 * ShareAlike 3.0 Unported License. To view a copy of this license,     *
 * visit http://creativecommons.org/licenses/by-sa/3.0/ or send a       *
 * letter to Creative Commons, 171 Second Street, Suite 300,            *
 * San Francisco, California, 94105, USA.                               *
 *----------------------------------------------------------------------*/ 
 
#include <avr/sleep.h>

#define LED LED_BUILTIN            //LED on pin 13
#define KEEP_RUNNING 10000         //milliseconds

void setup(void)
{
    //to minimize power consumption while sleeping, output pins must not source
    //or sink any current. input pins must have a defined level; a good way to
    //ensure this is to enable the internal pullup resistors.

    for (byte i=0; i<20; i++) {    //make all pins inputs with pullups enabled
        pinMode(i, INPUT_PULLUP);
    }

    pinMode(LED, OUTPUT);          //make the led pin an output
    digitalWrite(LED, LOW);        //drive it low so it doesn't source current
}

void loop(void)
{
    goToSleep();
    for (byte i=0; i<5; i++) {     //flash the LED
        digitalWrite(LED, HIGH);
        delay(100);
        digitalWrite(LED, LOW);
        delay(100);
    }
    delay(KEEP_RUNNING);           //opportunity to measure active supply current 
}

void goToSleep(void)
{
    byte adcsra, mcucr1, mcucr2;

    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();

//  >>>--> either of the following two lines work!
    EICRA = _BV(ISC11);            //interrupt on falling edge
//    EICRA = 0x00;                  //interrupt on low level

    EIFR = _BV(INTF1);             //ensure interrupt flag is cleared (setting ISCnn can cause an interrupt)
    EIMSK = _BV(INT1);             //enable INT1
    adcsra = ADCSRA;               //save the ADC Control and Status Register A
    ADCSRA = 0;                    //disable ADC
    cli();                         //stop interrupts to ensure the BOD timed sequence executes as required
    mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE);  //turn off the brown-out detector while sleeping
    mcucr2 = mcucr1 & ~_BV(BODSE);
    MCUCR = mcucr1;                //timed sequence
    MCUCR = mcucr2;                //BODS stays active for 3 cycles, sleep instruction must be executed while it's active
    sei();                         //ensure interrupts enabled so we can wake up again
    sleep_cpu();                   //go to sleep
    sleep_disable();               //wake up here
    ADCSRA = adcsra;               //restore ADCSRA
}

//external interrupt 1 wakes the MCU
ISR(INT1_vect)
{
    EIMSK = 0;                     //disable external interrupts (only need one to wake up)
}

Table 10-1 Note 3 says Level for INT0, 1. That's what I have used.
Maybe your Falling was interpreted as a Level eventually.

CrossRoads:
Maybe your Falling was interpreted as a Level eventually.

I was thinking that with clkIO shut down, it would not be possible to detect a falling edge, so the interrupt should not trigger. And even though the level is held low after the falling edge, the interrupt is not configured to respond to that.

I did some more testing and found that any of the four possible ISC11/ISC10 configurations wake the MCU with the test code.

I can reproduce your results, but not explain them. :slight_smile:

I appreciate that! :smiley:

Further testing reveals that not only does the processor wake up, but it executes the ISR. I had thought maybe it would wake but realize that the "falling" condition was not satisfied.

It's hard to know where to go. Do we have faulty chips? (both of us)

Is the documentation wrong? It seems pretty explicit that it shouldn't be waking.

Jack seems to be finding a lot of weird stuff lately.

Thanks again, I did not actually test that.

It's hard to know where to go. Do we have faulty chips? (both of us)

That's always last on my list. In my case it would be multiple chips, both -AU and -PU packages, so I'd have to rule it out completely. (My chips came from Mouser, not eBay or whatever.)

Is the documentation wrong? It seems pretty explicit that it shouldn't be waking.

Maybe I should ask Atmel!

LarryD:
Jack seems to be find a lot of weird stuff lately.

I've been accused of that before! :smiley: :smiley:

Please do. My experience is the bigger the company, the more they ignore technical queries.

My guess is that the hardware must have some sort of "wake up when the pin changes" built into it, otherwise how do pin change interrupts work? Even with the clock off. However that contradicts the datasheet.

my code had INT1 configured for a falling edge

If the i/p bounces what would happen?

Yes I suppose it is hard to get a single contact when touching wires together, but still it shouldn't have fired if it was configured for falling (or rising, or change) when it isn't supposed to.

LarryD:

my code had INT1 configured for a falling edge

If the i/p bounces what would happen?

I suppose we get some extra edges for free :wink: I was using a tactile button switch in my test circuit so it was almost certainly bouncing, although note that the first (and only) thing the ISR does is to disable the interrupt. In my real application circuit, the signal is the alarm from an RTC so should be a clean transition. In both circuits the MCU wakes with the interrupt configured for falling edge.

Update: I did contact Atmel, this was keeping me up nights. They're looking for an ATmega328P to test the code on :astonished: In the meantime, in reading datasheets for other ATmega MCUs, I see that some explicitly state that edge-triggered interrupts are detected asynchronously without the I/O clock, and so will wake the MCU from power-down. Depending on the MCU, this does not necessarily apply to all external interrupts.

So this may be a documentation issue. I hope so because I like the ability to wake the MCU with edge-triggered interrupts.

I also experimented with a couple ATtinys (45, 84) and edge-triggered interrupts do not wake the MCU from power-down, but this behavior matches their datasheets.

Should be interesting. Hope to have more soon.

Perhaps they should contact the supplier. :stuck_out_tongue:

Request a free sample? See if it makes it past the sample-approvers.

explicitly state that edge-triggered interrupts are detected asynchronously without the I/O clock, and so will wake the MCU from power-down.

I believe this is usually done using an edge-triggered flip-flop that does not have a connection from the system clock, so it will trigger at any time at all, and hold its output until reset - usually by the ISR clearing it in s.w.

Jack, I like your code [will try it myself since I am setting up some battery-operated nodes], but FWIW, I don't think this would work properly with Mega chips, for reasons brought out on other threads recently [ie, interrupts being remapped on those chips],

    EIMSK = _BV(INT1);             //enable INT1

oric_dan:
Jack, I like your code [will try it myself since I am setting up some battery-operated nodes], but FWIW, I don't think this would work properly with Mega chips, for reasons brought out on other threads recently [ie, interrupts being remapped on those chips],

    EIMSK = _BV(INT1);             //enable INT1

@oric_dan, give me a little more on that please, or maybe links to some of those other threads, I'm not following...

Check reply #2, etc,
http://forum.arduino.cc/index.php?topic=187600.0

See also the final note on Nick's page,

Reply #8 on Sat 27 Jul 2013 12:41 AM (UTC)
Interrupt names to pin mappings

Ah OK, thanks. I wasn't concerned with portability between MCUs or pin mapping. I'm strictly working with the 328P. For whatever reason, I never much got into using Arduino's interrupt functions, I prefer to just code the ISRs and set the registers as needed.

I you do try that code, I'd be very interested in your results. Nick was able to duplicate my results, and I have informed Atmel that my results were independently verified by one person, but the more the merrier. Thanks!

Yeah, I just thought I'd mention it. If I saw your sketch, and I were using a Mega board, I'd immediately try it of course, since Arduino code is reputed to be so "transportable" across processors, lol. Now, back to work.