Author Topic: Correct timing by timer + overflow count  (Read 13713 times)

0 Members and 1 Guest are viewing this topic.

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Correct timing by timer + overflow count
« on: September 08, 2015, 09:45:07 am »
Hi all,

This is more of an MCU programming question, that was mentioned in Jack Ganssle's latest newsletter. He presented the naive way of measuring time beyond a timer's bit-size limit: whenever the timer overflows, in the overflow interrupt, you increase a separate variable. Then, when you need to know the time, you take that variable and the current timer value, and do the math. 

Jack said this usually works but is guaranteed to fail eventually. I assume that's because between the time you access the variable and the time you access the timer value, an overflow may occur and the overall result will be false. 

This issue was actually presented as sort of an advertisement to one of Jack's seminars, so I hope I don't harm his business by asking this here  ;) - how does one overcome the problem?

It seems to me that the only really safe way is to halt the timer itself right before the reading. However, this pause will interfere with the accuracy of the timing, unless I do exact cycle-counting on the process (or activate a separate timer just for that) and add the missing time to the timer afterwards. This doesn't feel very elegant.

So, is there a better way?

Thanks!
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Correct timing by timer + overflow count
« Reply #1 on: September 08, 2015, 10:07:56 am »
You'll need a capture time (on that stops by hardware) otherwise your extra bit hack will have interrupt jitter.
But, an RTOS might make the jitter predictable. Which gives an known error you can put on your spec sheet.

https://en.wikipedia.org/wiki/Interrupt_latency
 

Online Psi

  • Super Contributor
  • ***
  • Posts: 9930
  • Country: nz
Re: Correct timing by timer + overflow count
« Reply #2 on: September 08, 2015, 10:12:52 am »
Use a stm32 and enable hardware master/slave over all of the timers :D

That's like 6x 16bit timers and one 32bit timer

A 128 bit counter at 48mhz overflows after 7,089,215,977,519,551,322,153,637,654,828 seconds
« Last Edit: September 08, 2015, 10:37:05 am by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline Chris C

  • Frequent Contributor
  • **
  • Posts: 259
  • Country: us
Re: Correct timing by timer + overflow count
« Reply #3 on: September 08, 2015, 10:13:06 am »
1) Disable the timer interrupt (preferably all interrupts, so we can execute the following in a predictably timely fashion).
2) Get a copy of the timer value and the overflow count.
3) Check the interrupt flag.  If set, then we know we have to add one to our copy of the overflow count, since the interrupt hasn't yet been able to do it.  The timer value is where uncertainly creeps in, as we don't know if the value overflowed prior to, or after, previously copying its value - and if it was at 999 of 1,000, adding another 1,000 via the overflow count would give us an incorrect result.  But we know for certain it's overflowed NOW!  So copy the timer value again, problem solved.
4) Re-enable interrupts.
5) Use the copy of value and overflow count to calculate our final time.

I figured this out on my own, the first time I encountered this problem.  There may be other ways to do it.  Jack has no monopoly on the solution. ;)
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #4 on: September 08, 2015, 10:33:37 am »
3) Check the interrupt flag.  If set, then we know we have to add one to our copy of the overflow count, since the interrupt hasn't yet been able to do it.  The timer value is where uncertainly creeps in, as we don't know if the value overflowed prior to, or after, previously copying its value - and if it was at 999 of 1,000, adding another 1,000 via the overflow count would give us an incorrect result.  But we know for certain it's overflowed NOW!  So copy the timer value again, problem solved.

Yes! That last step, of re-reading the timer value if the interrupt flag is set, is what I missed in my attempt to solve this earlier. Thanks!

Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline Chris C

  • Frequent Contributor
  • **
  • Posts: 259
  • Country: us
Re: Correct timing by timer + overflow count
« Reply #5 on: September 08, 2015, 10:34:51 am »
Here's an alternate solution.  It doesn't require disabling interrupts.  But does require a long enough timer interval, and short enough interrupts, that the following can ALWAYS run to completion in half the interval.  If you cannot guarantee that, it's fallible, which is why I usually use the prior approach.

