attachInterrupt ATmega2560 not working

Hi everyone!
As the title suggests, interrupt does not work.
I connect the interrupt on the “flussostato” pin 2 before going to sleep.
But if I physically “put down” (GND) the pin, it doesn’t wake up.

In setup()

pinMode(flussostato, INPUT_PULLUP);

In enterSleep()

......
attachInterrupt(digitalPinToInterrupt(flussostato), flussostatoWakeUp, FALLING);
........

I thought maybe they need libraries on purpose but I don’t know.
Do you have any information for me?

Thank you

What pin number is flussostato? If it isn’t one of the external interrupt pins int0 through int7 then the call to digitalPinToInterrupt will silently fail.

The intx pins are:

D18-D21 = int0-int3 (EDIT Fixed Arduino pin names!)
D2-D3 = int4-int5

Please note, the data sheet mentions 8 int pins but none of the pinout diagrams for Arduino show more than 6!

Cheers,
Norm.

Ok, I found out that D12 and D13 are int6 and int7.

Cheers,
Norm.

The pin definition is

#define FLUSSOSTATO 2

Maybe it’s because it doesn’t have to be a define?

And if you don’t do that it works?

But your original code says:

So I’m surprised you didn’t get a compilation error, unless flussostato is also a variable elsewhere in the code?

When you set up the interrupt:

EDITED To correct details of wake up calls! See italicised text.

You specify a FALLLING edge. Unless you are using Idle Sleep Mode, the lightest of sleep modes, then your interrupt will not wake the Mega2560 unless the interrupt is on pins INT0, INT1, INT2 or INT3 which can wake any sleep mode. INT4, INT5, INT6 and INT7 can only wake non-Idle sleep modes with a LOW interrupt.. The data sheet explains (page 50) in the “Sleep Modes” section, that all other sleep modes require a level interrupt if you are using INT4, INT5, INT6 or INT7, so you cannot use FALLING, you have to use LOW instead.

A LOW trigger will mean that the interrupt service routine, flussostatoWakeUp, will execute while the button/pin is held LOW. It’s not a one-off trigger like the edge triggers, RISING, FALLING, TOGGLE are. So, for example, if your function toggles an LED when it gets an interrupt, it will fire repeatedly, all the time the pin, it is attached to, is held LOW. Probably not what you want. From the data sheet:

If enabled, a level triggered interrupt will generate an interrupt request as long as the pin is held low.

Also, it’s worth noting that as you are using the Arduino language, if you do use Idle sleep mode, then it will wake up every millisecond or so, as the Timer 0 overflow interrupt is used to update the millis and micros counter. So, that particular sleep mode, in an Arduino environment, is pretty useless I’m afraid.

In summary, as I tend to get carried away by the sound of my own typing: :grin:

  • You cannot use a FALLING interrupt if you are in any sleep mode, other than Idle Sleep mode.
  • Idle Sleep mode is pretty useless on an Arduino, as the millis/micros counters are updated every millisecond (and a bit) by Timer 0 overflow, and that will wake up from Idle Sleep mode too.
  • If you are using any other Sleep mode, you have to use LOW for the interrupt trigger.
  • A LOW interrupt trigger will fire the flussostatoWakeUp function repeatedly for as long as the pin is held LOW.

I’m currently writing my second (and third!) Arduino book, and it is all about interrupts – if I ever finish it – so I’m well aware of the problems of sleep modes and interrupt triggers at the moment! :wink: What are you trying to achieve? I might be able to help.

Cheers,
Norm.

I apologize, in the second answer I capitalized it but because a programmer friend told me that the defines must be capitalized and so I redid the code:

Library used

#include "LowPower.h"

Definitions

#define FLUSSOSTATO 2
#define aux 21
#define DATA_PLUVIOMETRO 3

Setup()

 pinMode(FLUSSOSTATO, INPUT_PULLUP);
pinMode(aux, INPUT);
pinMode(DATA_PLUVIOMETRO, INPUT_PULLUP);

enterSleep()

    //attachInterrupt(digitalPinToInterrupt(aux), auxWakeUp, FALLING);
    attachInterrupt(digitalPinToInterrupt(FLUSSOSTATO), flussostatoWakeUp, FALLING);
    // attachInterrupt(digitalPinToInterrupt(DATA_PLUVIOMETRO))
    delay(100);
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    delay(100);

@maxgerhardt I tried not to put it to sleep and it actually works.
But I have to put the MCU to sleep at all costs.

