EmbeddedRelated.com
Forums

How to read a 32-bits hw counter on 8-bits microcontroller

Started by pozzugno October 15, 2014
In the thread above about RS485 driver enable, always originated by me, 
some new and interesting (for me) techniques has been descripted to use 
a "wide" (32- or 64-bits) hardware up/down counter to read effectively 
without disabling interrupts, as I usually have done in the past.

If the processor supports native hardware 32-bits counter, the problem 
doesn't exist: most probably it can be read atomically.  Most probably 
there are some 8-bits architectures where it's possible to read 
atomically a 32-bits counter, but they are exceptions.

Suppose to have a poor 8-bits micro with only 8-bits counters.  I want 
to extend it to 32-bits, starting from the suggestions by Wouter van 
Ooijen/Don Y:

     >h1 = read_high();
     >l1 = read_low();
     >h2 = read_high();
     >if h1 == h2 return ( h1, l1 ) else return ( h2, 0 )

and David Brown:

     >On an 8-bit system with 64-bit counters, the "read_high"
     >should read the upper 56 bits, and the "read_low" reads the low
     >8-bit (or use a 48-bit/16-bit split if you can do an atomic 16-bit
     >read of the counter hardware, which IIRC is possible on an AVR).

When the 8-bits counter wraps-around, the relative ISR is called and the 
upper 24-bits are incremented:

     volatile uint32_t counter_high = 0;
     Timer/Counter ISR:
       counter_high += 0x10;

To read the 32-bits value, without disabling interrupts, I can use (I'm 
assuming the compiler have a native support of uint32_t):

     uint32_t read_counter(void) {
       uint32_t h1 = counter_high;
       uint8_t l = COUNTER_REGISTER;   /* Atomic operation */
       uint32_t h2 = counter_high;
       return h1 == h2 ? h1 + l : h2;
     }

Now a generic "software" timer can be set and checked in the following way:

     typedef uint32_t Timer;
     void TimerSet(Timer *tmr, uint32_t delay) {
       *tmr = read_counter() + delay;
     }
     bool_t TimerExpired(Timer *tmr) {
       return (read_counter() - *tmr) <= 0x7FFFFFFFU);
     }

With the comparison in TimerExpired(), I can set a maximum delay of 
0x7FFFFFFFU, but usually it's not a problem.

Do you think it is correct?


IMHO, it's very good to have a sufficiently wide counter that will never 
wraps around (in a reasonably time).  This happens only if the counter 
is 64-bits, even for a counting frequency of 1GHz (1ns).  If the counter 
is "only" 32-bits wide, even with a low counting frequency like 1kHz 
(1ms), it wraps around after "only" about 50 days.

The comparison in TimerExpired() would be simpler and the maximum delay 
would be greater:

     bool_t TimerExpired(Timer *tmr) {
       return read_counter() > *tmr;
     }