1) Get a copy of the timer value (V1) and the overflow count (OC).
2) Get another, separate copy of the timer value (V2).  If V2 < V1, then resolve ambiguity by setting V1=V2, and getting OC again.
3) Compute time based on V1 and OC.
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: Correct timing by timer + overflow count
« Reply #6 on: September 08, 2015, 10:38:37 am »
Old-timer timer trick from 8-bit days for accessing long timers through small registers was to read the least most significant word(s) then least significant word, then read the most significant word again. If the most significant word changes you've sampled it during a roll-over, so repeat reading the least significant and most significant words again. The most significant word should be stable. Worst case is you'll stall for a few a handful of instructions.

If you do have to reread the registers you can roll take a few counts off the value to make allowance for the extra instruction cycles it took.
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #7 on: September 08, 2015, 10:47:35 am »
Use a stm32 and master/slave all of the timers to each other :D

That's like 6x 16bit timers and one 32bit timer

A 128 bit counter at 48mhz overflows after 7,089,215,977,519,551,322,153,637,654,828 seconds

Quite safe for everyday use, I guess, but if I want my system to outlast the universe, there may still be an issue  ;D

I actually got a cheap STM32 board lately and a programmer for it, but proper documentation for beginners seems hard to find. I guess I'll have to do some serious datasheet reading.


Thanks Chris C and hmaster_nz for the other suggestion. It sounds reasonable too  :)

Edit: Oh, and thanks Jeroen3, however I currently don't work with RTOSs.
« Last Edit: September 08, 2015, 10:49:17 am by igendel »
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #8 on: September 08, 2015, 12:11:16 pm »
This is pretty basic. Just disable interrupts while accessing the counter in RAM.  The timer still keeps running without any skew getting added.
I posted some example code to do this on an AVR last year.
http://nerdralph.blogspot.ca/2014/07/writing-avr-interrupt-service-routines.html
Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #9 on: September 08, 2015, 12:42:18 pm »
This is pretty basic. Just disable interrupts while accessing the counter in RAM.  The timer still keeps running without any skew getting

No, if I understand you correctly, that doesn't solve the problem Ganssle was talking about.

Let's say the timer is 8-bit. To get the time, I read it, disable interrupts, read the counter in RAM etc.
Now suppose I caught the timer when it was 255, one nanosecond before it overflowed. There is a possibility, I think, that the overflow interrupt will trip before my "main" code gets the chance to disable it. So it runs, the counter is +1'ed, timer now 0, and only then I get to disable the interrupt and read the (updated) counter. So I get the new RAM value but the old timer value, and that's a bug.

Same if I read the RAM counter first and the timer later.
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline Brutte

  • Frequent Contributor
  • **
  • Posts: 614
Re: Correct timing by timer + overflow count
« Reply #10 on: September 08, 2015, 02:29:13 pm »
I have asked a more generic question on avrfreaks some time ago.
By contrast, I was not interested in extending one timer register but rather whole timer.
I have found that implementations can be categorized in terms of "permissible delays" and range from "ridiculous" up to "timer period".
All (not buggy) extensions require runtime resolution of events which is PITA but doable. An example for resolution of only two events for AVR8 is available on the linked page.

Quote
This is pretty basic.
That is neither pretty nor basic.
 

Offline Chris C

  • Frequent Contributor
  • **
  • Posts: 259
  • Country: us
Re: Correct timing by timer + overflow count
« Reply #11 on: September 09, 2015, 12:38:29 am »
By contrast, I was not interested in extending one timer register but rather whole timer.

Virtual timers could accurately be called the heart of my custom PIC programming environment.

It takes a single real timer, and expands it to up to 16 virtual timers; with up to 32-bit resolution, regardless of what the real timer is.  It's bug-free, lean, can defer its own maintenance tasks in order to not delay an imminent event, and emulates interrupt chaining even if the MCU lacks that feature.  Glitchless PWM is built-in.  So is MIBAM, which can make every pin on an MCU capable of LED dimming, regardless of the number of pins, for about 5% CPU utilization.

