Electronics > Projects, Designs, and Technical Stuff
Measuring time span very precise
magic:
AVR has the CLI/SEI instructions to temporarily disable/enable interrupts.
Interrupts are automatically disabled when the core starts executing an interrupt handler and re-enabled when it ends.
SEI can enable interrupts within an interrupt handler, but I doubt that Arduino would do it behind your back and of course you don't need to do it either if it's undesirable.
HendriXML:
There're 2 ways to hook on interrupts one using ISR(vector) and one using AttachInterrupt.
One is (I think) replacing the low level interrupt handler, one is adding something to be called by the standard low level handler.
It wouldn't surprise me if the latter has interrupts disabled and the other one not. If it's time critical the first option is probably the better choice.
It is good to know only one interrupt of a kind can be queued, so staying in them with interrupts disabled (for long) can have side effects. So if one knows what he's doing it's best to have only a small portion of code in CLEI/SEI. Besides that is getting harder to understand interrupts within interrupts within ... this is probably the main idea behind the common advice to keep the interrupt code very short.
But if one had to do some lengthy calculation (of lets say 500 ticks) every 1000th of a timer interrupt then it can still work fine. It will be interrupted many times (500).
I haven't tested this, but that is how I expect it to function.
Ian.M:
The key to handling software extended capture registers with a software extended timer is: right at the start of the ISR, to grab the timer high bit, timer overflow interrupt flag, and all capture interrupt flags, and pack them into a byte or word for further processing. Then do it again and check they haven't changed. Repeat until you get two consistent sets of high bit and flags, and you then avoid all race conditions due to captures on different channels happening too close to each other or to the timer rollover. All the possible cases encoded in the flag set, including incrementing the software timer extension if the stored overflow flag is set and doing a soft capture of the timer extension if any capture flag is set can then be handled without races or severe timing constraints.
If multiple close captures on a single channel are required, that capture channel's capture register must be read in the same loop that gets a consistent flag set, and also checked for consistency.
On MCUs with vectored interrupts like the AVRs, point the timer overflow and all the capture interrupt vectors at the same ISR as described above. Arduino attachInterrupt() is not useful here as it only supports interrupt capable digital pins, and one needs to route the timer overflow to the same ISR.
However that's a *LOT* of work and is only really appropriate when you have multiple input events (and possible generated output events) that you need to relate to a common long period timebase, so if you just want to measure an interval (or a frequency), its much simpler conceptually to use an internally or externally hardware gated timer that can simply count up from zero, with or without software extension, and only needs to be read when the gate signal has ended and the timer is thus stopped. I described this for a PIC18 Timer 0 in my previous post.
The original method of clocking a prescaler under program control to extract the count in it that you cant read directly comes from Microchip AN592.
HendriXML:
--- Quote from: HendriXML on September 07, 2019, 08:33:27 pm ---There're 2 ways to hook on interrupts one using ISR(vector) and one using AttachInterrupt.
One is (I think) replacing the low level interrupt handler, one is adding something to be called by the standard low level handler.
It wouldn't surprise me if the latter has interrupts disabled and the other one not. If it's time critical the first option is probably the better choice.
It is good to know only one interrupt of a kind can be queued, so staying in them with interrupts disabled (for long) can have side effects. So if one knows what he's doing it's best to have only a small portion of code in CLEI/SEI. Besides that is getting harder to understand interrupts within interrupts within ... this is probably the main idea behind the common advice to keep the interrupt code very short.
But if one had to do some lengthy calculation (of lets say 500 ticks) every 1000th of a timer interrupt then it can still work fine. It will be interrupted many times (500).
I haven't tested this, but that is how I expect it to function.
--- End quote ---
Hmm what if a time critical interrupt was called and the bulky one immediately steps in... That would not be so great. Starting with interrupts disabled would have benefits then..
I've checked this, the interrupts are disabled by HW when entering an interrupt. Thus when doing a bulky operation one should enable them.
These things also show why using external stuff (the counters are kind of external too) can be advantageous.
HendriXML:
--- Quote from: HendriXML on September 07, 2019, 07:42:11 pm ---Very interesting to think about interrupts and their order of execution again. That's how I started programming in DOS years ago. In the more modern OS's these problems got shifted to multi threading.
There critical sections, mutex's are used to be sure that access to data is synchronized: no simultaneous writing of data (or reading of dynamic data). In the DOS days one had to CLI (clear interrupts) to stop another "thread" from interrupting. Arduino has the same functionality. (On the 386 there were interrupts that could not be stopped, I recall). B.t.w. on PC's even the writing of a multiple byte (word, dword etc.) value can be interrupted, maybe for Arduino's as well.
What makes me uncomfortable with the overflow interrupt is that there's also stuff going on beyond code (which can be protected). Like overflowing (going to zero) and the setting of the overflow flag should be atomic from the perspective of the code. Between one and the other instruction both should be updated, not only one. This I guess is the case.
However reading them will not be atomic.
It would have make me then more comfortable if there where higher order functions that would give both of them, and do a clear of the flag atomically. The caller that gets the overflow flag is the one that increments the higher bits. (Those higher bits would be read from / written to in a protected way.)
I don't think such a function can be written without interpretation of the counter, because of the external nature of updating the counter and flag.
So the next best thing would be to first read the counter, and only read the flag if the counter is less the 50% of max. (If it's more than that the flag is always 0. In reality it could be 1, but that wouldn't match the counter) If the flag is set then clear it and update the high bit counter. The same should be done in the overflow interrupt. The reason for this is only to not have the overflow happen twice without the execution of this code in between. (Anything happening more frequent than that would be usable - it should however be called before the counter reaches 50%)
99.99% of the time the overflow interrupt will be updating the higher bits. But the interrupt can't be the only one.
--- End quote ---
As said by Ian above reading stuff repeatedly is also a solution, but its good to know that this is because they are written atomically (at least practically). Otherwise one could theoretically read inconsistent values twice.
(Just to point out the difference between externally changed stuff, and for instance updating internal memory, which can be interrupted and be inconsistent a lot longer.)
Navigation
[0] Message Index
[#] Next page
[*] Previous page
Go to full version