Interrupt performance

Status
Not open for further replies.

DaveAK

Well-known member
I'm starting a new project that will be using a Teensy 4.0 or 4.1, but I haven't ordered it yet, so I'm prototyping some concepts on an ATmega328 based SparkFun RedBoard. I'm simply testing interrupt code from a limit switch I intend to use using the following example code from the Arduino reference site:

Code:
const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}

void loop() {
  digitalWrite(ledPin, state);
}

void blink() {
  state = !state;
}

My problem is that it's very easy to get the LED out of sync with the switch by rapid switching, (flicking it with a finger), so my question is whether I'd see improvement with the Teensy. The reason I'm asking is because the particular limit switch I'm using has a resistor and capacitor which I presume are there as an RC circuit for debouncing the input, (I'm using this switch), and I'm wondering if that might be effecting things. It's also configured to be active low, taking a supply voltage and returning a separate signal line. If I remove the interrupt and simply poll the state of the switch I have zero issues of course, but this is going to be part of a more complex system so interrupts would be ideal. If it's not simply a microcontroller performance issue but something that might be common between this RedBoard and a Teensy then I need to do a rethink of my design. For example this particular aspect of the overall system could be isolated to a Teensy LC and I could use polling, but I'd rather just have one Teensy providing a complete solution.

A second question would be whether to use a pull-up resistor or not. The pin can be declared as INPUT_PULLUP, but it makes no difference in my testing. Since I'm supplying voltage to the switch which returns HIGH when the switch is open and LOW when closed I'm not sure what I need in the way of pull-up, pull-down or nothing. I always find this part confusing. :)
 
Instead of toggling the state, you could actually read the state of the input pin -- then your state would never be out of synch with the switch/
 
Well that certainly seems to work. But now I'm curious as to why toggling state has issues. :D
 
button chatter, debounce needed. anyways, better to set state to the gpio result (HIGH (1) or LOW(0)) then set digitalwrite the state, that should solve that problem

state = digitalReadFast(interruptPin);
 
On the ATmega328 multiple interrupts of the same interrupt vector are discarded - not queued. It
only queues interrupts of different types (in other words the queue has one entry per vector).

Not sure if the T4 is the same.

Hardware deboucing requires a schmitt-trigger as well as RC circuit - otherwise you risk creating more
bounce-noise due to slow edges. The T4 requires incoming logic edges to be faster than 25ns unless
hysteresis mode is enabled on the input pin - not sure if this is the default.
 
A quick search turned up this schematic.

makerbot switch schmatic.PNG

Is this the switch you're using?
 
About your 2 questions...


My problem is that it's very easy to get the LED out of sync with the switch by rapid switching, (flicking it with a finger), so my question is whether I'd see improvement with the Teensy.

To answer directly, yes, you probably would see some improvement, but it's unlikely to be enough. This approach is simply not a good way to read a switch.


A second question would be whether to use a pull-up resistor or not.

If those switches really are that circuit, then no, you don't need to add a resistor or use INPUT_PULLUP. Those little red PCBs already have the resistor built in.


has a resistor and capacitor which I presume are there as an RC circuit for debouncing the input

Yes, but as debounce circuitry goes, this one isn't very good. Software debouncing will still be needed.


but this is going to be part of a more complex system so interrupts would be ideal. If it's not simply a microcontroller performance issue but something that might be common between this RedBoard and a Teensy then I need to do a rethink of my design.

Yes, you really should rethink your design.

First, I would urge you to consider whether interrupts really are worthwhile. Even if you get the interrupt-based switch reading working perfectly, you still have to find a way to reliably communicate between the interrupt and the rest of your program. Interrupts can be very tricky. It's easy to craft code that works almost always, but then has occasional failures when the interrupt occurs at precisely the wrong moment while variables are being updated.

In you are determined to use interrupts anyway, I'd recommend using IntervalTimer rather than attachInterrupt. Then you can craft code which reads the button regularly, like every couple milliseconds. One approach is to only recognize a change if the last several reading are all the same. Or you could use the Bounce library. Or come up with whatever way you like.

But unless your program will need to spend long times not able to check the buttons, I'd recommend using the Bounce library from loop(). Usually such programs can't actually do whatever it is they do in response to the button until they run loop() again anyway. While interrupts theoretically can give rapid response or tight timing, if you don't actually act upon the detected change until some particular main program activity is complete, you get no real benefit but still have to endure all the hidden costs of interrupts.

And one final point about human safety.... if you're using these limit switches in a way that potentially protects people from harm, relying only software is playing a very dangerous game. You can send the signal to both software for a nice user interface to give diagnostic info and also to hardware that will shut off power to motors or other equipment.
 
would just use the bounce library.
Since this is just about mechanics, it is not necessary to use interrupts at all. It makes no difference if you know the state of the switch within 50 nanoseconds or microseconds.
Any mechanics is orders of magnitude slower. I guess 10 milliseconds or even more would be fast enough.
In addition, it makes your code easier.
 
Thanks for all the responses. Some useful pointers but maybe I should clarify a couple of things. First the title of my post might be misleading, it obviously isn't a performance issue but perhaps an implementation issue. Secondly when I said that "interrupts would be ideal" that's purely conceptually rather than any notion of an ideal requirement. Since the limit switches should be rarely, if ever, activated in a well performing process that remains within limits I simply want the system to tell me when they've been triggered rather than continually asking "Are we there yet? Are we there yet? Are we there yet? ...."

As for debounce, while the RC might not be ideal I think it will be sufficient for my needs. (Yes Paul, that's the switch I'm using.) If I run in to problems as I grow the program I'll throw in the Bounce library. Simply switching from "state = !state" to "state = digitalRead(interruptPin)" as suggested by thebigg fixed the issue - I've been unable to get the LED out of sync with the switch. But I still don't understand why. Following the link Paul provided on IntervalTimers seems to imply interrupts are far more trouble than they're worth, which I have to say is disappointing, (subjectively speaking). I have several sensors that will need to be polled at varying intervals and I had intended using timers for this, but now it just looks like I'll poll everything from within loop() and be done with it.
 
It was probably getting out of step because some interrupts were dropped as they came in while the previous one
was still being handled, like I said. You don't get indefinite length queues for interrupts, and each interrupt has to
wait for its predecessor (of the same type/priority) to finish before re-triggering. If a third one comes in then, its
simply lost.

Given slowly transitioning edges can generate multiple (even upto thousands) triggers, its not surprizing to me.
You need a Schmitt-trigger (a.k.a. hysteresis) after an RC delay to prevent bogus transitions.

Interrupts are a tool for a job, some jobs they are the right tool for the job, others, less so. Its a judgement
call often, but interrupts are for urgent tasks (urgent usually means microsecond timescales).
 
It was probably getting out of step because some interrupts were dropped as they came in while the previous one
was still being handled, like I said. You don't get indefinite length queues for interrupts, and each interrupt has to
wait for its predecessor (of the same type/priority) to finish before re-triggering. If a third one comes in then, its
simply lost.

Given slowly transitioning edges can generate multiple (even upto thousands) triggers, its not surprizing to me.
You need a Schmitt-trigger (a.k.a. hysteresis) after an RC delay to prevent bogus transitions.

Interrupts are a tool for a job, some jobs they are the right tool for the job, others, less so. Its a judgement
call often, but interrupts are for urgent tasks (urgent usually means microsecond timescales).
OK, the penny has finally dropped! This clears up the mystery for me. I understood what you put in your original post but it didn't fully register. I certainly don't need microsecond timing for this, I just thought it was a convenient solution.
 
Status
Not open for further replies.
Back
Top