Other functions can be hard coded in, or provided via a virtual interrupt (callback).  If any function will run too long, it can use what I call a "bridge" to pass data down to a lower priority interrupt; which does the real processing, and no longer interferes with other virtual timers.

It can be used to poll pins to implement virtual external interrupts, frequency counters, duty cycle measurement, quadrature decoding, and so on.  That obviously requires a trade-off between poll rate and CPU utilization, but it's still handy.  I can partially port my environment to a new MCU, and add native hardware support for these functions when it actually becomes necessary, or at my leisure.  And after that, I can still use it to extend what hardware peripherals are there, using hardware for the most demanding tasks, and software for the least.

And it can produce "0-tick error" when generating a single, larger timer.  Though I'm not sure how often that would actually be useful.  I feel that would usually be better addressed by starting with at least a 16-bit MCU in the first place.

It seems your search for prior art was limited to a single forum, and probably also too narrow in looking specifically for a timer extension.  You'll have better luck by Googling for finished and tested interrupt-driven software PWM implementations.  Which seem to be more common.  And if you think about it, they are nothing but virtual timers being used for a single purpose. 

Still, it seems relatively few people attempt anything of this nature.  Or get it right on the first try.  So by all means, publish your project!
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #12 on: September 09, 2015, 04:41:32 am »
This is pretty basic. Just disable interrupts while accessing the counter in RAM.  The timer still keeps running without any skew getting

No, if I understand you correctly, that doesn't solve the problem Ganssle was talking about.

Let's say the timer is 8-bit. To get the time, I read it, disable interrupts, read the counter in RAM etc.
Now suppose I caught the timer when it was 255, one nanosecond before it overflowed. There is a possibility, I think, that the overflow interrupt will trip before my "main" code gets the chance to disable it. So it runs, the counter is +1'ed, timer now 0, and only then I get to disable the interrupt and read the (updated) counter. So I get the new RAM value but the old timer value, and that's a bug.

Same if I read the RAM counter first and the timer later.

You obviously didn't look at my code and I'm guessing you didn't even read the whole blog post that explains the code.
The getseconds() function is only a few lines of C.
Now if you need a microsecond-resolution timer things get a bit more complicated, but you never said that is a requirement.

Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Correct timing by timer + overflow count
« Reply #13 on: September 09, 2015, 05:34:57 am »
Still, it seems relatively few people attempt anything of this nature.  Or get it right on the first try.  So by all means, publish your project!
Virtual timers are often included in operating systems, few people have time to roll their own.
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #14 on: September 09, 2015, 06:37:28 am »
You obviously didn't look at my code and I'm guessing you didn't even read the whole blog post that explains the code.
The getseconds() function is only a few lines of C.
Now if you need a microsecond-resolution timer things get a bit more complicated, but you never said that is a requirement.

It might be my own daftness, but even now after re-reading the post you linked to, I don't see how it relates to the very specific issue I raised in the original post.
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline Brutte

  • Frequent Contributor
  • **
  • Posts: 614
Re: Correct timing by timer + overflow count
« Reply #15 on: September 09, 2015, 10:34:47 am »
Virtual timers could accurately be called the heart of my custom PIC programming environment.
That was not the subject of the Muse.
Quote
You'll have better luck by Googling for finished and tested interrupt-driven software PWM implementations. 
Nor software PWM.
As stated before:
Quote
all implementations can be categorized in terms of "permissible delays" and range from "ridiculous" up to "timer period".
the software PWM falls into "ridiculous" category because to obtain a "0-tick error" you would have to pinpoint toggling code in exact instruction clock.
Quote
Still, it seems relatively few people attempt anything of this nature.  Or get it right on the first try.  So by all means, publish your project!
I have already abandoned that sinking ship.
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #16 on: September 09, 2015, 12:05:05 pm »
You obviously didn't look at my code and I'm guessing you didn't even read the whole blog post that explains the code.
The getseconds() function is only a few lines of C.
Now if you need a microsecond-resolution timer things get a bit more complicated, but you never said that is a requirement.

