delays in C

Go To Last Post
42 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi there. I need to generate a 20ms delay in C. I'm not too sure if the function sleep(20) will work. Could someone please advise me on this.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

include util/delay.h

and use _delay_ms(20);

with the right F_CPU defined should do the trick

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hey thanks. I'll try that out now:)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Depends on what compiler you use.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh but it's true,
as we went warp factor 2,
who's Mr Spock?
(with apologies to SpizzEnergi).

I still think that 9992+8 = 10,000 when it happens ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MrSpock is right.

for small delays, I roll my own delay. Under a 8Mhz crystal, 1:8 clk divider, you can write your own delay routines that are good over a wide range.

the stock _delay() routines are just too sensitive to F_CPU and parameters, to me.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Count down an int variable from 2400 takes a ms at 16mhz xtal. Call this 20 times.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Count down an int variable from 2400 takes a ms at 16mhz xtal.

In which C compiler with which optimization settings?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

millwood wrote:

the stock _delay() routines are just too sensitive to F_CPU and parameters, to me.

Exact this was the excellence of the delay.h routines. :!: :!: :!:

You need never connect an oscilloscope for awkward trial and error programming. :evil:

If you need to use another crystal, simple change F_CPU to the crystal value.
If you need another delay, simple insert the wanted time into the _delay_ms, _delay_us calls.

Never needed to check the delay time after it.
Write it down, compile and burn the AVR and the delay fits always.

IIRC the maximum delay time is ~6s.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:
Count down an int variable from 2400 takes a ms at 16mhz xtal. Call this 20 times.

But for other crystals I need to edit every delay on the whole source files :?: :roll:

I think I need not such ways to increase my programming time further.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Write it down, compile and burn the AVR and the delay fits always.

really? I love magic like that.

what's the delay generated by this:

_delay_ms(64000);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One could always use an old-fashioned big mechanical alarm clock, sample the noise from that device with the AVR's ADC, and, in case the alaram fires, the ADC reading would pas a certain threshold. Voila: fixed timing without ANY need of source-code editing, nor recompile/link steps. Rating of the crystal? Who cares...

Just don't forget to wind up the mechanical clock from time to time, though...

Ah, and of course, timings in the ms range are somewhat trickier...

Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IIRC the _delay_ms (and _delay_us) routines MUST be compiled with the optimize=2 setting (-o2 ) or you won't get the right result.
I'm using these in my code and it's 'close enough for government work'.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
it's 'close enough for government work'.

I agree with that.

those routines are not meant for precision timing, nor are they meant to be perfect.

the key in any thing is to understand your own routines' weakness / traps and avoid them.

because of that, I use my own delay routines because of their simplicity.

the issue I take with the stock _delay() routines is their fall-back position if the temporary variable overflows.

it is a lot better to write your own routines that wrap around the stock routines to make them more reliable and to deal with variable delays too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Who on earth would use a software delay, using 100% CPU, for 64 seconds? Surely you want to either get on and do something else (timers/interrupts) or you want to sleep the CPU.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In my case the timing isn't super critical. The delays must be at least a critical amount of time in length, longer (but not grossly longer) is just fine. In many cases delays must be accurate only to +/- 50 percent and these simple loops are fine. Note that if you have regular interrupts firing the delays will be longer than you think. For critical timing I just use one of the hardware timers. (such as in IR demodulation or missing pulse detection).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Who on earth would use a software delay, using 100% CPU, for 64 seconds? Surely you want to either get on and do something else (timers/interrupts) or you want to sleep the CPU.

An application that doesn't do very much.
I am actually using the _delay_ms() functions to simulate pressing a switch to debug the power latching circuit of another piece of equipment. The power on switch (push button) is shorted by a fet controlled by an output port of the AVR. I use delays of 3 and 5 seconds(!) in software. But all the AVR is doing is 'pressing a switch' on and off all night. I had a race condition in my button detect and power latch function and was using an AVR in a button press simulator to exorcise my fix over night.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I use delays of 3 and 5 seconds(!) in software.

But that's not the issue here _delay_ms(3000) or _delay_ms(5000) work just fine - even on 20MHz. The suggestion seems to be that _delay_ms(64000) for a 64 second delay may have overflow problems.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The delays must be at least a critical amount of time in length, longer (but not grossly longer) is just fine.

that's fairly typical.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void delnms(unsigned int n){
//delay n ms
unsigned int x;

  while(n--){
//    x=2200; //14mhz
    x=2400; //16mhz
//    x=2800; //18mhz
//    x=3000; //20mhz
    while(x--){};
  } 
}

That's what I meant when I said 1ms delay at 16mhz. See? I don't have to edit every delay in every c file. Just one.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmmmm ... According to the header file the argument is a floating point one and the absoulte max is 6.5535 SECONDS. Delays of FRACTIONAL MS are possible up to about 262.14 ms after which delays in increments of WHOLE MS are possible. Guess you need to call this routine in a loop form longer delays than 6.5535 seconds.