So having seen everything you’ve written @normandunbar how do I use interrupts with Atmega2560? :sweat_smile:

On this occasion I say that I also have two other interrupts:

  • “aux” which is the aux pin of a LoRa modem.
  • “DATA_PLUVIOMETER” which is a rain sensor.

Obviously I realized that these don’t work either and I should get them to work.

Thank you

EDITED To correct wake up calls. See italicised text.

Ok, this is not a problem. You can use non-Idle Sleep modes, but only on pins INT0, INT1, INT2 and INT3 otherwise you would need to disable Timer 0 to prevent it from waking the board frequently. You can do this by:

// Save Timer0 status. (Global variable)
byte saveTCCR0B = TCCR0B;

// Disable Timer 0. (In setup()
TCCR0B &= 0xF8;

Which will disable the timer’s clock source, so it will not count and thus, will not overflow. You can now use a FALLING interrupt to wake the board. Unfortunately, doing this disables micros() and millis() and also, delay() which uses micros() in the background.

At some point later, if you wish to restore Timer 0, then:

TCCR0B = saveTCCR0B;

HTH

Cheers,
Norm.

OK I solved the problem with the “PLUVIOMETRO” and “aux” interrupt because in both cases I always have the HIGH pin and when an external event occurs these devices send the pin LOW and therefore these two are working thanks!

Now the problem with “FLUSSOSTATO” remains because the operation is:
when I have the MCU in sleep and the flow switch detects the passage of water it puts the FLUSSOSTATO pin at LOW and here it is okay because the MCU wakes up and sends an alarm message via the LoRa modem.

But when I receive the opening command via modem (and the aux pin wakes me up the MCU) then I give the command to the solenoid which opens a idraulic valve and then starts to flow the water and the flow switch (INPUT_PULLUP) goes to LOW.
And so far so good but …
If someone (human hands) manually shuts off the water I need to know and when the flow switch goes from LOW to HIGH it should wake up the MCU and send the error message via modem.

So I am obliged to use CHANGE

The quastion is: if I disable Timer 0 it should work?

Can I leave Timer 0 disabled forever? (I don’t use RTC or anything like that) but I use the delay() function, can it give problems to this one?

Then I didn’t understand, if I disable Timer 0 can I use CANGHE?

If you need to use delay(), millis() or micros() then, unfortunately, you cannot disable Timer 0 at all, because the Timer 0 overflow interrupt makes these functions work. This effectively means that your MCU will be waking up far too often.

However, you said you were using the INTx external interrupts. Which pins are you using please, I have re-read the data sheet for the Mega2560, and I notice a note attached to “wake up sources” for various sleep modes, and it appears that INT7, INT6, INT5 and INT4 require a LOW level interrupt to wake the MCU in anyothing other than Idle Sleep Mode.

I surmise from that note, that INT3, INT2, INT1 and INT0 do not need a LOW level interrupt to wake the MCU in any of the sleep modes. These are pins D18, D19, D20 and D21 – so, my apologies, you can use a CHANGE interrupt on those 4 pins and you can use any of the sleep modes, not just Idle, and then there is no need to disable Timer 0.

My apologies for not reading the data sheet correctly and I shall correct my posts above on the matter.

If you need more than 4 interrupt pins, which can take any of the “edge” interrupts, you might wish to take a look at using the Pin Change Interrupts. There are 24 of those and any of those will wake the MCU in any sleep mode with any of the interrupt types.

HTH

Cheers,
Norm.

I did some tests with the PIN 21 and 20 and in fact it works with CHANGE.

But if I try with PINs 18 and 19 it gives me this:

#define DATA_GPIO_1 19
#define DATA_GPIO_2 18

Going to see the Interrupts page on the official Arduino website at this page, you will find this table:


But if I go to see what the digitalPinToInterrupt () function does I find this:
#define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 : ((p) >= 18 && (p) <= 21 ? 23 - (p) : NOT_AN_INTERRUPT)))

I am very confused … it would seem that if you use 18,19,20 or 21 he always sets 23 … How is this possible? :sweat_smile:

Oh I noticed that a parenthesis was missing, now everything is working but there is a problem.

In my case the interrupts are:
PIN 21 Aux Modem
PIN 20 FLUSSOSTATO
PIN 19 GPIO 1
PIN 18 GPIO 2

For each PIN I have made a different attachInterrupt with the relative functions. But I realized that it goes a bit at random, and actually it is because any pin I declare between 18 and 21 he always takes 23 as I have shown above.
In the file pins_interrupts.h there is this line:

