Strange interrupts problem

I attached relatively small sketch that I extracted from my much larger project.

In this sketch there are 2 interrupt handlers:

  • ADC interrupt handler is running all the time, approx. 20 times per second.
  • FAN interrupt handler is enabled for the brief period of time every 5 seconds when it should run 3-4 times. (In my project use FAN interrupts to measure speed of the 3-wire fan.)

I’m counting, for both FAN and ADC, the number of interrupts occurred and print this counters every 1 second when I also reset them back to zero.

Here is what I get when I run my sketch:

In the first 5 seconds, there is no FAN interrupts (as expected) and there are 20 ADC interrupts per second (also, as expected):

FAN interrupt counter = 0, ADC interrupt counter = 3
FAN interrupt counter = 0, ADC interrupt counter = 20
FAN interrupt counter = 0, ADC interrupt counter = 20
FAN interrupt counter = 0, ADC interrupt counter = 20

At this moment FAN interrupts are enabled for the brief period of time, and as expected now there are some FAN interrupts to process:

FAN interrupt counter = 1, ADC interrupt counter = 20

Now, FAN speed is measured and FAN interrupts are again disabled:

RPM=4452

But now, suddenly, we started to receive 20 FAN interrupts per second all the time, when we should receive no FAN interrupt because they should be disabled!?

FAN interrupt counter = 22, ADC interrupt counter = 20
FAN interrupt counter = 20, ADC interrupt counter = 20
FAN interrupt counter = 20, ADC interrupt counter = 20
FAN interrupt counter = 20, ADC interrupt counter = 20
FAN interrupt counter = 21, ADC interrupt counter = 20
RPM=4419
FAN interrupt counter = 23, ADC interrupt counter = 20
FAN interrupt counter = 20, ADC interrupt counter = 20
FAN interrupt counter = 20, ADC interrupt counter = 20
FAN interrupt counter = 20, ADC interrupt counter = 20
FAN interrupt counter = 21, ADC interrupt counter = 20
RPM=4388
FAN interrupt counter = 23, ADC interrupt counter = 20
FAN interrupt counter = 20, ADC interrupt counter = 20
FAN interrupt counter = 19, ADC interrupt counter = 19
FAN interrupt counter = 20, ADC interrupt counter = 20
FAN interrupt counter = 21, ADC interrupt counter = 20

// etc

I checked the implementation of the functions attachInterrupt and detachInterrupt inside file WInterrupts.c from the Arduino library for the SEM boards. I have two observations I think are interesting:

At the beginning, there is initialization of the callbacksPioB (interrupt PIN’s inside my sketch belongs to port B) for every pin (32 of them) to NULL:

int i;
for (i=0; i<32; i++) {
	callbacksPioA[i] = NULL;
	callbacksPioB[i] = NULL;
	callbacksPioC[i] = NULL;
	callbacksPioD[i] = NULL;
}

When attachInterrupt is called, callbacksPioB is set to given callback:

// Set callback function
if (pio == PIOA)
	callbacksPioA[pos] = callback;
if (pio == PIOB)
	callbacksPioB[pos] = callback;
if (pio == PIOC)
	callbacksPioC[pos] = callback;
if (pio == PIOD)
	callbacksPioD[pos] = callback;

I was expecting, that in detachInterrupt, callbacksPioB will again be set to NULL, but there is no such code!?

void detachInterrupt(uint32_t pin)
{
	// Retrieve pin information
	Pio *pio = g_APinDescription[pin].pPort;
	uint32_t mask = g_APinDescription[pin].ulPin;

	// Disable interrupt
	pio->PIO_IDR = mask;
}
  1. Interrupt handler for the port B looks like this:
void PIOB_Handler(void) {
  uint32_t isr = PIOB->PIO_ISR;
  uint8_t leading_zeros;
  while((leading_zeros=__CLZ(isr))<32)
  {
    uint8_t pin=32-leading_zeros-1;
    if(callbacksPioB[pin]) callbacksPioB[pin]();
    isr=isr&(~(1<<pin));
  }
}

So, depending of PIO_ISR, multiple callbacks (given with attachInterrupt) could be called during single port B interrupt handler.

And indeed, in my case, after first 5 seconds, every time when ADC interrupt has been triggered, both ADC bit and FAN bit of PIO_ISR are set to 1 (I repeat, at this point FAN interrupts should be disabled).

Consequently, both (FAN and ADC) interrupt handlers that are set with attachIntterupt are called.

If detachInterrupt had reset callback to NULL (see point 1) FAN interrupt handler wouldn’t be called. But still, the question is why ISR bit for the FAN pin is marked when FAN interrupts should be disabled?

due_interrupts_problem.ino (8.75 KB)