Distinction between LOW and FALLING modes in attachInterrupt

I am new to Arduino and this forum, and am amazed by the amount of information and tutorials - which I have started browsing. I have one (probably) basic question that is puzzling me concerning the 'mode' parameter in attachInterrupt.

Two of the modes are FALLING and LOW, and it's not clear what is the difference between them, On the face of it, both would trigger the ISR when the pin goes from HIGH to LOW, but is that all? I find LOW to be a little odd for an interrupt since it describes a pin state, and interrupts are surely event-driven.

I want an interrupt to be generated when a button is operated bringing the interrupt pin from HIGH to LOW, so I would expect to use the FALLING mode. But when would you use LOW, and what would it do that's different from FALLING?

If this is already addressed somewhere, or this query should be in a different part of the forum, I apologise, and perhaps I could be redirected.

Thanks,
Paul.

FALLING triggers an interrupt when the signal changes from HIGH to LOW. LOW fires the interrupt for the ENTIRE time the signal remains LOW. So, if your ISR returns while the signal is LOW, the ISR will immediately be called again, after executing only a single instruction back in the foreground task. LOW is almost never the right choice for the interrupt mode...

Regards,
Ray L.

paulfryer:
I want an interrupt to be generated when a button is operated bringing the interrupt pin from HIGH to LOW

If it is a button for a human to press and not some microsecond contact or something, then an interrupt is probably going to end up being the wrong tool. Don’t worry about catching the button the microsecond it gets pressed unless it is a reaction time game or something. Most humans can’t tell the difference. If your loop function can’t read your button at least a few hundred times a second then checking the button with an interrupt is just going to band-aid the real problem until the next thing you want to add to the code. The real problem is whatever blocking code you have in loop. Interrupts are for things that happen on the microsecond time scale. They’re a weapon of last resort.

The following diagram (Fig-1) may help to understand when we should use edge and active level to interrupt the MCU.


Figure-1:

1. The circuit arrangement of Fig-1 supports only FE (falling edge) and AL (active low level) to interrupt the MCU.

2. When the button K1 is not closed, logic level of INTO-pin is HIGH; when K1 is being closed, the logic level of INT0-pin makes a transition from HIGH-to-LOW (called FE for falling edge); when K1 is kept closed, the logic level of INT0-pin is LOW (called AL for active low).

3. If a patient wants to draw the attention of a doctor, he (the patient) will use AL trigger mode for interruption. (When an interrupt happens and as long as the trigger signal remains present, the INTF0 signal remains available.) Why? It is possible that the doctor does not want to get interrupted until he has finished the current patient; but, he wants to see the patient who is next in the queue and it can be known by polling the INTF0 signal.

4. If we want to detect someone who has just gone by (a momentary event), we must use FE trigger mode and the interrupt logic must remain active with the switches SW1 and SW2 are at closed conditions.

5. Finally--
Edge Trigger Mode is used when MCU expects that something to be happening externally for a moment.
Active Level Trigger Mode is used when the external agent wants that MCU should do something for him.

Another time the distinction between LOW and FALLING can be important is when the interrupt is used to wake the MCU from a sleep mode. Some MCUs (e.g. ATTINY85) cannot react to a FALLING (edge trigger) interrupt when in power down sleep mode whereas an ATMEGA328P can.

Thanks for those replies - it makes it much clearer.

In my case, the loop code is driving a relatively slow stepper motor, and it only makes sense for the behaviour deviation to occur at specific points in the motion, to allow me to keep track of where the motor is at. So inserting a test for the 'interrupt' directly into the loop might get missed if the button is operated too quickly. So I am thinking of having the ISR set a flag which the loop tests whenever the alternative desired behaviour would make sense. It would seem that a FALLING interrupt mode is the way to go.

Paul.

paulfryer:
So inserting a test for the 'interrupt' directly into the loop might get missed if the button is operated too quickly.
Paul.

Your stepper takes half second steps? Or superman is the one pushing the button?

Wherever your code tests that flag it could just read the button. If your loop is taking so long that you might miss a button press then you need to look at that problem instead of patching it with an interrupt. That's going to cause more issues later if this code grows at all.

I've been bit by this one enough times. You go ahead if you want, just trying to help you out a little and stop you making the same mistake.

Thanks for your concern, and I understand it. But that low speed is intentional and is what the application requires: it takes between 1 and 2 seconds for the motor (which is actually micro-stepping) to transition between two adjacent locations where the position is predictable - 1/50 of a complete revolution or 4 equivalent full steps in a 200-step motor. I’m not willing to put a button read in the middle of the micro stepping code, and I couldn’t take any action at that point even if I did since I wouldn’t have an accurate feel for motor position.

Other than the micro stepping implementation, this is a fairly simple application which won’t grow into anything big. Using an interrupt button to stop motion when necessary is very simple code and should work well.

paulfryer:
Thanks for your concern, and I understand it. But that low speed is intentional and is what the application requires: it takes between 1 and 2 seconds for the motor ...

You still don't understand. The fact that it takes 2 seconds for the motor to step DOES NOT mean that it needs to take 2 seconds for your loop to run. Your loop should be able to repeat and recheck that button many millions of times in that 2 seconds. If it can't then you have a design flaw and the interrupt is just a band-aid for something that is going to bite you later.

I understand low speed steppers. I know what you think the problem is.

The digitalRead will take a few microseconds. If you really need microsecond resolution on a 2 second long pulse then THAT is the thing that should be interrupt driven, not the stupid button. You've got other interrupts being serviced (millis()). If you're that worried about being 2 microseconds late on your pulse then you need to think about them more than the digitalRead.

You are saying you have blocking code driving a stepper, and don't know how to make it non-blocking?

The AccelStepper library might help you with that, it non-blocking, designed for this kind of purpose and easy to use.

Not a reason in the world the stepper driver code cannot be entirely interrupt-driven, or even entirely hardware-driven (as long as accel/decel ramps are not required), once each move has been started. That would leave the processor almost entirely free to handle low-priority tasks like running the UI.

Regards,
Ray L.