#define digitalPinToInterrupt(p)    ((p) == 2 ? 0 : ((p) == 3 ? 1 : ((p) >= 18 && (p) <= 21 ? 23 - (p) : NOT_AN_INTERRUPT)))

Is there any modification to make all four read well separately?

No! 18 to 21 get (23 -18) to (23-21), so 5 down to 2.

It looks like the Arduino code is only allowing int0 through int5 rather than right up to int7 which the AtMega2560 allows. Interesting, and probably accounts for why few pinout diagrams show int6 and int7.

HTH

Cheers,
Norm.

Sorry but I’m not understanding.
Maybe I’m hard headed.

Trivial question, can I use pins 18,18,29 and 21 as interrupt with CHANGE and use them separately?

My current situation:

    attachInterrupt(digitalPinToInterrupt(aux), auxWakeUp, LOW);
    attachInterrupt(digitalPinToInterrupt(FLUSSOSTATO), flussostatoWakeUp, CHANGE);
    attachInterrupt(digitalPinToInterrupt(DATA_GPIO_1), gpio1Wake, CHANGE);
    attachInterrupt(digitalPinToInterrupt(DATA_GPIO_2), gpio2Wake, CHANGE);

Where PIN Definition are:

  • aux 21
  • FLUSSOSTATO 20
  • DATA_GPIO_1 19
  • DATA_GPIO_2 18

If I put DOWN (for example) 21 aux the functions of the other interrupts also start randomly.

An other example: if I put DOWN “FLUSSOSTATO” let’s start flussostatoWakeUp and gpio1Wake totally random.
And if I put HIGH “FLUSSOSTATO” let’s start flussostatoWakeUp and gpio2Wake and auxWakeUp…

It’s a very strange thing and I don’t understand.

I can see that others recommend the “PinChangeInterrupt” library.
So maybe it’s a known problem?

Nah! You’re fine. It takes a while to get your head around these things. Look at me, I made a mistake reading and understanding the data sheet and led you down the garden path, about which interrupts can be used to wake the MCU (Mega2560) from non-idle sleep modes. :wink:

You are using pins D18, D19, D20 and D21 which are INT5, INT4, INT3 and INT2. Those 4 interrupts can wake the MCU from any sleep mode, with any interrupt trigger.

You are currently, not using pins D2 and D3, which are INT0 and INT1, and those can only wake:

  • Idle mode with any interrupt trigger;
  • Non-Idle mode with a LOW interrupt trigger.

So, your current pin usage can wake any mode that you put the MCU to sleep in, and with any trigger mode.

It’s not always 23, it’s 23 minus the pin number, so D18 is INT5, D19 is INT4 etc. Try this:

void setup() {
    Serial.begin(9600);
    for(byte x = 18; x < 22; x++) {
        Serial.print("Pin: "); Serial.print(x, DEC);
        Serial.print(" is Interrupt INT"); 
        Serial.println(digitalPinToInterrupt(x));
    }
}

void loop() {
    // Do nothing.
}

I get:

Pin D18 is Interrupt INT5.
Pin D19 is Interrupt INT4.
Pin D20 is Interrupt INT3.
Pin D21 is Interrupt INT2.

In a word, yes!

Do you have pullup/pulldown resistors on those pins? I note from earlier in the thread, that your aux pin was defined as INPUT. Does the sensor it is attached to have pullups/pulldowns? Because if you leave an input pin floating, it will get triggered by random tings, digital noise when another pin changes state, stray capacitance when you move your hand over the board etc etc.

You need to ensure that all input pins are in a known state when waiting for input. You can do this by defining them as INPUT_PULLUP so they read HIGH when not pressed and LOW when pressed, or, if you are like me and prefer your push buttons to be HIGH when pressed, use external pulldown resistors, about 10K, between the input pin and ground.

DO not wire the input pins directly to GND, always use a resistor.

Otherwise, when the switch or sensor is connected to 5V, it causes a short and could destroy your MCU.

I suggested using Pin Change Interrupts earlier too. The reason being, those interrupts, can be enabled on any of the MCU pins, not just on a specific few, and they can all wake the MCU regardless of the sleep mode or the interrupt trigger mode.

There’s no known problem, other than which iof the INTx interrupts can wake the MCU from each sleep mode. You should be ok with your chosen set of pins. Just be sure you have them pulled up of puller down.

