T4.1 multiple interrupts firing on one pin state change

Status
Not open for further replies.

Talkiet

Well-known member
I'm having odd phantom interrupts being fired on a single buttonpress. I have hardware pullups (10k) in place on 2 of the pins (22 and 23) and INPUT_PULLUP on the other (pin 2). I have only seen the behaviour on pins 22 and 23, and I THINK I have only observed pin23 interrupt firing when I press 22, not vice versa. This is the example code. (Teensy 4.1 @ 528Mhz)

Code:
volatile uint32_t startlastfired, finlastfired, dnflastfired;
volatile uint32_t beamdebounce = 1000;

#define DEBUG_CS Serial // Debug to USB serial console, can change to Serial6

void setup() {
  DEBUG_CS.begin(115200);

  pinMode(22, INPUT_PULLUP); // sets the digital pin as input for START beam
  attachInterrupt(22, isrStartBeam, FALLING); // interrrupt on light beam input
  pinMode(23, INPUT_PULLUP); // sets the digital pin as input for FINISH beam
  attachInterrupt(23, isrFinBeam, FALLING); // interrrupt on light beam input
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(2, isrDNFbutton, FALLING); // Interruptt for the local DNF button
}

void isrStartBeam()
{
  cli();
  if (millis() - startlastfired > beamdebounce) {
    startlastfired = millis();
    DEBUG_CS.println("START PRESSED IN INTERRUPT");
  }
  sei();
}

// FINISH Light beam trigger generates this interrupt
void isrFinBeam()
{
  cli();
  if (millis() - finlastfired > beamdebounce) {
    finlastfired = millis();
    DEBUG_CS.println("FINISH PRESSED IN INTERRUPT");
  }
  sei();
}

// This is a hardware interrupt for the DNF button. Overkill but easy
void isrDNFbutton()
{
  cli();
  if (millis() - dnflastfired > beamdebounce) {
    dnflastfired = millis();
    DEBUG_CS.println("DNF PRESSED IN INTERRUPT");
  }
  sei();
}

void loop() {
  // put your main code here, to run repeatedly:
}

And this is an example output when it happens - the timestamp is just from the serial monitor - Note the identical timestamp at 32.313.

16:05:30.674 -> START PRESSED IN INTERRUPT
16:05:31.139 -> FINISH PRESSED IN INTERRUPT
16:05:31.799 -> DNF PRESSED IN INTERRUPT
16:05:32.313 -> START PRESSED IN INTERRUPT
16:05:32.313 -> FINISH PRESSED IN INTERRUPT
16:05:33.330 -> DNF PRESSED IN INTERRUPT

This is doing my head in. I can't see how this is a SW issue and I have tried with and without "INPUT_PULLUP" on 22 and 23. My only theory at the moment is the wires to the pushbuttons are functioning as aerials and somehow there's not enough noise rejection. Can I safely use a much stronger resistor for the physical pullup? What would be a safe value?

I don't think I can use bounce or polling. In the full program the button presses are actually from a beam input and need to be at least millisecond accurate - The interrupt routine only checks for bounce, saves a timestamp and sets a flag for the main loop to process the timestamp later.

Cheers - Neil G
 
The TimeStamp printed from IDE SerMon doesn't have the resolution to trust those printed values:
Code:
01:47:42.494 -> 
01:47:42.494 -> START PRESSED IN INTERRUPT
01:47:42.494 -> FINISH PRESSED IN INTERRUPT
01:47:42.541 -> DNF PRESSED IN INTERRUPT
01:47:42.541 -> START PRESSED IN INTERRUPT
01:47:42.541 -> FINISH PRESSED IN INTERRUPT
01:47:42.541 -> DNF PRESSED IN INTERRUPT

Here it is with the micros() value printed:
Code:
02:08:06.723 -> C:\T_Drive\tCode\FORUM\InterruptCycle\InterruptCycle.ino Oct  2 2021 02:08:00
02:08:09.313 -> START PRESSED IN INTERRUPT us=5034938
02:08:09.313 -> FINISH PRESSED IN INTERRUPT us=5036940
02:08:09.313 -> DNF PRESSED IN INTERRUPT us=5038941
02:08:09.313 -> START PRESSED IN INTERRUPT us=5040942
02:08:09.313 -> FINISH PRESSED IN INTERRUPT us=5042943
02:08:09.313 -> DNF PRESSED IN INTERRUPT us=5044944