pozzugno schreef op 15-Oct-14 9:33 AM:
> In the thread above about RS485 driver enable, always originated by me, > some new and interesting (for me) techniques has been descripted to use > a "wide" (32- or 64-bits) hardware up/down counter to read effectively > without disabling interrupts, as I usually have done in the past. > > If the processor supports native hardware 32-bits counter, the problem > doesn't exist: most probably it can be read atomically. Most probably > there are some 8-bits architectures where it's possible to read > atomically a 32-bits counter, but they are exceptions. > > Suppose to have a poor 8-bits micro with only 8-bits counters. I want > to extend it to 32-bits, starting from the suggestions by Wouter van > Ooijen/Don Y: > > >h1 = read_high(); > >l1 = read_low(); > >h2 = read_high(); > >if h1 == h2 return ( h1, l1 ) else return ( h2, 0 ) > > and David Brown: > > >On an 8-bit system with 64-bit counters, the "read_high" > >should read the upper 56 bits, and the "read_low" reads the low > >8-bit (or use a 48-bit/16-bit split if you can do an atomic 16-bit > >read of the counter hardware, which IIRC is possible on an AVR). > > When the 8-bits counter wraps-around, the relative ISR is called and the > upper 24-bits are incremented: > > volatile uint32_t counter_high = 0; > Timer/Counter ISR: > counter_high += 0x10; > > To read the 32-bits value, without disabling interrupts, I can use (I'm > assuming the compiler have a native support of uint32_t): > > uint32_t read_counter(void) { > uint32_t h1 = counter_high; > uint8_t l = COUNTER_REGISTER; /* Atomic operation */ > uint32_t h2 = counter_high; > return h1 == h2 ? h1 + l : h2; > }
I think the reading of an 8-bit register on an 8-bit chip is always atomic. To make ( h1 + 1 ) valid you must increment counter_high in the ISR by 0x100, not 0x10. For an 8-bit chip various optimizations could be made (which might require using assembler), for instance h1 == h2 needs to check only the lowest-but-one byte, and the counter_high could be handled as a 3-byte integer.
> > Now a generic "software" timer can be set and checked in the following way: > > typedef uint32_t Timer; > void TimerSet(Timer *tmr, uint32_t delay) { > *tmr = read_counter() + delay; > } > bool_t TimerExpired(Timer *tmr) { > return (read_counter() - *tmr) <= 0x7FFFFFFFU); > } > > With the comparison in TimerExpired(), I can set a maximum delay of > 0x7FFFFFFFU, but usually it's not a problem. > > Do you think it is correct?
I am not a language lawyer, but substracting two uinit32_t where the result could be negative does not feel good to me. When I use this method I don't think of using timers, but of handling moments in time. So I would write something like time_t message_timeout = now() + 100 * ms; while( ... ){ . . . if( now() > message_timeout ){ . . . } } Or, for a busy delay void wait_busy( time_t duration ){ time_t end = now() + duration; while( now() < end ){} } That would be the C version. I mostly work in C++, where I use an ADT for time_t, and I distinguish between absolute time (returned by now()) and relative time (constexpr ns, us, ms, s, etc). But maybe I should thing in timers. That matches better with the way I use time in threads. I'll give it a try.
> IMHO, it's very good to have a sufficiently wide counter that will never > wraps around (in a reasonably time). This happens only if the counter > is 64-bits, even for a counting frequency of 1GHz (1ns). If the counter > is "only" 32-bits wide, even with a low counting frequency like 1kHz > (1ms), it wraps around after "only" about 50 days. > > The comparison in TimerExpired() would be simpler and the maximum delay > would be greater: > > bool_t TimerExpired(Timer *tmr) { > return read_counter() > *tmr; > }
I used to work with PICs a lot, but afew years ago I switched to Cortex. It is a relieve not having to worry about 8-bit and 16-bit integers and the speed penalty of using 32-bits any more. And C++ is so much more fun than C. Wouter van Ooijen
On 15/10/14 09:33, pozzugno wrote:
> In the thread above about RS485 driver enable, always originated by me, > some new and interesting (for me) techniques has been descripted to use > a "wide" (32- or 64-bits) hardware up/down counter to read effectively > without disabling interrupts, as I usually have done in the past. > > If the processor supports native hardware 32-bits counter, the problem > doesn't exist: most probably it can be read atomically. Most probably > there are some 8-bits architectures where it's possible to read > atomically a 32-bits counter, but they are exceptions. > > Suppose to have a poor 8-bits micro with only 8-bits counters. I want > to extend it to 32-bits, starting from the suggestions by Wouter van > Ooijen/Don Y: > > >h1 = read_high(); > >l1 = read_low(); > >h2 = read_high(); > >if h1 == h2 return ( h1, l1 ) else return ( h2, 0 ) > > and David Brown: > > >On an 8-bit system with 64-bit counters, the "read_high" > >should read the upper 56 bits, and the "read_low" reads the low > >8-bit (or use a 48-bit/16-bit split if you can do an atomic 16-bit > >read of the counter hardware, which IIRC is possible on an AVR). > > When the 8-bits counter wraps-around, the relative ISR is called and the > upper 24-bits are incremented: > > volatile uint32_t counter_high = 0; > Timer/Counter ISR: > counter_high += 0x10; >
I have two points here. The first is simple - you should be adding 0x0100, not 0x10. (I'm assuming your 8-bit hardware counter counts up - sometimes they are set to count down.) The second issue is about code efficiency - size and speed. Using "volatile" imposes restraints on the compiler, and should be used when necessary but /only/ when necessary. It is commonly the case that you need the "volatile" access when using a variable like counter_high from outside the ISR, but that "volatile" is unnecessary inside the ISR. In this case, without the "volatile" the compiler could skip the read and write of the lowest byte, and it could also do the addition with fewer registers which would mean less pushing and popping for the ISR. (I don't know if current avr-gcc does such optimisation, but it /could/ do them.) It will certainly help if you use counter_high for anything else in the same ISR. So personally, I would write: #define volatileAccess(v) *((volatile typeof((v)) *) &(v)) static uint32_t counter_high = 0; // No "volatile" Timer/Counter ISR: counter_high += 0x0100; But when reading counter_high in read_counter, I would use: uint32_t h1 = volatileAccess(counter_high); This also helps make it clear that it is /accesses/ that are volatile, not the variable itself. Keep counter_high "static" to avoid it being misused by code in other files.
> To read the 32-bits value, without disabling interrupts, I can use (I'm > assuming the compiler have a native support of uint32_t): > > uint32_t read_counter(void) { > uint32_t h1 = counter_high; > uint8_t l = COUNTER_REGISTER; /* Atomic operation */ > uint32_t h2 = counter_high; > return h1 == h2 ? h1 + l : h2; > } >
uint32_t read_counter(void) { uint32_t h1 = volatileAccess(counter_high); uint8_t l = COUNTER_REGISTER; /* Atomic operation */ uint32_t h2 = volatileAccess(counter_high); return (h1 == h2) ? (h1 + l) : h2; }
> Now a generic "software" timer can be set and checked in the following way: > > typedef uint32_t Timer; > void TimerSet(Timer *tmr, uint32_t delay) { > *tmr = read_counter() + delay; > } > bool_t TimerExpired(Timer *tmr) { > return (read_counter() - *tmr) <= 0x7FFFFFFFU); > }
You are using an AVR - don't use pointers unless you /really/ have to, as they are very inefficient. Even on a processor with good address registers, there is no point in using pointers here - it makes code less clear and may reduce optimisation flexibility. In particular, functions like these can often be inlined to give reduced code size (as well as faster code), and that works better without an extra pointer layer. typedef uint32_t Timer; Timer TimerSet(uint32_t delay) { return read_counter() + delay; } bool TimerExpired(Timer tmr) { return ((int32_t) (read_counter() - tmr)) >= 0; } Converting the time difference to a signed int lets you compare it to 0 instead of a fixed unsigned number. It is easier to write, easier to read, easier to modify if you change everything to 64-bit, and may give better code.
> > With the comparison in TimerExpired(), I can set a maximum delay of > 0x7FFFFFFFU, but usually it's not a problem. > > Do you think it is correct?
Yes, apart from your 0x10 / 0x0100 typo, your code was correct.
> > > IMHO, it's very good to have a sufficiently wide counter that will never > wraps around (in a reasonably time). This happens only if the counter > is 64-bits, even for a counting frequency of 1GHz (1ns). If the counter > is "only" 32-bits wide, even with a low counting frequency like 1kHz > (1ms), it wraps around after "only" about 50 days. > > The comparison in TimerExpired() would be simpler and the maximum delay > would be greater: > > bool_t TimerExpired(Timer *tmr) { > return read_counter() > *tmr; > }
On 15/10/14 10:03, Wouter van Ooijen wrote:
> pozzugno schreef op 15-Oct-14 9:33 AM:
>> >> Now a generic "software" timer can be set and checked in the following >> way: >> >> typedef uint32_t Timer; >> void TimerSet(Timer *tmr, uint32_t delay) { >> *tmr = read_counter() + delay; >> } >> bool_t TimerExpired(Timer *tmr) { >> return (read_counter() - *tmr) <= 0x7FFFFFFFU); >> } >> >> With the comparison in TimerExpired(), I can set a maximum delay of >> 0x7FFFFFFFU, but usually it's not a problem. >> >> Do you think it is correct? > > I am not a language lawyer, but substracting two uinit32_t where the > result could be negative does not feel good to me. >
I am not a language lawyer either, but I listen to others on comp.lang.c Overflow and wrapping is fully defined for unsigned arithmetic - so subtracting two uint32_t values will work as expected even if you subtract a large value from a small value. But you won't get a negative result - you will get an unsigned result greater than 0x7fff'ffff. In my post, I suggested casting the result of the subtraction to a signed int, and checking for the sign of the result: return ((int32_t) (read_counter() - tmr)) >= 0; That actually has more issues, since the standard does not define what happens when an unsigned value greater than 0x7fff'ffff is converted into an int32_t - it is "implementation defined". I am confident that avr-gcc will do the expected thing here - but in theory a compiler could decide that the conversion gave the absolute value of the two's complement negative number. This would then let the compiler optimise that return statement to "return true;". However, since such conversions are "implementation defined" the compiler has to be consistent, and anything other than the "obvious" behaviour would usually lead to worse code - so the compiler must /always/ do the "obvious" thing. If the conversion had been "undefined behaviour" then it would be a different matter - the compiler could behave differently at different times.
Il 15/10/2014 10:03, Wouter van Ooijen ha scritto:
> pozzugno schreef op 15-Oct-14 9:33 AM:
> I think the reading of an 8-bit register on an 8-bit chip is always atomic.
I think it too.
> To make ( h1 + 1 ) valid you must increment counter_high in the ISR by > 0x100, not 0x10.
Yes, of course. It was a typo.
> For an 8-bit chip various optimizations could be made (which might > require using assembler), for instance h1 == h2 needs to check only the > lowest-but-one byte, and the counter_high could be handled as a 3-byte > integer.
Yes, here I'm more interested in the general aspect.
>> Now a generic "software" timer can be set and checked in the following >> way: >> >> typedef uint32_t Timer; >> void TimerSet(Timer *tmr, uint32_t delay) { >> *tmr = read_counter() + delay; >> } >> bool_t TimerExpired(Timer *tmr) { >> return (read_counter() - *tmr) <= 0x7FFFFFFFU); >> } >> >> With the comparison in TimerExpired(), I can set a maximum delay of >> 0x7FFFFFFFU, but usually it's not a problem. >> >> Do you think it is correct? > > I am not a language lawyer, but substracting two uinit32_t where the > result could be negative does not feel good to me.
As David Brown wrote, it is well defined in C standard.
> When I use this method I don't think of using timers, but of handling > moments in time. So I would write something like > > time_t message_timeout = now() + 100 * ms; > while( ... ){ > . . . > if( now() > message_timeout ){ > . . . > } > }
I think it's very similar to my approach. Your time_t message_timeout = now() + 100 * ms; is the same as my Timer tmr; TimerSet(&tmr, 100 * ms); And your if (now() > message_timeout) { is the same as my if (TimerExpired(&tmr)) { And your comparison doesn't care about wrap-around!!
> Or, for a busy delay > > void wait_busy( time_t duration ){ > time_t end = now() + duration; > while( now() < end ){} > }
It's the same as: void wait_busy(Timer duration) { Timer end; TimerSet(&end, duration); while(!TimerExpired(&end){} }
> That would be the C version. I mostly work in C++, where I use an ADT > for time_t, and I distinguish between absolute time (returned by now()) > and relative time (constexpr ns, us, ms, s, etc). > > But maybe I should thing in timers. That matches better with the way I > use time in threads. I'll give it a try. > >> IMHO, it's very good to have a sufficiently wide counter that will never >> wraps around (in a reasonably time). This happens only if the counter >> is 64-bits, even for a counting frequency of 1GHz (1ns). If the counter >> is "only" 32-bits wide, even with a low counting frequency like 1kHz >> (1ms), it wraps around after "only" about 50 days. >> >> The comparison in TimerExpired() would be simpler and the maximum delay >> would be greater: >> >> bool_t TimerExpired(Timer *tmr) { >> return read_counter() > *tmr; >> } > > I used to work with PICs a lot, but afew years ago I switched to Cortex.
I am exactly in this situation, except I started with AVRs (and only a few PICs).
> It is a relieve not having to worry about 8-bit and 16-bit integers and > the speed penalty of using 32-bits any more.
How you are right.
> And C++ is so much more fun than C.
Yes? Why?
Il 15/10/2014 10:14, David Brown ha scritto:
> On 15/10/14 09:33, pozzugno wrote: > > I have two points here. The first is simple - you should be adding > 0x0100, not 0x10. (I'm assuming your 8-bit hardware counter counts up - > sometimes they are set to count down.)
Yes, it was a typo.
> The second issue is about code efficiency - size and speed. Using > "volatile" imposes restraints on the compiler, and should be used when > necessary but /only/ when necessary. It is commonly the case that you > need the "volatile" access when using a variable like counter_high from > outside the ISR, but that "volatile" is unnecessary inside the ISR. In > this case, without the "volatile" the compiler could skip the read and > write of the lowest byte, and it could also do the addition with fewer > registers which would mean less pushing and popping for the ISR. (I > don't know if current avr-gcc does such optimisation, but it /could/ do > them.) It will certainly help if you use counter_high for anything else > in the same ISR. > > So personally, I would write: > > #define volatileAccess(v) *((volatile typeof((v)) *) &(v)) > > static uint32_t counter_high = 0; // No "volatile" > Timer/Counter ISR: > counter_high += 0x0100; > > But when reading counter_high in read_counter, I would use: > uint32_t h1 = volatileAccess(counter_high); > > This also helps make it clear that it is /accesses/ that are volatile, > not the variable itself. Keep counter_high "static" to avoid it being > misused by code in other files.
Good suggestion, thank you very much :-)
>> Now a generic "software" timer can be set and checked in the following way: >> >> typedef uint32_t Timer; >> void TimerSet(Timer *tmr, uint32_t delay) { >> *tmr = read_counter() + delay; >> } >> bool_t TimerExpired(Timer *tmr) { >> return (read_counter() - *tmr) <= 0x7FFFFFFFU); >> } > > You are using an AVR - don't use pointers unless you /really/ have to, > as they are very inefficient.
Yes, I know, but here I wanted to stress just the tecnique to access a 32-bits counter on an 8-bit microcontroller. Anyway I would implement TimerSet and TimerExpired as inline functions (or macro), so the compiler is able to convert pointer arithmetic to direct assignment in most cases. IMHO the API with pointers are more generic if, in the future, I'll change Timer to a complex structure.
> Even on a processor with good address > registers, there is no point in using pointers here - it makes code less > clear and may reduce optimisation flexibility. In particular, functions > like these can often be inlined to give reduced code size (as well as > faster code), and that works better without an extra pointer layer. > > typedef uint32_t Timer; > Timer TimerSet(uint32_t delay) { > return read_counter() + delay; > } > bool TimerExpired(Timer tmr) { > return ((int32_t) (read_counter() - tmr)) >= 0; > } > > Converting the time difference to a signed int lets you compare it to 0 > instead of a fixed unsigned number. It is easier to write, easier to > read, easier to modify if you change everything to 64-bit, and may give > better code.
As you observed in your other post, it brings to an "implementation defined" behaviour.
On 15/10/14 12:24, pozzugno wrote:
> Il 15/10/2014 10:14, David Brown ha scritto: >> On 15/10/14 09:33, pozzugno wrote: >> >> I have two points here. The first is simple - you should be adding >> 0x0100, not 0x10. (I'm assuming your 8-bit hardware counter counts up - >> sometimes they are set to count down.) > > Yes, it was a typo. > > >> The second issue is about code efficiency - size and speed. Using >> "volatile" imposes restraints on the compiler, and should be used when >> necessary but /only/ when necessary. It is commonly the case that you >> need the "volatile" access when using a variable like counter_high from >> outside the ISR, but that "volatile" is unnecessary inside the ISR. In >> this case, without the "volatile" the compiler could skip the read and >> write of the lowest byte, and it could also do the addition with fewer >> registers which would mean less pushing and popping for the ISR. (I >> don't know if current avr-gcc does such optimisation, but it /could/ do >> them.) It will certainly help if you use counter_high for anything else >> in the same ISR. >> >> So personally, I would write: >> >> #define volatileAccess(v) *((volatile typeof((v)) *) &(v)) >> >> static uint32_t counter_high = 0; // No "volatile" >> Timer/Counter ISR: >> counter_high += 0x0100; >> >> But when reading counter_high in read_counter, I would use: >> uint32_t h1 = volatileAccess(counter_high); >> >> This also helps make it clear that it is /accesses/ that are volatile, >> not the variable itself. Keep counter_high "static" to avoid it being >> misused by code in other files. > > Good suggestion, thank you very much :-) > > >>> Now a generic "software" timer can be set and checked in the >>> following way: >>> >>> typedef uint32_t Timer; >>> void TimerSet(Timer *tmr, uint32_t delay) { >>> *tmr = read_counter() + delay; >>> } >>> bool_t TimerExpired(Timer *tmr) { >>> return (read_counter() - *tmr) <= 0x7FFFFFFFU); >>> } >> >> You are using an AVR - don't use pointers unless you /really/ have to, >> as they are very inefficient. > > Yes, I know, but here I wanted to stress just the tecnique to access a > 32-bits counter on an 8-bit microcontroller. > > Anyway I would implement TimerSet and TimerExpired as inline functions > (or macro), so the compiler is able to convert pointer arithmetic to > direct assignment in most cases.
Prefer "static inline" functions to macros whenever possible. Usually pointers can be converted to direct assignment when inlining, but not always - you are more likely to have situations where the data can "leak" to other modules (or that the compiler cannot easily prove that there is no leak), and thus limit optimisation.
> > IMHO the API with pointers are more generic if, in the future, I'll > change Timer to a complex structure.
That can sometimes be the case, but not here - the code must change if the Timer type changes. As Wouter suggests, you can always change to C++ - then you could make your Timer's into a class and write generic code that works for timers of different sizes. It can be more fun - but don't forget that generalisation is wasted if you only ever need the one size.
> > >> Even on a processor with good address >> registers, there is no point in using pointers here - it makes code less >> clear and may reduce optimisation flexibility. In particular, functions >> like these can often be inlined to give reduced code size (as well as >> faster code), and that works better without an extra pointer layer. >> >> typedef uint32_t Timer; >> Timer TimerSet(uint32_t delay) { >> return read_counter() + delay; >> } >> bool TimerExpired(Timer tmr) { >> return ((int32_t) (read_counter() - tmr)) >= 0; >> } >> >> Converting the time difference to a signed int lets you compare it to 0 >> instead of a fixed unsigned number. It is easier to write, easier to >> read, easier to modify if you change everything to 64-bit, and may give >> better code. > > As you observed in your other post, it brings to an "implementation > defined" behaviour. >
Yes - but you have a fixed implementation, so implementation defined behaviour is fine if it does what you want. But you may need to consider such things in the future. (In this particular case, any sane compiler will have the same implementation-defined behaviour on sane processors. You only need to worry if you have one's complement arithmetic or weird sized integers.)
pozzugno schreef op 15-Oct-14 12:19 PM:
> Il 15/10/2014 10:03, Wouter van Ooijen ha scritto: >> pozzugno schreef op 15-Oct-14 9:33 AM: > >> I think the reading of an 8-bit register on an 8-bit chip is always >> atomic. > > I think it too. > > >> To make ( h1 + 1 ) valid you must increment counter_high in the ISR by >> 0x100, not 0x10. > > Yes, of course. It was a typo. > > >> For an 8-bit chip various optimizations could be made (which might >> require using assembler), for instance h1 == h2 needs to check only the >> lowest-but-one byte, and the counter_high could be handled as a 3-byte >> integer. > > Yes, here I'm more interested in the general aspect. > > >>> Now a generic "software" timer can be set and checked in the following >>> way: >>> >>> typedef uint32_t Timer; >>> void TimerSet(Timer *tmr, uint32_t delay) { >>> *tmr = read_counter() + delay; >>> } >>> bool_t TimerExpired(Timer *tmr) { >>> return (read_counter() - *tmr) <= 0x7FFFFFFFU); >>> } >>> >>> With the comparison in TimerExpired(), I can set a maximum delay of >>> 0x7FFFFFFFU, but usually it's not a problem. >>> >>> Do you think it is correct? >> >> I am not a language lawyer, but substracting two uinit32_t where the >> result could be negative does not feel good to me. > > As David Brown wrote, it is well defined in C standard. > > >> When I use this method I don't think of using timers, but of handling >> moments in time. So I would write something like >> >> time_t message_timeout = now() + 100 * ms; >> while( ... ){ >> . . . >> if( now() > message_timeout ){ >> . . . >> } >> } > > I think it's very similar to my approach. Your > > time_t message_timeout = now() + 100 * ms; > > is the same as my > > Timer tmr; > TimerSet(&tmr, 100 * ms); > > And your > > if (now() > message_timeout) { > > is the same as my > > if (TimerExpired(&tmr)) { > > And your comparison doesn't care about wrap-around!!
No need to, I work with 64 bit time_t only, and I won't be around when that wraps around.
> > >> Or, for a busy delay >> >> void wait_busy( time_t duration ){ >> time_t end = now() + duration; >> while( now() < end ){} >> } > > It's the same as: > > void wait_busy(Timer duration) { > Timer end; > TimerSet(&end, duration); > while(!TimerExpired(&end){} > }
Yes, it boils down to the same effect, but the it looks (and reads) different in the source. Which I do care about.
> >> That would be the C version. I mostly work in C++, where I use an ADT >> for time_t, and I distinguish between absolute time (returned by now()) >> and relative time (constexpr ns, us, ms, s, etc). >> >> But maybe I should thing in timers. That matches better with the way I >> use time in threads. I'll give it a try. >> >>> IMHO, it's very good to have a sufficiently wide counter that will never >>> wraps around (in a reasonably time). This happens only if the counter >>> is 64-bits, even for a counting frequency of 1GHz (1ns). If the counter >>> is "only" 32-bits wide, even with a low counting frequency like 1kHz >>> (1ms), it wraps around after "only" about 50 days. >>> >>> The comparison in TimerExpired() would be simpler and the maximum delay >>> would be greater: >>> >>> bool_t TimerExpired(Timer *tmr) { >>> return read_counter() > *tmr; >>> } >> >> I used to work with PICs a lot, but afew years ago I switched to Cortex. > > I am exactly in this situation, except I started with AVRs (and only a > few PICs).
Better architecture, worse manufacturer. But you do have GCC! (But I would still drop those 8-bitter and switch to Cortex if I were you.)
> >> It is a relieve not having to worry about 8-bit and 16-bit integers and >> the speed penalty of using 32-bits any more. > > How you are right. > > >> And C++ is so much more fun than C. > > Yes? Why?
Better abstraction mechanisms. For one thing, try to implement the absolute_time_t versus time_duration_t distinction (including operators) in C. For some more fun: http://www.embedded.com/design/programming-languages-and-tools/4428377/Objects--No--thanks---Using-C--effectively-on-small-systems- (note: 2 pages, the essence is on page 2) Wouter
Op Wed, 15 Oct 2014 17:32:38 +0200 schreef Wouter van Ooijen  
<wouter@voti.nl>:
> pozzugno schreef op 15-Oct-14 12:19 PM: >> [...] > > No need to, I work with 64 bit time_t only, and I won't be around when > that wraps around.
Why be so fatalistic? The nanobots are coming! -- (Remove the obvious prefix to reply privately.) Gemaakt met Opera's e-mailprogramma: http://www.opera.com/mail/