Forums

Delay Routine: Fully-portable C89 if possible

Started by Martin Wells October 9, 2007
I'm doing an embedded systems project which consists of taking input
from the user via simple buttons, and giving output in the form of
lighting LED's. So far, I've written the program as fully-portable
C89, and I intend to keep it that way as much as possible. Obviously,
I'll have microcontroller-specific parts to it such as:

void SetPinHigh(unsigned)
{
    /* Must call microcontroller-specific library functions or
something here */
}

, but the rest of my program calls these "wrapper" functions so I can
keep the bulk of it fully-portable.

Anyway, I've come to a point where I need to introduce delays, and I
again want this to be fully portable. The delays will be in the region
of milliseconds (typically 250ms, eg. for flashing LED's).

I had considered using a macro which indicates the "Floating Point
Operations per Second" for the given hardware setup, and then doing
something like:

void Delay(unsigned const fraction_of_full_second)
{
    long unsigned amount_flop = FLOPS / fraction_of_full_second;

    float x = 56.3;

    do x /= x;
    while (--amount_flop);
}

(I realise I'll have to take into account the delays of looping and so
forth)

Is this a bad idea? How should I go about introducing a delay? Must
this part of my program be non-portable?

Martin

Martin Wells wrote:
> I'm doing an embedded systems project which consists of taking input > from the user via simple buttons, and giving output in the form of > lighting LED's. So far, I've written the program as fully-portable > C89, and I intend to keep it that way as much as possible. Obviously, > I'll have microcontroller-specific parts to it such as: > > void SetPinHigh(unsigned) > { > /* Must call microcontroller-specific library functions or > something here */ > } > > , but the rest of my program calls these "wrapper" functions so I can > keep the bulk of it fully-portable. > > Anyway, I've come to a point where I need to introduce delays, and I > again want this to be fully portable. The delays will be in the region > of milliseconds (typically 250ms, eg. for flashing LED's). > > I had considered using a macro which indicates the "Floating Point > Operations per Second" for the given hardware setup, and then doing > something like: > > void Delay(unsigned const fraction_of_full_second) > { > long unsigned amount_flop = FLOPS / fraction_of_full_second; > > float x = 56.3; > > do x /= x; > while (--amount_flop); > } > > (I realise I'll have to take into account the delays of looping and so > forth) > > Is this a bad idea? How should I go about introducing a delay? Must > this part of my program be non-portable? >
Yes, that's a bad idea - embedded systems are inherently non-portable, and are dependant on the particular choice of target and compiler. Obviously it's a good idea to limit the number of non-portable parts of the program, but it's impossible to make an entirely portable embedded solution. Given that, there is no reason to worry about your delay function being non-portable. It should have a portable API, but does not need a portable implementation. Also, if you are going to try and stick to a standard, C99 makes more sense (even though few compilers support it completely). Most importantly, it gives you "stdint.h" and types like "uint32_t" so that you can avoid unspecific non-standardised types like "long unsigned" (which should always be written "long unsigned int"). As for using floating point ops for a delay - that's a bad idea too. Floating point on most embedded systems is slow and requires large library routines, thus wasting large amounts of space. Floating point operations also have inconsistent runtimes - even on a cpu with hardware floating point, division times are normally not constant. Finally, a loop like the one you wrote would be optimised away by any half-decent compiler. If you want to use busy waiting loops like this, learn to use "volatile". There is no way in C to express a delay or any other time-related information - thus your solution will inevitably be non-portable. If you want accurate times, it is normally best to use a hardware timer of some sort. Many compiler's also come with library functions handling delays, which can be a good choice. Otherwise you have to write and test your delay functions carefully to see that they work properly even when using different optimisation functions (check the generated assembly code, as one usually does for critical functions in embedded programming).
On Oct 9, 8:49 am, Martin Wells <war...@eircom.net> wrote:


> Is this a bad idea? How should I go about introducing a delay? Must > this part of my program be non-portable?
Just think quickly about how portable your idea REALLY is. Two obvious problems: - What happens if there are interrupts coming in? - What happens if the Icache is being thrashed, on architectures that have cache?
David:

> Most > importantly, it gives you "stdint.h" and types like "uint32_t" so that > you can avoid unspecific non-standardised types like "long unsigned" > (which should always be written "long unsigned int").
"long unsigned int" is a part of C89. Perhaps you were on about "long long unsigned int"? (which is a part of C89 but not C99) As far as any compliant C89 compiler is concerned, "long unsigned" and "long unsigned int" are the same thing. If the compiler doesn't accept it, then it isn't a C89 compiler. Even if it were worth switching to C99 (which I don't think it is), I still wouldn't because it's so poorly implemented today. Martin
Martin Wells <warint@eircom.net> writes:

> David: > >> Most >> importantly, it gives you "stdint.h" and types like "uint32_t" so that >> you can avoid unspecific non-standardised types like "long unsigned" >> (which should always be written "long unsigned int"). > > "long unsigned int" is a part of C89. > > Perhaps you were on about "long long unsigned int"? (which is a part > of C89 but not C99) > > As far as any compliant C89 compiler is concerned, "long unsigned" and > "long unsigned int" are the same thing. If the compiler doesn't accept > it, then it isn't a C89 compiler. > > Even if it were worth switching to C99 (which I don't think it is), I > still wouldn't because it's so poorly implemented today.
No, he is saying that he prefers being able to use uint32_t, instead of, for example, long unsigned. I personally don't agree though. At least in my own work I have not found a real-life situation where these types are an improvement. Basically, if you are worried about the exact widths of types, then that part of the program is likely non-portable anyway, so the new types don't help much. For example, in C99 I could define a 32 bit hardware register like this: #include <stdint.h> #define PORT (*(volatile uint32_t *)(0x1FFF0000)) But in fact this code will likely be useless on some hypothetical other CPU anyway. I can just as easily rely on ints being 32 bit on my platform, and do #define PORT (*(volatile unsigned long *)(0x1FFF0000)) -- John Devereux
John Devereux wrote:
> Martin Wells <warint@eircom.net> writes: > >> David: >> >>> Most >>> importantly, it gives you "stdint.h" and types like "uint32_t" so that >>> you can avoid unspecific non-standardised types like "long unsigned" >>> (which should always be written "long unsigned int"). >> "long unsigned int" is a part of C89. >> >> Perhaps you were on about "long long unsigned int"? (which is a part >> of C89 but not C99) >> >> As far as any compliant C89 compiler is concerned, "long unsigned" and >> "long unsigned int" are the same thing. If the compiler doesn't accept >> it, then it isn't a C89 compiler. >> >> Even if it were worth switching to C99 (which I don't think it is), I >> still wouldn't because it's so poorly implemented today.
*All* C standards are implemented to varying degrees, and *all* embedded compilers add their own extensions. Take advantage of what you get (such as <stdint.h>, inline, and // comments), and leave out the parts that are poorly or inconsistently implemented (such as variable length arrays). Even nominally C89 compilers frequently support such features.
> > No, he is saying that he prefers being able to use uint32_t, instead > of, for example, long unsigned. >
Actually, I was saying two things (without making that very clear). First the simple part - omitting the "int" part of declarations and definitions is an abomination brought about by the terrible keyboards K&R had to work with when developing C. The type in question is called "long unsigned int" - "long" and "unsigned" are type qualifiers. The language standards say that if a type is expected, but you haven't given one, then an "int" is assumed, and any C compiler will accept that. But the compiler will accept all sorts of hideous code and generate working results - you have to impose a certain quality of code standards if you are going to write good code. One of these rules is that if you mean a type should be "int" (optionally qualified), then you write "int". If you don't want to write out "long unsigned int", then use a typedef. Secondly, I was suggesting that if you want portable code, you have to use size-specific integer types. Using <stdint.h> is an easy way to get that - otherwise, a common format header file that is adapted for the compiler/target in question is a useful method. It doesn't really matter whether you use "uint32_t" from <stdint.h>, or have a "typedef unsigned long int uint32_t" in a common header file - nor does it matter if you give the type your own name. But it *does* matter that you have such types available in your code. Certainly many of the situations where size specifics are important are in hardware dependant and non-portable - and thus the only issue is that the code in question is clear. But there are many cases where you need a minimum range which may not be satisfied by "int" on every platform, and also where you want the fastest implementation. If you have a function delayMicrosecs(unsigned int n), then the useful range is wildly different on a 32-bit target and a 16-bit (or 8-bit, with 16-bit int) target. On the other hand, if it is declared with "uint32_t n", it is portable from 8-bit to 64-bit processors. Since the OP was asking for portable code in an embedded newsgroup, there's no way he can make assumptions about the size of "int". mvh., David
> I personally don't agree though. At least in my own work I have not > found a real-life situation where these types are an > improvement. Basically, if you are worried about the exact widths of > types, then that part of the program is likely non-portable anyway, so > the new types don't help much. > > For example, in C99 I could define a 32 bit hardware register like > this: > > #include <stdint.h> > #define PORT (*(volatile uint32_t *)(0x1FFF0000)) > > But in fact this code will likely be useless on some hypothetical > other CPU anyway. I can just as easily rely on ints being 32 bit on my > platform, and do > > #define PORT (*(volatile unsigned long *)(0x1FFF0000)) > >
John:

> For example, in C99 I could define a 32 bit hardware register like > this: > > #include <stdint.h> > #define PORT (*(volatile uint32_t *)(0x1FFF0000))
This will only work on implementations that actually have an unsigned integer type that has exactly 32 value representational bits. On other implementations, it won't compile. Best to use uint_least32_t (at least 32-Bits).
> But in fact this code will likely be useless on some hypothetical > other CPU anyway. I can just as easily rely on ints being 32 bit on my > platform, and do > > #define PORT (*(volatile unsigned long *)(0x1FFF0000))
unsigned long is guaranteed to be atleast 32-Bit, so that's fine. If you wanted to go easy on space consumption, you could still use C89 and use macros: #if VALUE_BITS(char unsigned) >= 32 typedef char unsigned uint_least32_t; #elif VALUE_BITS(short unsigned) >= 32 typedef short unsigned uint_least32_t; #elif VALUE_BITS(unsigned) >= 32 typedef unsigned uint_least32_t; #else typedef long unsigned uint_least32_t;
David Brown <david@westcontrol.removethisbit.com> writes:

[...]

> Secondly, I was suggesting that if you want portable code, you have to > use size-specific integer types. Using <stdint.h> is an easy way to > get that - otherwise, a common format header file that is adapted for > the compiler/target in question is a useful method. It doesn't really > matter whether you use "uint32_t" from <stdint.h>, or have a "typedef > unsigned long int uint32_t" in a common header file - nor does it > matter if you give the type your own name. But it *does* matter that > you have such types available in your code. > > Certainly many of the situations where size specifics are important > are in hardware dependant and non-portable - and thus the only issue > is that the code in question is clear. > > But there are many cases where you need a minimum range which may not > be satisfied by "int" on every platform, and also where you want the > fastest implementation. If you have a function > delayMicrosecs(unsigned int n), then the useful range is wildly > different on a 32-bit target and a 16-bit (or 8-bit, with 16-bit int) > target. On the other hand, if it is declared with "uint32_t n", it is > portable from 8-bit to 64-bit processors. Since the OP was asking for > portable code in an embedded newsgroup, there's no way he can make > assumptions about the size of "int".
If I wanted a minimum range of 32 bits, I would use "unsigned long". As you know this is already guaranteed to be at least 32 bits on all platforms, so I don't think there is any portability problem with 8,16 and 32 bit processors. Now I admit that these are the only cases I think about when writing my own embedded code. But I suppose you could argue that on some hypothetical 64 bit embedded processor, I would then be using values that were longer than needed. But, - with 64 bit CPUs, is it not true that the compilers still tend to have 32 bit longs, and use "long long" for 64 bits? - If longs *are* 64 bits, it could be because 32 bit operations are *slower* on that processor. Strictly, I think there might not even *be* a 32 bit type - unlikely, I agree. - On a 64 bit system, it is very likely that I would want to take advantage of the greater word size and use 64 bit arguments in any case. -- John Devereux
Martin Wells <warint@eircom.net> writes:

> John: > >> For example, in C99 I could define a 32 bit hardware register like >> this: >> >> #include <stdint.h> >> #define PORT (*(volatile uint32_t *)(0x1FFF0000)) > > > This will only work on implementations that actually have an unsigned > integer type that has exactly 32 value representational bits. On other > implementations, it won't compile. Best to use uint_least32_t (at > least 32-Bits).
I think perhaps you missed my point, which was that this sort of code is inherently non-portable since it refers to hardware registers built into the chip. In this situation I am *allowed* to assume that, e.g, unsigned long is 32 bits. And even if it was not, your version would not be an improvement since PORT is a 32 bit register. If uint_least32_t was in fact 64 bits, the code could still fail (e.g. you would overwrite the next register, or endianness could be wrong).
>> But in fact this code will likely be useless on some hypothetical >> other CPU anyway. I can just as easily rely on ints being 32 bit on my >> platform, and do >> >> #define PORT (*(volatile unsigned long *)(0x1FFF0000)) > > > unsigned long is guaranteed to be atleast 32-Bit, so that's fine.
That was my point; it is just as good as uint32_t for this sort of usage.
> If you wanted to go easy on space consumption, you could still use > C89 and use macros: > > #if VALUE_BITS(char unsigned) >= 32 > typedef char unsigned uint_least32_t; > #elif VALUE_BITS(short unsigned) >= 32 > typedef short unsigned uint_least32_t; > #elif VALUE_BITS(unsigned) >= 32 > typedef unsigned uint_least32_t; > #else > typedef long unsigned uint_least32_t;
-- John Devereux
In message <470b8d0c$0$3221$8404b019@news.wineasy.se>, David Brown 
<david@westcontrol.removethisbit.com> writes
>Martin Wells wrote: >> I'm doing an embedded systems project which consists of taking input >> from the user via simple buttons, and giving output in the form of >> lighting LED's. So far, I've written the program as fully-portable >> C89, and I intend to keep it that way as much as possible. Obviously,
>Yes, that's a bad idea - embedded systems are inherently non-portable, >and are dependant on the particular choice of target and compiler.
Absolutely
>Also, if you are going to try and stick to a standard, C99 makes more >sense (even though few compilers support it completely).
Absolutely NOT All the embedded compilers are based on C95. Also the next version of C will be C99 less some items (and some more added)
>As for using floating point ops for a delay - that's a bad idea too.
Yes I would suggest a HW timer and an interrupt. BTW what is/are the target MCU(s) for this -- \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ \/\/\/\/\ Chris Hills Staffs England /\/\/\/\/ /\/\/ chris@phaedsys.org www.phaedsys.org \/\/\ \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/