Here's the code used on a Teensy MicroMod - with altered pins jumpered and code to emulate ...
Code:
// https://forum.pjrc.com/threads/68349-T4-1-multiple-interrupts-firing-on-one-pin-state-change?p=289999&viewfull=1#post289999
volatile uint32_t startlastfired, finlastfired, dnflastfired;
volatile uint32_t beamdebounce = 1;

#define DEBUG_CS Serial // Debug to USB serial console, can change to Serial6

void setup() {
  DEBUG_CS.begin(115200);
  while ( !Serial ) ;
  if ( CrashReport) Serial.print( CrashReport);
  Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  while ( !Serial.available() ) ;

  pinMode(42, OUTPUT); // 43
  pinMode(3, OUTPUT); // 2
  pinMode(11, OUTPUT); // 12 //22
  digitalWrite(42, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(11, HIGH);

  pinMode(12, INPUT_PULLUP); // sets the digital pin as input for START beam
  attachInterrupt(12, isrStartBeam, FALLING); // interrrupt on light beam input
  pinMode(43, INPUT_PULLUP); // sets the digital pin as input for FINISH beam
  attachInterrupt(43, isrFinBeam, FALLING); // interrrupt on light beam input
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(2, isrDNFbutton, FALLING); // Interruptt for the local DNF button
}
volatile uint32_t usI;
void isrStartBeam()
{
  if (millis() - startlastfired > beamdebounce) {
    startlastfired = millis();
    usI = micros();
    DEBUG_CS.print("START PRESSED IN INTERRUPT us=");
  }
}

// FINISH Light beam trigger generates this interrupt
void isrFinBeam()
{
  if (millis() - finlastfired > beamdebounce) {
    finlastfired = millis();
    usI = micros();
    DEBUG_CS.print("FINISH PRESSED IN INTERRUPT us=");
  }
}

// This is a hardware interrupt for the DNF button. Overkill but easy
void isrDNFbutton()
{
  if (millis() - dnflastfired > beamdebounce) {
    dnflastfired = millis();
    usI = micros();
    DEBUG_CS.print("DNF PRESSED IN INTERRUPT us=");
  }
}

int ii = 6;
void loop() {
  // put your main code here, to run repeatedly:
  if ( ii == 6 || ii == 3 ) {
    digitalWrite(11, LOW); // START 22
    delay(1);
    digitalWrite(11, HIGH); // START 22
  }
  if ( ii == 5 || ii == 2 ) {
    digitalWrite(42, LOW); // FINISH 43
    delay(1);
  digitalWrite(42, HIGH); // FINISH 43
  }
  if ( ii == 4 || ii == 1 ) {
    digitalWrite(3, LOW); // DNF 2
    delay(1);
    digitalWrite(3, HIGH); // DNF 2
  }
  if ( ii > 0 ) {
        Serial.println(usI);
    ii--;
  }
  else {
    ii = 6;
    delay(1000);
    Serial.println();
  }
  delay(1);
}
 
Sorry yes I wasn't 100% clear on that. I was using the times on the serial monitor only to confirm that I was pressing a single button at that time... I was single finger pecking between the buttons every second or so, and at that one press at 32.313, I got a Serial output from both isrStartBeam and isrFinBeam.

I can see the logic in your code around the timing, but I admit I don't understand how setting pin 11 low fires the interrupt attached to pin 12!

The main issue is still that from a single buttonpress, two different interrupts are firing. I guess I may need to run another high frequency intervaltimer to log the analogstate of both buttons for a few microseconds after first press is detected.

Cheers - N
 
For buttonless bounce free emulation here paired pins were wired together.

42<>43 and 3<>2 and 11<>12 :: Had to pick from available pins on the carrier board.

Just plugged in a new Teensy MicroMod and wanted to test it - first experience was BAD CABLE that partly worked but made the unit seem broken ... though used through BETA test of that without such issues, maybe not with that cable.

Not sure of any reason the pins should cross talk - where pushing one button would affect the others?

Some short interrupts can double - and that didn't show - but there is a .print() in the _isr, which can cause trouble.

Also the three interrupts created are all at the same default level of Priority - so no need to disable interrupts - only one can be activated at a time, multiple presses would queue them up, and they'd be handled in the order parsed form the larger pin array,


So the altered test doesn't/can't show what the behavior seen ... not sure if there is anything useful in that sketch that might help with the issue there ???
 
Nevermind, thanks for looking at it... Also, your reply has taught me something new... I didn't know that interrupts at the same default priority would queue! If nothing else that gives me some basis for understanding why the order of the (nearly) simultaneous executions is always in the same order.

(Also can't believe I missed the physical wiring connection of pins together! Of course...!)

In working through the process in my head, I now realise that due to the nature of physical buttons, the interrupts are likely being called multiple times for each buttonpress, and it's only my manual debouncing that's making the output look like clean single pass execution.

There is another higher priority timer in the main program so I don't want to remove the interrupt disabling (unless you're going to tell me that because the other one is higher priority it doesn't have any real effect). However the issue is exhibiting here in this small example - I will remove the CLI/SEI and see if I can get it to replicate.

Since earlier today I have run the test on a completely separate board (different T4.1, different switch, different (but same value) pullups, on a PCB from the same run) and it exhibits the same occasional behaviour. I don't know what to try next.

Actually I do, but it's a workaround and I would rather understand this issue. The workaround is a relatively high frequency interval timer (every half a millisecond) that polls all the inputs. A quick test shows it was taking between 110 and 800 clock cycles per execution depending on how much I could pull out of the intervaltimer loop.... But. Ugly.

Cheers - N
 
You can adjust the priority on the pins above the systick.

Not having the print in the _isr would assure it isn't causing confusion. If that could just be a flag to check in the loop in some safe fashion.

If the inputs are set _PULLUP then external pullups shouldn't be needed - maybe the external pullup dropping for one is carrying over to the other?
 
You can adjust the priority on the pins above the systick.

Not having the print in the _isr would assure it isn't causing confusion. If that could just be a flag to check in the loop in some safe fashion.

If the inputs are set _PULLUP then external pullups shouldn't be needed - maybe the external pullup dropping for one is carrying over to the other?

Thanks... The PCB I made had the primary 2 inputs with the external pullups but I broke out another 6 pins for future less critical use and didn't put pullups on them. I will switch the beam inputs over to 2 of those and see if I can replicate.

I have also tried not using INPUT_PULLUP on the pins but observed the same issue.

Thanks all for the pointers and information. Even without a smoking gun here I've learn some useful stuff!

Cheers - Neil G
 
Hi Talkiet,

My suggestion is this is, place capacitors across the switches. The capacitors charge up and hold the line high for a small fraction of time while you finish pushing a noisy switch fully in (it's a hardware de-bounce circuit, use values 100nf or above for best effect).

Also change the switch on pin 23, it could be a bad switch that has a very small gap between its contacts and even slightly flexing the board could activate the switch. When you push the button on pin 23 you will never see the issue as your activating the button and it hides the fact that the button could be bad.

These two things may or may not help in finding a permanent solution to your issue.

best regards

Simon.M
 
Thanks for the thoughts - the switches are connected to the board through 2 pin JST connectors and about 10cm wires. I have already swapped the 2 switches around and the issue followed the Teensy pin, not the physical switch (or wiring). I have also tried a differnet PCB and Teensy 4.1. Problem exists on an entire second set of hardware. The PCB layout with respect to the switches and HW pullups is pretty trivial so I am just stumped at this point and am resigned to looking for a workaround.

I will try a couple of small caps though - that certainly makes sense.

Cheers - N
 
Thanks... The PCB I made had the primary 2 inputs with the external pullups but I broke out another 6 pins for future less critical use and didn't put pullups on them. I will switch the beam inputs over to 2 of those and see if I can replicate.
...

Did it repro the same when switching to those alternate pins?
 
It's back to weekday working so it might be a night or 2 before I can get back to this... I'm trying to come up with a small synthetic program which will capture and log the behaviour instead of me having to run through program logic at human speed...

Cheers - N
 
Status
Not open for further replies.
Back
Top