Perform a delay of \c __ms milliseconds, using _delay_loop_2().
The macro F_CPU is supposed to be defined to a
constant defining the CPU clock frequency (in Hertz).
The maximal possible delay is 262.14 ms / F_CPU in MHz.
When the user request delay which exceed the maximum possible one,
_delay_ms() provides a decreased resolution functionality. In this
mode _delay_ms() will work with a resolution of 1/10 ms, providing
delays up to 6.5535 seconds (independent from CPU frequency). The
user will not be informed about decreased resolution.
*/

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:

void delnms(unsigned int n){
//delay n ms
unsigned int x;

  while(n--){
//    x=2200; //14mhz
    x=2400; //16mhz
//    x=2800; //18mhz
//    x=3000; //20mhz
    while(x--){};
  } 
}

That's what I meant when I said 1ms delay at 16mhz. See? I don't have to edit every delay in every c file. Just one.

Bob,

One day, ImageCraft will update their Compiler. Your function will be optimised. Then you can join Mr Samperi in being upset.

Now you could just simply add the word 'volatile' to ensure that your method will always work. It may not work at the same speed. But at least it will delay to some extent.

Believe me. ImageCraft will optimise one day. Be prepared.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
what's the delay generated by this:

_delay_ms(64000); 

A delay of approximately 5 seconds since the value exceeds the maximum of the function (but you would know that if you actually read the documentation).

Quote:
IIRC the _delay_ms (and _delay_us) routines MUST be compiled with the optimize=2 setting (-o2 ) or you won't get the right result.
No, any optimization level other that -O0 will work. And the compiler will warn you if that setting is made.
Quote:
One day, ImageCraft will update their Compiler. Your function will be optimised. Then you can join Mr Samperi in being upset.
Or even just updating the compiler might change the delay.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually the really IMPORTANT part of the _delay_ms and _delay_us functions are written in assembler. These routines (_delay_loop_1 and _delay_loop_2) are called multiple times by the 'C' code. Optimization of the compiler will change the time between calls of the assembler functions, but MOST of the actual delay happens inside the assembler functions. I'd guess as a result of this these functions should have an accuracy tolerance of no worse than +/- 10 percent, probably a lot better than that.
(see and

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey DP... whats unoptimal? x is used, n is used? Nothing is changed in an interrupt handler, so neither of those variables are volatile. I don't know what to prepare for optimization wize.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Bob,

An optimizer will recognize that a code segment with no inputs and no outputs (just counting on your fingers) does not achieve anything and simply throw away the entire while(x--) loop as it does nothing but waste cycles. If the optimiser is attempting to reduce execution time then wasted cycles in pointless loops will be first on it's hit list.

If I build:

void delnms(unsigned int n){
  unsigned int x;

  while(n--){
    x=2400; //16mhz
    while(x--){};
  }
} 

in GCC (which is an optimising compiler) it generates:

00000080 :

  while(n--){
    x=2400; //16mhz
    while(x--){};
  }
}
  80:    08 95           ret

Nothing but a "ret" opcode.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:
Hey DP... whats unoptimal? x is used, n is used? Nothing is changed in an interrupt handler, so neither of those variables are volatile. I don't know what to prepare for optimization wize.

It is nothing to do with interrupts. The optimiser looks at your function, and sees that at the end of the while loop: n = 0 and x = 0.
So it could simply generate n = 0, x = 0.
But since you do not use either 'n' or 'x', there is no point in even doing this.

One day, a Compiler may recognise that your function does nothing but RET. So it could throw away the LDI Rx,0x12 LDI Ry,0x34 CALL delnms sequence too.

GCC is not particularly clever. C compilers for ST, PIC, MSP430, ARM, ... all optimise already.

So ImageCraft WILL optimise. You will have to ask Richard WHEN.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
So ImageCraft WILL optimise. You will have to ask Richard WHEN.

When you pay for it:
Quote:
PRO Version includes Code Compressor and a Global Optimizer that improves program size and runtime speed by 10-15%

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
C compilers for ST, PIC, MSP430, ARM, ... all optimise already.

Keil is particularly impressive on that. sometimes, it will through out entire functions or sections of a routine if it senses that it does nothing.

that can be incredibly puzzling for newbies.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What do you call this kind of optimizing? (Not dead code elimination... that's unreachable code after a call or jmp)

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I don't know what to prepare for optimization wize.

Maybe you made more of a point there than you realized..

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

WOW guys you really got into this. For my application the LCD just needs some time at start up. I think the easiest method will just be the _delay_ms() function.

Thanks for the insight:)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Glad you got the info you want. I still want to know the compsci term for the type of optimization where the compiler silently deletes a couple of lines of c right in the middle of a subroutine without telling you. (I have a term for it).

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I still want to know the compsci term for the type of optimization where the compiler silently deletes a couple of lines of c right in the middle of a subroutine without telling you.
It's called "ANSI C Compliant".

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You're looking at something called "Dead Code Elimination". Several approaches to this, and most result in you having to understand precisely what the complier considers as "dead".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Guys, as some of you may know, over the past year,
I've been fairly critical of the current implementation due to its granularity
and rounding issues that can be unacceptable for short delays like sub microsecond hardware setup timing.

