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 :
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 ?
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 .
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.
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.
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.
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.
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.
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.