Teensy 4.1 interrupt trigger on wrong edge

noisymime

Active member
So, I thought I was going crazy but I think there's an issue with the edge triggering interrupts on Teensy 4.1. If I set a RISING or FALLING interrupt using attachInterrupt then I will occasionally see the ISR getting called on the wrong edge

Here is a basic example sketch:
Code:
void setup() {
  pinMode(10, OUTPUT);
  attachInterrupt(20, pinFallingISR, FALLING);
}

// the loop function runs over and over again forever
void loop() {

}

void pinFallingISR()
{
  digitalWrite(10, !digitalRead(10));
}

The code should toggle the output pin every time there is a falling edge on the input pin, which it does most of the time, however here is a waveform I captured showing the result. On this Yellow is the input signal (pin 20) and green is the output (Pin 10)

pic_316_1 copy.jpg

There are these odd short pulses on the output signal (green) that always align with the rising edge of the input signal. As far as I can see, this means that the ISR is occasionally getting called on the RISING edge as well as the FALLING one. I have a reasonable cap on the input signal, so I don't think this is caused by hardware noise.
I did wonder whether this was something to do with the specific pin I'm using for the input, but I've tried other pins and gotten the same result.

Is anyone else able to try and see if they can reproduce this?
 
Try adding this to the _ISR() code: asm("DSB");

That short _ISR() code exits and is likely returning given how the 1062 tracks interrupt completetion. Done so fast the update to 'handled' isn't noticed before the code decides there was an interrupt to handle.

Code:
void pinFallingISR()
{
  digitalWrite(10, !digitalRead(10));
  asm("DSB");
}

Also this is faster - given a constant for a pin number:
Code:
void pinFallingISR()
{
  digitalWriteFast(10, !digitalReadFast(10));
  asm("DSB");
}

Or his just to see it Toggle with even less time waiting for read and write paired around value change:
Code:
void pinFallingISR()
{
  digitalToggle(10);
  asm("DSB");
}
 
Still the same thing with that asm("DSB"); added unfortunately. Doesn't matter which toggle method is used.
In my actual sketch the ISR is quite a bit longer than the example, but it's probably still exiting fairly quickly.

FWIW I swapped a Teensy 3.5 into the same circuit and with the same sketch there is no issues at all. Still could be hardware related, but not sure what else to try on that side of things.
 
This makes it slightly more obvious:

Code:
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(10, OUTPUT);
  attachInterrupt(20, pinFallingISR, FALLING);
}

void loop() {

}

void pinFallingISR()
{
  if(digitalRead(20) == HIGH)
  {
    digitalWrite(10, !digitalRead(10));
  }
  asm("DSB");
}

The output should never toggle its value in this case because the pin should never be in a HIGH state, but running it shows it flipping regularly.
Just for reference, I've run the input signal anywhere from about 500Hz through 3kHz and see the same thing at all speeds. Tested with a 3.5 board, the above sketch is fine at all these speeds with the same filter cap etc.
 
Try a
Code:
pinMode(20,INPUT_PULLUP)
This, as a side effect, will activate a hysteresis on the input which prevents double triggering on slowly rising input signals.
 
Try a
Code:
pinMode(20,INPUT_PULLUP)
This, as a side effect, will activate a hysteresis on the input which prevents double triggering on slowly rising input signals.

Wow, that fixed it, thanks! I wouldn't ever have thought to try that as a fix for this type of issue. So the falling interrupt can fire on a rising pulse if it is too slow?

Is there any way to enable the hysteresis without the pullup being set? I'm using an external pullup and would prefer to keep to just that if it's at all possible is all.
 
OK, it mostly fixed it by the looks. I'm still seeing the occasional false interrupt getting triggered, but it's FAR better than what it was.
 
Also, just to be a pain with the questions, is there any guidance around what is 'too slow' for a rise time? In this setup the full rise from 0 to 3.3v is about 8uS, but probably around 4uS to 2.7v. That is somewhat slow, but I wouldn't have thought it was bad enough to cause a problem.
 
Lower impedance might be more important than speed, as the problem is related to false triggering due to high frequency noise.
 
Is there any way to enable the hysteresis without the pullup being set?

Code:
 *digital_pin_to_info_PGM[20].pad |= IOMUXC_PAD_HYS;

might work (untested!!).
 
Code:
 *digital_pin_to_info_PGM[20].pad |= IOMUXC_PAD_HYS;

might work (untested!!).

I managed to copy the code out of digital.c in the cores area and customise it to work. Looks very similar to this, but I also added the before/after checks from pinMode() just to be safe. Thanks so much for pointing me in the right direction though!
 
Lower impedance might be more important than speed, as the problem is related to false triggering due to high frequency noise.

I’ll have to see what I can do around this. It’s a design that was originally for the 3.5 boards that I’m altering for 4.1, but I probably have a few options for lowering the impedance of these input circuits

Thanks everyone for you help, it’s greatly appreciated.
 
Well for any digital input really, though its especially advantageous to stop interrupt storms for floating inputs.
 
Can you add a "dsb" to the end of the GPIO interrupt handler too? It really should have one...
 
Can you add a "dsb" to the end of the GPIO interrupt handler too? It really should have one...
I proposed this a while back (for other internal handlers too) but Frank B had pointed out that we shouldn’t do this always. I still don’t always know when it’s appropriate. My current mental model is that when the interrupt register is handled via a different internal clock or something, for example a peripheral clock, is when “dsb” is needed. I haven’t looked too deeply at the pin handler yet; is this the case here?
 
I proposed this a while back (for other internal handlers too) but Frank B had pointed out that we shouldn’t do this always. I still don’t always know when it’s appropriate. My current mental model is that when the interrupt register is handled via a different internal clock or something, for example a peripheral clock, is when “dsb” is needed. I haven’t looked too deeply at the pin handler yet; is this the case here?
I don't believe internal exceptions like SVC or PendSV need it because they don't go through the NVIC. But for externally triggered exceptions that require writes to memory-mapped registers to clear their interrupt triggers, especially if those writes are done near the end of the ISR (when the CPU can potentially return from the ISR before the write has completed), a dsb is needed to ensure they complete before the CPU checks for pending exceptions to tail-call from the current exception.

For the GPIO handler it just makes sense to have the dsb in one single place rather than users having to add it to each individual pin handler to guard against extra triggering.
 
For the GPIO handler it just makes sense to have the dsb in one single place rather than users having to add it to each individual pin handler to guard against extra triggering.
This would be very welcome on my project as it would save it needing to be placed in probably 40 places across the code (No there's not 40 different interrupts, but different functions that could be called depending on config/state). Having it automatically placed for all external interrupts would be so much simpler

Likewise, I can't see any reason why you wouldn't want this on any GPIO interrupt.
 
Likewise, I can't see any reason why you wouldn't want this on any GPIO interrupt.
Last time I checked, a DSB took quite some time.

Generating a double invokation on timer interrupts is quite easy since the underlying ISR is very lean. E.g., if you only set a flag or toggle the LED, you need a DSB to prevent double invokation. However, due to the checking of which pin was actually triggering the interrupt, the GPIO/Pin ISR should be slow enough to not need a DSB. At least, I never observed this bus sync issue with pin interrupts. Would be interesting to see an example which shows the double invocation issue with pin interrupts.
 
Back
Top