It might be my own daftness, but even now after re-reading the post you linked to, I don't see how it relates to the very specific issue I raised in the original post.
My solution provides a glitch-free timer.  Isn't that what you want?
If you want to do it the more complicated way, reading the timer register and combining it with the overflow count, it's a bit more code, but still not rocket science.  After cli(), if the overflow interrupt flag is set and the timer register value is < 128 then add 1 to the overflow value.
Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #17 on: September 09, 2015, 03:36:25 pm »
My solution provides a glitch-free timer.  Isn't that what you want?

As a by-product, perhaps. My original question was about the technique of counting time specifically using overflow counting + timer value.

If you want to do it the more complicated way, reading the timer register and combining it with the overflow count, it's a bit more code, but still not rocket science.  After cli(), if the overflow interrupt flag is set and the timer register value is < 128 then add 1 to the overflow value.

Yes, in the vein of some answers given above. It all boils down to checking, in retrospect, whether an overflow occurred while I was "taking time";  the exact details of the implementation seem to be a matter of taste and resource constraints. Thanks!
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2803
  • Country: nz
Re: Correct timing by timer + overflow count
« Reply #18 on: September 09, 2015, 11:40:04 pm »
My solution provides a glitch-free timer.  Isn't that what you want?
If you want to do it the more complicated way, reading the timer register and combining it with the overflow count, it's a bit more code, but still not rocket science.  After cli(), if the overflow interrupt flag is set and the timer register value is < 128 then add 1 to the overflow value.

I didn't read your code, but based on the description "This is pretty basic. Just disable interrupts while accessing the counter in RAM.  The timer still keeps running without any skew getting added" are you sure that it is glitch free?

Right column is RAM_Counter:Timer Counter for a 16 bit counter, left column is the instructions being run....

0000:FFFF disable interrupts
0000:FFFF read timer.   Gives xxxx:FFFFF for timer value (the upper half comes from RAM)
 --- timer rolls over here ---
0000:0000 read RAM  - GIves 0000:FFFF for timer value
0000:0000 check timer's IRQ flag = true
0000:0000 enable interrupts
-- IRQ happens --
0001:0000 IRQ flag is set, so inc reading from RAM (0000:FFFF => 00001:FFFF)

Or how about:

0000:FFFF disable interrupts
0000:0000 read timer.   Gives xxxx:FFFF for timer value (the upper half comes from RAM)
0000:0000 read RAM  - GIves 0000:FFFF for timer value
 --- timer rolls over here ---
0000:0000 check timer's IRQ flag = true
0000:0000 enable interrupts
-- IRQ happens --
0001:0000 IRQ flag is set, so inc reading from RAM (0000:FFFF => 00001:FFFF)

I guess you are now about to suggest that if an IRQ was flagged you look at the value from the h/w timer, and decide if the IRQ was raised before of after the timer was read (based on the high bit).

Why not just keep it simple...

   read RAM value into reg1
   read H/W timer into reg2
   read RAM value into reg3
   if reg3 != reg1
      clear reg2 to zero
   use reg3:reg2 as timer value

You don't even need to disable interrupts...
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #19 on: September 10, 2015, 02:52:54 am »
If you want to do it the more complicated way, reading the timer register and combining it with the overflow count, it's a bit more code, but still not rocket science.  After cli(), if the overflow interrupt flag is set and the timer register value is < 128 then add 1 to the overflow value.

Yes, in the vein of some answers given above. It all boils down to checking, in retrospect, whether an overflow occurred while I was "taking time";  the exact details of the implementation seem to be a matter of taste and resource constraints. Thanks!

