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 :smiley-eek:
Notice Note (3):
(https://lh6.googleusercontent.com/--ynpOnSqFLw/UfWg5ngiU8I/AAAAAAAAJEc/3LYFDmunXHk/s800/sec_10_1.png)
Note that edge-triggered interrupts require clkIO, which is inactive in power-down mode:
(https://lh5.googleusercontent.com/-IgllOb7OF88/UfWjkr5x0LI/AAAAAAAAJEw/AM0qtUmFgTo/s800/sec_13.png)
(https://lh5.googleusercontent.com/-yLDzPG08KWI/UfWjlnftBeI/AAAAAAAAJE4/sGWpKc3754w/s800/sec_13_2.png)
/*----------------------------------------------------------------------*
* 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.
Maybe your Falling was interpreted as a Level eventually.
I was thinking that with clk
IO 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. :)
I can reproduce your results, but not explain them. :)
I appreciate that! :D
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.
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.
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!
Jack seems to be find a lot of weird stuff lately.
I've been accused of that before! :D :D
Maybe I should ask Atmel!
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.
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 ;) 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 :smiley-eek: 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.
They're looking for an ATmega328P to test the code on ...
Perhaps they should contact the supplier. :P
They're looking for an ATmega328P to test the code on ...
Perhaps they should contact the supplier. :P
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
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,
http://www.gammon.com.au/forum/?id=11488
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.
Atmel has reproduced the behavior, and has escalated the issue. I asked whether it might just be a documentation error (reply #12 above).
Good catch there Jack. :-)
Atmel has reproduced the behavior, and has escalated the issue. I asked whether it might just be a documentation error (reply #12 above).
So that's Jack, myself, and Atmel. :)
Well at least we aren't going mad.
Well at least we aren't going mad.
I know! That's what I always think in these situations! :D
The final word from Atmel:
From: <avr@atmel.com>
Date: Fri, Oct 11, 2013 at 1:04 AM
Subject: (ATTicket:751590) External interrupt to wake ATmega328P from power-down
To: jack.christensen
Dear Jack,
You are correct, both edge and level based interrupt are waking up the device and the issue have verified with the internal resource. As you mention this has to be updated in the document and this will be updated in the upcoming release.
Thanks for your valuable inputs.
Sorry for the delay in response and Kevin is on leave so the ticket has assinged to me
Best Regards,
Vincent Christopher
Atmel Technical Support Team
You are correct, both edge and level based interrupt are waking up the device and the issue have verified with the internal resource.
It's strangely vague about whether this is by design, or just happens on the chips that are tested.
It would be nice if they had said it is designed to do it.
You are correct, both edge and level based interrupt are waking up the device and the issue have verified with the internal resource.
It's strangely vague about whether this is by design, or just happens on the chips that are tested.
It would be nice if they had said it is designed to do it.
I thought the same thing which is why I asked one final question, that was not answered (below). Assuming they were diligent about the whole thing, and also assuming that the update to the datasheet will make it read like the other ATmega datasheets, then I can only conclude that the observed behavior is by design.
From: Jack Christensen
Date: Fri, Oct 11, 2013 at 7:54 AM
Subject: Re: (ATTicket:751590) External interrupt to wake ATmega328P from power-down
To: avr@atmel.com
Hello Vincent,
No problem, thanks for the update. Just to be clear, the device is operating as designed, and the issue was with the documentation, correct?
Best regards,
Jack Christensen
Ticket Status: Confirmed Bug Feature
Ticket Status: Confirmed Bug Feature
I really do think the chip is working as designed, and it was just a documentation error.
What was that old saying? Something like "A bug is a problem with the code, an error is a problem with the documentation." ??
I have been searching this forum / other forums for an explanation to my findings on m328p, and finally I see this thread. Thanx Jack!
I can confirm wake-up of m328p from power-down on rising/falling edge on INT0 pin. I used a minimalist setup running with 8Mhz internal oscillator on a breadboard, and the triggering edge was generated by a master m328p. After the wake-up, the slave m328p sent a digital signal back to the master that measured the start-up delay to be max 90 microseconds (BOD was disabled on power-down, and according to atmel's documentation this creates a start-up delay of 60us)
I also measured the supply current to the slave m328. In active mode current was 6-7mA, and on entering sleep the current drop to 0.2uA, which confirms that m328p entered power-down mode.
Atmel has reproduced the behavior, and has escalated the issue. I asked whether it might just be a documentation error (reply #12 above).
Have you got a link to your report, Jack? I submitted my own one yesterday and got this response (the implication being it was news to them) :
Commented by Manoraj Gnanadhas (Atmel)
2015-01-08 11:27:32 GMT
[Recipients: Nick Gammon]
Hello Nick,
Thank you for contacting Atmel Technical Support.
It looks like a mistake in the datasheet. We tested here and also able to reproduce the same behavior here. If you refer "Section - 13. External Interrupts (Page-70)", you can read the following information.
"Low level interrupts and the edge interrupt on INT2:0 are detected asynchronously. This implies that these interrupts can be used for waking the part also from sleep modes other than Idle mode."
This is contradicting with the 'Note-3' mentioned under Table 10-1. Therefore I need to confirm this with the concerned team. I will get back to you once I receive any valid update from the design team.
Best Regards,
Manoraj Gnanadhas
I got this confirmation today that the datasheet was wrong:
Commented by Manoraj Gnanadhas (Atmel)
2015-01-20 06:23:36 GMT
(Recipients: Nick Gammon)
Hello Nick,
Our design team has confirmed that "Note-3 mentioned under Table 10-1" is a datasheet bug. So you can use any type of interrupt (Rising edge/ Falling edge / Low level / Any logical change) to wake up from sleep mode. Sorry for the inconvenience caused.
Best Regards,
Manoraj Gnanadhas
Well done for getting that out of them Nick!
Thanks! I've amended my Interrupts page (http://www.gammon.com.au/interrupts) to mention this altered information.
Good, because that is where I would look if I were checking!
It is also good to note that almost any form of interrupt would work.
Humm,
This is an interessting post, to be honest, I was using interrupt like that (and worked) without really have read the datasheet on this point. It looked to me so logical (and worked) that I missed this one, good catch.
By the way, while on interrupt post, I would like if possible to ask you guys on your high level of expertise on interrupt. I read all Nick dedicated post about interrupt on his forum (my godness this site is my most technical reference bible) and still have a interrogation due to strange thing happening on my ULPNode (http://hallard.me/category/ulpnode/)prototypes with IRQ and sleep mode.
Let's say I have this code (I simplified it from my lib)
// blah blah configure IRQ that could wake us (in my case INT0 and a Pin change)
volatile uint8_t counter = 0;
// we don't want to be waked by the watchdog, so be
// sure to disable it by changing the config
WDTCSR = _BV(WDCE) | _BV(WDE);
WDTCSR = 0;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
// turn off BOD (sequenced order see datasheet)
// Don't change anything before we're waked
MCUCR = _BV(BODS) | _BV(BODSE);
MCUCR = _BV(BODS);
sei();
sleep_cpu();
// ...........
// ZZZZZZZZZZZ
// ...........
// Waked !!!
counter++;
nop(); // be sure to avoid compiler optimization changing 3*+1 counter increment to 1*3
counter++;
nop(); // be sure to avoid compiler optimization changing 3*+1 counter increment to 1*3
counter++;
I think while reading this code, your mind is already anticipating my question ;)
So my question is : what is the 1st instruction executed after wake up ? If I check counter in my IRQ which should have been the 1st instruction executed (well after the IRQ push stack overhead of course) I should always read it to 0 or is there a change that the IRQ trigger was delayed due to wake up and counter incremented before my IRQ was triggered ?
Really thanks for your help
So my question is : what is the 1st instruction executed after wake up ?
Call to the interrupt service routine vector which is nearly always a jump to the interrupt service routine. In your case, the vector for the watchdog.
Hey,
Thank you for your answer, that's what I thought, but sometimes, I saw strange things just like if my code was executed just before entering into my IRQ (the one who wake up me). But I have so much IRQ stuff in my project that it can be another thing. I think I will need to isolate one and write a small sketch with only this config and one IRQ. Then stress it with a switch push button to verify.