The “problem” with PinChangeInterrupts is that:

  • You define a number of pins which have PCI enabled. The pins below to three separate “banks”.
  • There is a single interrupt vector, and ISR function, per bank as opposed to one for each pin.
  • The code in the ISR needs to work out what changed, and for which pin in the bank.

HTH

Cheers,
Norm.

Thanks for your comprehensive reply.

Given that now I understand how INT5,4,3 and 2 is calculated.

However I have all interrupts (and other PINs too) declared as INPUT_PULLUP.
Is it a problem that has no end?

Tomorrow I try to do some more tests and I will write a report here.

1 Like

I dug out my Mega2560 board and had a play. Here’s the breadboard layout I used.

The attached sketch will hopefully help you see what’s going on.

/*
 MEGA2560, sleep  modes and INTx wake up calls.

There are 5 switches attached to pins D2, D18, D19, D20 and D21.
The switches connect to GND when pressed. The pins are all INPUT_PULLUP.

Pressing a switch attached to D2 will NOT wake up from anything other
than SLEEP_MODE_IDLE, but as Timer 0's overflow is in use to update
millis() and micros(), the board will be awake anyway.

Pressing any of the other swicthes will cause the board to wake. It
will tell you which swoicth was pressed, before going back to sleep.


Norman Dunbar
For community.platformio.org.
17 October 2022.
*/

#include <avr/sleep.h>

volatile byte whichPin = 0;

void int2ISR() {
    // Triggers when pin D21 changes.
    whichPin = 21;
}


void int3ISR() {
    // Triggers when pin D20 changes.
    whichPin = 20;
}


void int4ISR() {
    // Triggers when pin D19 changes.
    whichPin = 19;
}


void int5ISR() {
    // Triggers when pin D18 changes.
    whichPin = 18;
}


void int0ISR() {
    // Triggers when pin D2 changes.
    whichPin = 2;
}


void setup() {
    Serial.begin(9600);

    // Pins 2, 18-21 are INPUT_PULLUPs.
    pinMode(2, INPUT_PULLUP);
    for (byte p = 18; p < 22; p++) {
        pinMode(p, INPUT_PULLUP);
    } 

    // Attach interrupts to all 5 pins.
    attachInterrupt(digitalPinToInterrupt(2),  int0ISR, FALLING);
    attachInterrupt(digitalPinToInterrupt(18), int5ISR, FALLING);
    attachInterrupt(digitalPinToInterrupt(19), int4ISR, FALLING);
    attachInterrupt(digitalPinToInterrupt(20), int3ISR, FALLING);
    attachInterrupt(digitalPinToInterrupt(21), int2ISR, FALLING);

    // Which sleep mode shall I use? If you change this to 
    // SLEEP_MODE_IDLE you won't need to press a button.
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
}


void loop() {
    Serial.println("I'm going to sleep now.");   
    delay(250);

    // The manual says to do it this way!
    noInterrupts();
    sleep_enable();
    interrupts();

    // Go to sleep with interrupts running.
    sleep_cpu();

    // I'm awake, disable sleep.
    sleep_disable();

    // Tell the world we are awake.
    Serial.print("I'm awake, thanks to pin D");
    Serial.println(whichPin, DEC);

    // And then go back to sleep on the next iteration of loop().
    delay(250);   
}

When running, it will display a message, then go into sleep mode. If you press one of the switches, it will wake up and tell you which switch got pressed, then go back to sleep. The D2 switch will not wake the MCU and will not display a message.

I'm going to sleep now.
I'm awake, thanks to pin D18
I'm going to sleep now.
I'm awake, thanks to pin D19
I'm going to sleep now.
I'm awake, thanks to pin D20
I'm going to sleep now.
I'm awake, thanks to pin D21
I'm going to sleep now.

It doesn’t matter how long you hold the button down for, the message will appear once, because the ISR function was triggered by the pin state falling to GND and not because it is being held there.

If you run the code as above, the MCU will go into Power Down sleep mode. Press the D2 switch and nothing happens. This would be the case if there was another switch on D3 as well, neither of those two switches will wake the board unless they are configured with a low interrupt trigger. LOW.

If you press any other switch, you get a message telling you that the board is awake and the name of the pin that woke it.

Change 1

Now, in setup(), change SLEEP_MODE_PWR_DOWN to SLEEP_MODE_IDLE and don’t press any buttons. You will see this in the Serial Monitor:

I'm going to sleep now.
I'm awake, thanks to pin D0
I'm going to sleep now.
I'm awake, thanks to pin D0
...