I can only think of one way to do it, and it is straightforward enough that I doubt there's a better way: (code tested only in my head)
Code: [Select]
__uint24 get_ticks()
{
  uint16_t ovfl;
  uint8_t timerval;
  cli();
  ovfl = _overflows;
  timerval = TCNT0;
  if ( (TIFR0 & (1<<TOV0)) && (timerval < 128) ) ovfl++;
  sei();
  return ( (ovfl << 8) | timerval );
}
Unthinking respect for authority is the greatest enemy of truth. Einstein
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26896
  • Country: nl
    • NCT Developments
Re: Correct timing by timer + overflow count
« Reply #20 on: September 10, 2015, 04:19:26 am »
This is pretty basic. Just disable interrupts while accessing the counter in RAM.  The timer still keeps running without any skew getting
No, if I understand you correctly, that doesn't solve the problem Ganssle was talking about.

Let's say the timer is 8-bit. To get the time, I read it, disable interrupts, read the counter in RAM etc.
Now suppose I caught the timer when it was 255, one nanosecond before it overflowed. There is a possibility, I think, that the overflow interrupt will trip before my "main" code gets the chance to disable it. So it runs, the counter is +1'ed, timer now 0, and only then I get to disable the interrupt and read the (updated) counter. So I get the new RAM value but the old timer value, and that's a bug.

Same if I read the RAM counter first and the timer later.
Interesting problem... The timer interrupt adds extra difficulties due to parallel process synchronisation. It is not a particular timer problem but a process synchronisation problem. If you don't use a timer interrupt but just poll the counter from a main loop you can create a global time counter in a very simple way. Every time you read a value from the counter which is less than the previous value an overflow has occured. If you need the instant time just call the routine which determines the time. The only requirement is that the counter is polled often enough so that an overflow do not happen unnoticed.
« Last Edit: September 10, 2015, 04:36:17 am by nctnico »
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #21 on: September 10, 2015, 11:24:30 am »
I can only think of one way to do it, and it is straightforward enough that I doubt there's a better way: (code tested only in my head)
Code: [Select]
__uint24 get_ticks()
{
  uint16_t ovfl;
  uint8_t timerval;
  cli();
  ovfl = _overflows;
  timerval = TCNT0;
  if ( (TIFR0 & (1<<TOV0)) && (timerval < 128) ) ovfl++;
  sei();
  return ( (ovfl << 8) | timerval );
}

Why do you check whether timerval < 128?
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3238
  • Country: gb
Re: Correct timing by timer + overflow count
« Reply #22 on: September 10, 2015, 11:48:08 am »
Why do you check whether timerval < 128?

Think about what would happen if the overflow occurred after TCNT0 had been read.
 

Offline igendelTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: il
    • It's Every Bit For Itself (Programming & MCU blog)
Re: Correct timing by timer + overflow count
« Reply #23 on: September 10, 2015, 12:00:49 pm »
Why do you check whether timerval < 128?

Think about what would happen if the overflow occurred after TCNT0 had been read.

Actually that's another bug in the code above. If the OF flag is raised, it should re-read TCNT0 to get its updated value, not just increase the overflow count. This has nothing to do with the number 128. [edit: perhaps no need to re-read TCNT0, but the 128 check is still redundant]
« Last Edit: September 10, 2015, 12:03:44 pm by igendel »
Maker projects, tutorials etc. on my Youtube channel: https://www.youtube.com/user/idogendel/
 

Offline ralphd

  • Frequent Contributor
  • **
  • Posts: 445
  • Country: ca
    • Nerd Ralph
Re: Correct timing by timer + overflow count
« Reply #24 on: September 11, 2015, 12:39:14 am »
Why do you check whether timerval < 128?

Think about what would happen if the overflow occurred after TCNT0 had been read.

Actually that's another bug in the code above. If the OF flag is raised, it should re-read TCNT0 to get its updated value, not just increase the overflow count. This has nothing to do with the number 128. [edit: perhaps no need to re-read TCNT0, but the 128 check is still redundant]

Sounds like it is rocket science to you.   :palm:
Unthinking respect for authority is the greatest enemy of truth. Einstein
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf