Interrupt bottleneck in Arduino UNO ? [Solved]

Hello,
I am designing a clock whose time source is a 10 MHz signal, and the prototyping is done with an Arduino UNO.
10 MHz is too fast for an arduino so the frequency is divided by 2^14 by a HC4020 binary counter, so a final frequency of 610.35 Hz.
The arduino counts the pulses with an interrupt checking a rise on pin 3, then increments a second counter accordingly. Here is the code :

#define ECHO 3		// pin receiving pulses
#define BEAT LED_BUILTIN		// pin pulsing each second
#define PULSE 16384 	// length of pulse in 10^-7 secondes

boolean b=0;
volatile unsigned long
	p=0,
	sec=0,
	s=0;
unsigned long
	s0=0,
	scopy;

void setup()
	{
	Serial.begin(9600);
	pinMode(ECHO, INPUT);
	pinMode(BEAT,OUTPUT);
	digitalWrite(BEAT,LOW);
  	attachInterrupt(digitalPinToInterrupt(ECHO), pulse, RISING);
	}
	
void pulse()
	{
	p+=PULSE;
	s=sec+p/10000000;
	
	if(p%10000000==0)
		{
		sec=s;
		p=0;
		}
	}

void loop()
	{
	noInterrupts();
  	scopy = s;
  	interrupts();      
	if (scopy>s0)
		{
		Serial.println(s);
		digitalWrite(BEAT,b=1-b);
		s0=scopy;
		}
	}

Theoretically, the ATMega processor at 16 MHz can easily read a 610 Hz signal.
Practically, my code works about half the time seemingly randomly. Sometimes, it runs for hours without issue. Sometimes, a kind of bottleneck seems to occur and the reading began to slow. A reset doesn't fix the issue and it needs to power off / on the board to make it read correctly, but it doesn't always work. Other time, it runs fine during some minutes and suddenly began to slow down. So my questions :

  • do you see an issue with the code ?
  • if an interrupt occurs between noInterrupts() and interrupts(), is it lost ?
  • if an interrupt occurs while ISR is running, is it lost ?
  • if not, is there an interrupt "memory" of still not used interrupts and could it be overflowed ?
  • could there be a condition where the interrupt doesn't reset itself ?

All the best

I am more inclined to think of a (hardware) problem with your 10MHz source or the HC4020 binary counter

You might be better off using a higher baud rate for the serial printing.

I'll test your code, and see how it works for me.

The arguments for '%' should be of type int, which means they are limited to 32767 maximum. 10E6 is out of range

Have a look at remainder , I don’t think it works with your variable types- that could be an issue .( Jim beat me to that )

Remainder

I don’t see why you need to disable interrupts .
You would be better with an input source frequency or processor frequency that divides and gives you seconds more easily.
There are lot of clocks made with Arduino , worth a google .
If it was me I’d only increment the variable in the ISR and do the other parts elsewhere .
Watch out for variable overflows .

1 Like

sure... and not a single one

First of all - your math in the pulse() routine is a way far from optimal. Do not forget that you make a calculation in an integers, so this line do nothing most of time:

Further, the 10,000,000 is not exactly divisible by 16384, so the p value will never be exactly equal 10 * 10^6 and the condition will never be true:

Instead of that code, I would leave in the interrupt only an increment (and increment by 1, not 16384). And I would move all the rest of the math to the main code, changing the s variable type to float.

2 Likes

Really , anyway , you’d be better off with a DS3231 or an internet based clock , or GPS based .

Maybe this way?

void pulse()
   {
     p++;
     if (p >  PULSE) {
        p -= PULSE;
        s++;  
	    }
   }

I suspected that at first, but If I put a second HCT4020 behind the first to divide the frequency up to 76.29Hz, the Arduino works fine.

This line is used to detect when the accumulation of pulses goes above one second, to increment the second counter.

78125 Ă— 16384 / 1e7 = 128 so the condition is true after 78125 pulses.

Interesting clue. Are you sure the % operator doesn't work for long integers ?

Thank you for the other answers, but my question is not what is the better way to make a clock, but how to explain the behavior observed.

I'm sure it works fine.

There are always things to watch out for with that operator, so testing and verification make sense; negative numbers are a surprise different to mathematics.

a7

Hi @harlock974g ,

Welcome to the forum..

shouldn't that be scopy??

good luck.. ~q

I recommend to read datasheet for ATmega328P.

If the interrupt occur during "noInterrupts" it will be remembered and associated routine will be executed immediately after int. enable but according to priority with respect to other interrupts. However, if more interrupts of the same kind occur during this period, they will be lost.

Another one interrupt can be remembered while it's ISR is running.

Read the data sheet, chapter: Reset and Interrupt Handling. It explains all.

No but the documentation says integers.

A long int is an integer so it meets the criteria

1 Like

The rule No.1 with ISRs is: Keep it as simple as it is possible. It must be fast to minimize blocking of other interrupts.
Do the heavy utilizing calculation in the main program.

Think about using the Timer/Counter1.

No, it isn't. You could use the 10 MHz as an external CPU clock source (instead of the 16 MHz default). Then all the timing is internal to the Arduino, based on that clock source.

If you want to precisely determine the frequency of the 10 MHz source, use Input Capture on a hardware timer, with a GPS 1 PPS signal as the time base.

See my link in post 5 ; gives details of variable types allowed for the remainder function

Well this document

https://docs.arduino.cc/language-reference/en/structure/arithmetic-operators/remainder/

explicitly names int as the allowed data type for the % operator.

Which at this ecaxt moment means find better documentation… and when I get to the lab, a few quick tests.

a7

long int is a data type (range: -2,147,483,648 to 2,147,483,647); whereas, integer refers to a non-fractional number of any size and any sign. If I am correct, then long int is not the synonym for integer.

I see no point to explain the behavior of incorrect code - rather write a correct one.
Your interrupt on each call performs several complex mathematical operations, which are quite expensive for the processor, and at the same time completely unnecessary for your task.

My IRQ routine in post #8 do exactly the same as yours, but without using of multiplication, division and "%" operator.

1 Like