Electronics > Projects, Designs, and Technical Stuff
Measuring time span very precise
<< < (8/16) > >>
HendriXML:

--- Quote from: magic on September 08, 2019, 06:32:13 am ---By the way, the value of TCNTn or ICRn is one of the few things that can actually be read atomically; consult the datasheet.

--- End quote ---
Good info! A good example of how they took extra effort of making something atomic  :horse:..

So first read low byte and then high byte.

However reading both the counter and the flag together cannot be done consistent (atomically). That was something I was referring to. So my brain gets to think how to make it atomic? Why not take a derived overflow flag, which is always 0 when no overflow has happened in the counter. And is only 1 when the captured counter is low, and the real flag is still 1. It would even be possible that the flag would have been cleared HW after reading it (I think this kind of "tricks" are done), so hence the "practice" of not touching the flag if not needed. (You touch it, you eat it) In this case the flag probably won't change when reading (haven't checked it) but it could have been a way to only have one "thread" made responsible for it.

A side note, this atomic stuff is relative to the HW changing things. There still the requirement that only one thread reads the low and the high counter value. That is not atomically by it self, so interrupts should be disabled. :horse:

Normally api would try to handle these kind of pitfalls. So for young players this discussion might be of use.
Kleinstein:
There is no need to make reading the overflow flag and ICP value atomic. The check is done inside the ICP ISR and thus with disabled interrupt flag. If the ICP value is changing due to a 2 nd event, one has a problem anyway.

With the AVR there is no problem reading the interrupt flag (no "You touch it, you eat it").

When the overflow happens just before the ICP, it can happen that the ICP ISR is called first. The case has to be handled separately and is checked with the overflow interrupt flag and ICP value. When actually handling this special case one can just do the jobs normally done in the overflow ISR and clear the flag or only correct the one reading and let the overflow ISR run (just after the ICP ISR is done). The 2 nd case is preferred if there are more jobs done in the timer overflow ISR.

Reading and writing low and high byte in the correct sequence is handled by GCC. However it does not use  CLI/SEI to disable interrupts.
Quite often the ISRs don't write to 16 bit registers that would effect the 16 bit access. In this case no CLI/SEI needed to make the access atomic.
Ian.M:
The problem is, on many MCUs you need a snapshot of the flags and timer high bit state at a single moment in time to unequivocally guarantee that you are dealing with the capture extension and timer overflow extension soft registers correctly without inadvertant race conditions if they are too close together that might result in out of order processing and a 65536 count error in the full extended capture time stamp.  As the interrupt flags and the timer high bit are in different registers and are hardware volatile, so can never be read atomically, repeating till you get no changes in the snapshot is needed.  The same approach will work on *ANY* MCU that doesn't have clear on read interrupt flags, or even by polling in your main loop if you cant hook the necessary interrupts.

On some MCUs with vectored interrupts at the same priority level you *MAY* be able to get away without a snapshot and use separate ISRs, but to confirm that requires a deep understanding of the specific MCU's interrupt structure and worst case latencies, and if any library code can hook the same interrupts or runs at a higher interrupt priority, is horribly fragile.
Kleinstein:
The correction inside the ISR depends on the ISR priorities and HW details. So it is hardware specific (should still work for all AVRs with ICP so far).
The AVRs don't have interrupt priorities in the sense of a higher priority interrupt interrupting another ISR. There is only a sequence in which the pending flages are checked. When doing only the correction (still keep the overflow flag active), there should be no problem if the lib code / Arduino environment would add extra code to the ISRs (as long as it would not enable nested interrupts before the timer code). Unless extremely long interrupt latency does not matter.

Repeated reading of capture register and software extension is quite tricky and would add other limitations form the loop speed. It does not really solve the problem and one would still have to handler the race condition case separate.
So I would not consider repeated reading in the main loop a viable solution. In the ISR there is no need to repeat the register readings - it's more like the opposite to read the ICP register only once, so that in case of 2 events one gets at least the correct time of one of the events and not in rare cases a wrong mixed value.
Ian.M:
To get a consistent state for further processing you only need two identical repeated reads of the interrupt flags + the timer high bit.   Initialise the old_state variable with a spare bit set and the state variable with the spare bit cleared to guarantee an initial mismatch  and it simply becomes:

--- Code: ---while(state!=old_state){
   old_state=state;
   state=pack_flags_and_timer_high_bit(); //replace with inline bitwise stuff
}
//Smash old_state here if in main loop

--- End code ---

That cant hang for more than four passes unless you have a storm of short pulses on either capture trigger pin as only three independent event sources contribute to the state. (Timer overflow is a dependent event - it can only happen coincident with a high bit 1 to 0 transition). Therefore, if in each timing run you disable each capture unit after a single acquisition or if you are making do with a single capture unit, disable it after the second acquisition, then execution time is strictly bounded.

There is absolutely no need to 'blind read' or repeatedly read any capture registers, or reread the timer or keep any part of the timer value except its high bit.   

If you handle the various cases in the main loop, then you don't use any ISRs, and don't enable the relevant interrupts.   AVR peripherals set their interrupt flag when their trigger condition is met even when that interrupt is masked or otherwise disabled, so one would simply poll the interrupt flags to grab the state as described above then handle the various cases, as if in an ISR except with worse latency (i.e. the superloop execution time).  With an AVR @16MHz, you've got 2ms for the full superloop including the worst case capture and timer handling code
Navigation
Message Index
Next page
Previous page
There was an error while thanking
Thanking...

Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod