For ARM7, A hardware timer is 32-bits, and unsigned int is 32-bits too; For a 16-bits MCU, A hardware timer is 16-bits, and unsigned int is 16-bits too;
So,
t0 = timer_val; while ( ( timer_val - t0 ) < delay_val );
provide a simple and good delay;
Not so sure about, if a cast is needed.
t0 = timer_val; while ( (unsigned int)( timer_val - t0 ) < delay_val );
Just noticed that, due to the integral promotion,
unsigned short t0, t1; ( t1 - t0 ) is a signed int.
It seems that,
unsigned int t0, t1; ( t1 - t0 ) is still an unsigned int.
I noticed this, because I use unsigned short to present a 16-bits timer_val on my X86-PC for testing/simulating purpose, and the result is not what I expected.
Extend the two values to unsigned before subtracting.
void delay(uint32_t ticks) { int32_t now, end; now = TIMER_VALUE; /* assuming the timer is 32-bit */ end = now + ticks; do { now = TIMER_VALUE; } while (now - end < 0); }
Notice how I'm using (now - end < 0) instead of (now < end). The latter would be wrong.
What will happen if the [ticks] parameter happen to be zero [0] or one [1] or two [2] or generally a very small number ?
such overlooked details make important missions to fail.
What will happen if the [ticks] parameter happen to be zero [0] or one [1]
That's not related to the types issue, which my example was designed to clarify. You can easily extend the code to treat corner cases. You'll also note that the 'do-while' is not necessary, a 'while' loop will do. By the way, the provided example does a reasonable thing when ticks==0: it returns without waiting. When ticks!=0 you'll probably want to make sure that the delay is at least as long as required, so it would make sense to do the following:
if (ticks != 0) ticks++; else return;
For ARM7, A hardware timer is 32-bits, and unsigned int is 32-bits too; For a 16-bits MCU, A hardware timer is 16-bits, and unsigned int is 16-bits too; this is the usual illusion that embedded code (for sure, the part touching I/O) is portable some HW timers count up, some count down, what about tyhat?
rik
It doesn't matter if the ticks parameter is small.
Comparing (a-b) < 0 works as long as the tick doesn't run so fast that it overflows and changes sign before you have time to check the result of the subtraction.
Obviously, if the timer ticks really fast, and the function is called with a very short delay, then more time than intended will pass before the function manages to check the result of the subtraction. If the timer ticks every 50ns (20MHz) and I request a delay of 1, I will not be able to call the function and have the function perform first iteration of the loop within 50ns. So there is a minimum delay involved in the actual function call. And there is a limit to how often the loop can perform the check.
You can try, but you might need to change quite a few lines of code in order for it to compile depending how older the lib you have is from the lib you want to upgrade to. I had similar issues with a example running old CMSIS and my implementation using newer, that I tried to mix together. I lost a couple of days before I fixed it.
int32_t now, end;
int32_t is the wrong type for that. You should use the same unsigned type as the actual timers are. Using signed instead adds nothing useful -- it only adds the issue of what happens on overflow of signed arithmetic. Unsigned arithmetic, OTOH, is guaranteed to wrap around cleanly.
The condition to test for is
(TIMER_VALUE - start) % timer_range >= delay
For timers 16 bits or 32 bits wide, you can avoid the explicit modulo operation if you choose the accorting uint16_t or uint32_t for 'start', since unsigned arithmetic already does the same modulo.
If you're going to work in this field, you must know better than "it tseems that". That's absolutely fundamental knowledge about the programming language.
I noticed this, because I use unsigned short to present a 16-bits timer_val on my X86-PC for
Using generic, non-size-spefic types like "unsigned short" for that is wrong. C99 <stdint.h> types like uint16_t were made for exactly this kind of situation.
If you analyze the code sample carefully, you'll note how it gracefully handles the overflow issue. The timer is assumed free-running, no reload. It might depend on the arithmetics being '2's complement', but that would be a rather safe assumption.
If you analyze the code sample carefully, you'll note how it gracefully handles the overflow issue.
I don't note, because it doesn't. It causes signed integer overflows every time the TIMER_VALUE is bigger than int32_t variables can hold --- which it is 50% of the time.
What that code does is it assumes signed arithmetic overflows cleanly. It doesn't do anything to handle the possibility it might not.
Exactly. This was meant by design. I think you'll struggle to find an ARM compiler where 'signed arithmetic does not overflow cleanly.' In fact, I don't think one exists. Of course, it should be easy to add some protection against the unlikely event in the form of an assertion. I consulted the C99 standard. Indeed, it does not specify the behavior of a signed integer overflow, while it does for unsigned.
The standard two-complement arithmetic can really be assumed.
I do not thing any new chip vendor would dare to implement a different alternative because they would not save any transistors but would instead get into a shitload of problems.
The two-complement form really is trivial to implement in hardware (and a not too uncommon exercise for students to implement using VHDL or similar), both when having a very strict transistor count, and when having to implement an extremely wide ALU.
Sorry for my limited technical ability and English ability. I realize that I lack a lot of fundamental knowledge of computer science; and I try to learn more at almost every opportunity. My skill level is far behind you guys, so I am not sure if I understand the information in this thread correctly.
Hi Mike, May I assume that, your example code is to show me how to handle the types issue/integral promotion properly; so that a cast is not necessary?
=====================================================
Don't know if my understanding on Mike's example code is correct or not: 1. a cast is not necessary for
int32_t now, end; (now - end < 0)
2. 0 is better than delay_val
( ( timer_val - t0 ) < delay_val )
(now - end < 0)
(Anything else?) =====================================================
Isn't it, for computer science, both signed arithmetic and unsigned arithmetic are based on two-complement arithmetic?
How about this:
void delay(uint16_t ticks) { uint16_t now, end; now = TIMER_VALUE; end = now + ticks; do { now = TIMER_VALUE; } while ( (uint16_t)(now - end) < 0 ); }
I regularly discover that I am an idiot.
while ( (uint16_t)(now - end) < 0 );
unsigned always >= 0