This is because of the Timer 0 overflow interrupt. It mentions D0 but that’s because whichPin is initialised to zero.

Change 2

Change setup() back to SLEEP_MODE_PWR_DOWN.
Change attachInterrupt(digitalPinToInterrupt(2), int0ISR, FALLING) to attachInterrupt(digitalPinToInterrupt(2), int0ISR, LOW).

Now when you quickly press the switch attached to D2, you’ll get multiple messages like:

I'm going to sleep now.
I'm awake, thanks to pin D2
I'm going to sleep now.
I'm awake, thanks to pin D2
I'm going to sleep now.
...

This is because a LOW level interrupt keeps on triggering for as long as the LOW level is present on the pin. You might be able to press and release quickly due to the delays I have in the code – those were put there to ensure that the messages could be read in the Serial Monitor. Without the delays, the MCU went back to sleep and the message text was not seen when it should have been.

Change 3

Feel free to change the triggering modes on the D18 to D21 switches to RISING, FALLING or CHANGE as desired, and try the code again.

  • If you use RISING nothing will happen until you release the switch.
  • If you use FALLING the message will appears when the switch is pressed.
  • If you use CHANGE you will see a message when the switch is pressed and another when it is released.
  • And, as you have seen, using LOW means you get a messages for as long as you hold down the switch.

In none of the above cases, does pressing one button cause other interrupt functions to trigger, so I am assuming (never assume!) that you either have a configuration problem, a floating input pin or the pins are actually being triggered correctly. Oh yes, if your pins are OUTPUT and the code changes state with digitalWrite(), the interrupt will trigger if you write the trigger condition to the pin.

Hopefully, this has been helpful in helping you understand what is happening with your configuration.

Cheers,
Norm.

Wow did you do all these tests for me? Thanks a lot

So, today I’m out of office, tomorrow I’ll do more tests and check all the expenses and income.

I have a completely custom HW and obviously standalone atmega2560.

We update tomorrow :hugs:

1 Like

I did some tests.
I set all the inputs with INPUT_PULLUP and as a function I use CHANGE
I connected 4 “ON” and “OFF” switches one each on pins 18,19,20 and 21.

The results are:

  • PIN 21, Starting from HIGH, if I put LOW it wakes up and only detects that input. Immediately after that he goes back to sleep and takes about 500ms. If now I change state from LOW to HIGH it does not detect it.
    So if I change from HIGH to LOW, it detects it and goes to sleep but if I change state immediately, it does not detect it from LOW to HIGH but if I immediately change to LOW it detects it.
    It gives me the impression that there is some capacitor that needs to recharge.
    Because if I change from HIGH to LOW, it detects and then goes to sleep and then I wait 4/5 seconds, it also detects the change from LOW to HIGH.

  • PIN 20 It’s correct.

  • PIN 19, starting with LOW PIN and I made this function LIKE ISR

void gpio1Wake()
{
    Serial.println("I woke by GPIO 1");
}

I change state to ON and call the correct function but it was called 4 times. Then…
I change state to OFF and call the correct function but it was called 1 times. Then…
I change state to ON and call the correct function but it was called 3 times. Then…
I change state to OFF and call the correct function but it was called 2 times. Then…
I change state to ON and call the correct function but it was called 4 times. Then…
I change state to OFF and call the correct function but it was called 2 times. Then…
I change state to ON and call the correct function but it was called 3 times. Then…
I change state to OFF and call the correct function but it was called 1 times. Then…
I change state to ON and call the correct function but it was called 4 times. Then…
I change state to ON and call the correct function but it was called 2 times. Then…
I change state to ON and call the correct function but it was called 4 times. Then…
I change state to ON and call the correct function but it was called 1 times. Then…
I change state to ON and call the correct function but it was called 4 times. Then…
I change state to ON and call the correct function but it was called 2 times. Then…
I change state to ON and call the correct function but it was called 4 times. Then…
I change state to ON and call the correct function but it was called 1 times. Then…

  • PIN 18 and with pin 18 same thing:
    I change state to ON and call the correct function but it was called 4 times. Then…
    I change state to OFF and call the correct function but it was called 2 times. Then…
    I change state to ON and call the correct function but it was called 4 times. Then…
    I change state to OFF and call the correct function but it was called 2 times. Then…
    I change state to ON and call the correct function but it was called 4 times. Then…
    I change state to OFF and call the correct function but it was called 2 times. Then…
    I change state to ON and call the correct function but it was called 4 times. Then…