Also, as some of you may know, a first attempt was made to
improve the granularity of the routines in the latest AVR LibC using a built in gcc delay function but
the new code actually totally broke the delay routines.
(Delays can be 3 or 4 times shorter than they should be)

see this bug report:
http://savannah.nongnu.org/bugs/?30363

The good news is that this is in the process of being fixed right now and part of fixing this is to actually
make the delay routines better than they are now yet try
to maintain as much backward compatibility as possible.

Yes the API can be 100% backward compatible but
to modify the routines, even if for the better,
the behavior will slightly change (due to better delay accuracy/resolution), and that is why 100% backward compatibility is not possible.

The new routines can take advantage of a new compiler
builtin function that creates code to generate
busy wait delay cycles.

As always, the biggest issue with creating s/w busy wait delays comes down to granularity and how the delay is rounded within that granularity.

The previous/current delay routines had a fairly large granularity. (3 or 4 loop cycles) and always rounded
the delay down to the nearest 3 or 4 cycle loop, unless
0 when it rounded up to 1 loop of 3 or 4 cycles.
When using the new delay cycle capability in gcc vs the basic delay loops, the delay granularity can be a single AVR clock cycle.
But even having single cycle granularity does not solve
the problem with how to round the delay.
i.e. do you round the delay up, down, or to the nearest
cycle?

The current proposal is to always round the delay up
to the nearest cycle to ensure that a delay is always
"at least as long as" requested,
but also have a few compile time defines to modify this
behavior for those that want/need other behavior.

There are 3 defines
- one to round down
- one to round to the closest (could be up or down)
- one to return to previous behavior using basic delay loops.
(this last option will eventually be deprecated).

The goal is to make the delay routines better and more accurate than they are today
yet also provide some flexibility for specific needs and backward compatibility.

One final thing that might need to be considered is
if rounding down, should that allow a delay to be
completely eliminated? i.e. if a calculation determined
that the requested delay is less than 1 full clock cycle
and a "round option" was selected, should the delay be
eliminated or be 1 cycle in this special case?

Note: this type of issue does not happen when always
rounding up which is the proposed default method
of rounding.

Please see the bug report for details and to make
any comments.:
http://savannah.nongnu.org/bugs/?30363

--- bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Considering that a single clock cycle of an AVR processor is under 1us having an error of 1 or 2 clock cycles would only affect a programmed software delay of less than a few us in most cases. In the case of 'bit banging' some kinds of hardware this could be a big deal. In the case of just trying to be inside of a critical window, maybe not as much. In the general case of just delaying 'long enough' to get things happening in the right order the error is usually not important. Still using a scope to test your software timing is important. Also knowing the EXACT frequency of your CPU clock helps!
(might not be what's stamped on the crystal...)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can you please also change the default from just giving a #warning if F_CPU is not defined to an #error?

Stealing Proteus doesn't make you an engineer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ArnoldB wrote:
Can you please also change the default from just giving a #warning if F_CPU is not defined to an #error?

I know you can force GCC to treat ALL warnings as errors. It would be nice to be able to control WHICH warnings (by number) ARE errors.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

It would be nice to be able to control WHICH warnings (by number) ARE errors.

When avr-gcc support pragma's this will be easier to design and implement. Maybe still not easy - but easier.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Please see the bug report for details and to make
any comments.:
http://savannah.nongnu.org/bugs/...

Bill
Have you reviewed the patch ?
http://savannah.nongnu.org/bugs/...

Edit: Clearer statement of what i have been avoiding...

I have been avoiding it (meaning 1.7.0 delay.h) and instead used "hendrix" delayx.h or used a toolchain with avr-libc 1.6.8.

/Bingo

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've looked at the patch. I have not had a chance to verify it yet in real
code yet. Visually it looks ok and I think it
will resolve all my usability issues with
I would have liked to see this update go a bit further as there are some
other issues that would have been nice to clean up, (like use of non constants and not assuming 1Mhz for F_CPU)
but as Anitha pointed, out those are not really part of the cycle delay calculation.

So those may have to wait.

One think that does concern me and it is outside the scope of
is that Hans delay_x code generates better code than the gcc builtin delay function.
Some of it may have to do with GCC compile options, but what I've seen is that
GCC sometime generates larger code by not using some instructions
so code will be a few instructions larger.
Same exact number of cycle of delay just larger code.

Also, and this is the concerning part, the GCC code is
is too quick to jump to using loops which can have unintended side effects.
In some cases, it will jump to using loops, which
may appear to generate smaller code but in fact generates
larger and slower code.
The reason is that the collapsing of the code to use a loop will need to grab a register, this can, depending on your code cause the compiler to have to do extra pushes/pops. in a prologue/epilogue of a function.

So for now, at least for me, in a fairly large project
(a GLCD library for Arduino) where there are lots of inline delays for hardware setup timing, I get better/smaller code from han's delay_x routines rather than using GCCs built-in functions.

While the extra size doesn't affect operation, using the GCC built in cycle delay routine is a loss of a few 100 bytes of flash space in my case vs Han's cycle delay routine.

Maybe somebody could take another look at the built in GCC delay function to address this.
(I'm not sure how to make that happen).

--- bill