Inlined Delay Algorithm

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

Hello All,

inspired by the delay routines in , I have written a more general approach. I would like to contribute this to the community and get some feedback if it is useful.

The code is not heavily tested so far. I did some short testprograms and found it OK. I think the compiler option '-O s' is neccessary.

Best regards and awaiting some feedback,
Hans-Juergen

Attachment(s): 

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

Can you comment on what problem it is trying to solve exactly?

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Quote:
Can you comment on what problem it is trying to solve exactly?

(I know active delay loops are a waste of CPU power, but sometimes they are the moste easy way to generate delays)

The function _delay_cycles( any_char_int_long_float_double_type_constant ) will always generate an accurate number of cpu cycles. As a programmer you do not have to think about implementation with nops or byte counters or int counters or long counters any more.
The function covers delays from a single cycle up to more than 500 seconds (8MHz).

_delay_cycles() could be the the same solution for WinAVR as we have it in the IAR compiler with its intrinsic __delay_cycles( unsigned long int ).

Hans-Juergen

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

I might have a look at it, OK. If you prefer, you could submit it to
the patch tracker of avr-libc so it won't be forgotten.

Anyway, programmers are usually not interested in delaying any number
of CPU cycles but any number of {micro,milli,}seconds. Incidentally,
I recently tried to implement something like that (similar to the
_delay_us in avr-libc) for the IAR compiler, but failed with the
issue. As the more simple implementation of such an algorithm (as
found in the avr-libc implementation of _delay_{us,ms}) rather need
some temp variables so they would best be implemented as an inlined
function call, it turns out you cannot do this with IAR's intrinsic
__delay_cycles function as it insists on getting a compile-time
constant parameter and it does not recognize the parameter to an
inline function call as being compile-time constant. Sigh.

I agree that extending the avr-libc implementation to uint32_t would
be a nice thing. I'd also prefer to offer the equivalent
_delay_us/ms/s abstraction for it then, as this is (from my
experience) what people have always been asking for.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Hans-Juergen Heinrichs:

This header file ROCKS!

I used it to make sure that I met the hardware timing on a graphic LCD. That is a geuine concern on the 16MHz parts . . you can clock the chip before the timing is met on the data pins that were written in the last instruction.

I had tweaked the LCD interface routine with the IAR delay_cycles function, and replacing that function 1:1 with the function from this library worked fine, and the overall screen updates took the same amount of time.

I vote for this file being included in the default GCC / WinAVR distribution . . maybe changing the name so it matches the name in the IAR stuff, since that will help everyone's code be more portable.

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

I agree with Baxsie, this header file looks very nice. What I also like here is that I can now use my own #define CPU_FREQ constant which I put in a _conf.h file in the directory of every project.

I'm still inexperienced with inline ASM, though, so I'll just assume for now that you're asm code is actually working.

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

I found this header to be extremely useful - add my vote for inclusion in the "next version" of avrlib

T

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

For that, please file a bug report to the avr-libc project.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Looks quite brilliant to me! I too wondered at first what exactly it was trying to achieve, but I think I can see it now. Accurate delays up to 500s at 8MHz? Count (heh) me in!

(n+1)thing this to be included in the next AVRLibC instead of the current delay system (as this looks backwards compatible also).

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

"Bug" (Enhancement request really) filed under Bug# 17216

Good job Hans-Juergen (and of course Jörg Wunsch et al for original implementation)

T

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

baxsie wrote:
Hans-Juergen Heinrichs:
I used it to make sure that I met the hardware timing on a graphic LCD. That is a geuine concern on the 16MHz parts . . you can clock the chip before the timing is met on the data pins that were written in the last instruction.

I came across this problem using a m128 @14 mHz for the t6963 graphic controller software. I thought the problem was a need for longer delays, but after debugging w/ a logic analyzer, it was a logic error instead.

However, I think it is a great addition and hope the code is included. If not, I'll save it on my own cvs,

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

What I'm doing wrong because this delay_x.h isn't very accurate.

I'm using now delay loop:

void Delay_ms(unsigned int t)

  {
   unsigned int i, aika;
   aika = 140;

   while(t--)
    for(i = 0; i < aika; i++)

       if(t==0) return;

  }

It seems to be +-0,1s/min accurate with following code:

#include 
#include 

int main(void)
  {
   DDRC = 0xFF;

   while(1)
    {
     PORTC = 0x00;
     Delay_ms(500);

     PORTC = 0x01;
     Delay_ms(500);
    }
  }

with delay_x.h when calculated 60 led blinks, time was about 58,8 seconds. What is wrong?

I'm using Atmega8, winavr, optimization -O0 when using viive.h and -Os when using delay_x.h. Internal clock 4MHz.

I'm a Newbie! ... and it's not my fault!!

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

Quote:
What is wrong?

Your trying to use C to create accurate delays. C doesn't do that.

Regards,
Steve A.

The Board helps those that help themselves.

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

Could you kindly provide your listing file (.lss) of the delay_x.h version for reference?

Best regards,
HJ

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

Listing file attached with code:

#include 
#include 

int main(void)
  {
   DDRC = 0xFF;

   while(1)
    {
     PORTC = 0x00;
     _delay_ms(500);

     PORTC = 0x01;
     _delay_ms(500);
    }
  }

Attachment(s): 

I'm a Newbie! ... and it's not my fault!!

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

MakeeK,

the generated assembler code looks very much as expected.
Please check the real atmega8 clock frequency as the internal oscillator may be very inaccurate.

Best regards,
HJ

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

Could it be better always to use external oscillator? STK500 board has option for that I suppose..

edit: I tried now with external 4MHz crystal. Results are 4000 blinks in 4000 seconds. I think that is accurate enough :P

I'm a Newbie! ... and it's not my fault!!

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

hi, I'm in deep love of this delay_x header, but now I started to work on AVR32 UC3B. So is there any technical problem to transplant this delay_x to AVR32?

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

Porting to AV32 will not be technical problem (as long you are using GCC tool chain).
Nevertheless, AVR32 is a totaly different hardware architecture - each of the the inlined delay loops must be carefully implemented with AV32 opcodes.

----
HJ

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

ok, so it's doable:)

Is there anyone who wanna do this then?

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

Doesn't the AVR32 have any timers then?

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

yes, it does have some timers, but it's always handy to have this delay_x around.

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

Why buy an insanely powerful CPU like an AVR32 to have it waste time sitting twiddling its thumbs?

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

I'm not talking about the long wait time, I normally just use very short wait time, like device drivers.

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

Cool...

I noticed the _NOP2 function is implemented as "rjmp 1". I will have to remember that the next time I need two NOPs.

The lpm for _NOP3 is also an interesting idea. Although, this is not a true NOP as it affects the R0 register. For many applications, this probably does not matter.

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

ok, no one wanna do this...

so I will do this then

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

what's description of the _NOP2 function?

"rjmp 1f" "\n\t"  "1:" "\n\t"

is "f" here means float constraint? I can't find this "f" at the "5.36.4 Constraints for Particular Machines" section of GCC manual.

and what's "1:" means here?

another question about the "lpm" insturction in _NOP3 is that if I use 3 NOPs instead of lpm, then the only tradeoff here is the size of the program, right?

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

No, in GCC asm you append 'f' for forward and 'b' for back to help the assembler locate the destination. So it's doing an rjmp forward to the label on the next instruction. 1: is a local label. You can use 1: .. 9:

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

aha, understood.

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

how about my lpm question then?

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

Quote:
another question about the "lpm" insturction in _NOP3 is that if I use 3 NOPs instead of lpm, then the only tradeoff here is the size of the program, right?

Yes!
---
HJ

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

ok, thanks, HJ.

Now I got another question, about the _delay_loop_3_x( uint32_t __n )

"1: sbiw %A0,1           "  "\n\t"
"   sbc  %C0,__zero_reg__"  "\n\t"
"   sbc  %D0,__zero_reg__"  "\n\t"

I guess the captical letter "A", and "C", "D" indicate the first, 3rd, and 4th bytes of this "uint32_t __n", but where is the definition of this format? I can't find this in GCC manual.

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

and why choose 12 as the highest NOP? what's so special about this 12?

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

I just can't find a single suitable instruction in AVR32 to use as _NOP2 or _NOP3 for the AVR32_delay_x, hehe, this means most of the AVR32 instructions are very fast, which is a good thing though.

maybe I have to use nop many times instead...

Anyone has idea?

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

Quote:
I guess the captical letter "A", and "C", "D" indicate the first, 3rd, and 4th bytes of this "uint32_t __n", but where is the definition of this format? I can't find this in GCC manual.
Please have a look at the avr-libc-user-manual, chapter "inline asm".
Quote:
and why choose 12 as the highest NOP? what's so special about this 12?
the smallest loop is 4 instructions (3 instructions for the loop + 1 instruction for loading the counter register). 12 cycles delay is implemented with 4xlpm which has the additional advantage of not consuming additional registers. For a 13 cycle delay a loop will be shorter.

--------
HJ

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

aha, ok, thanks.

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

just finished my _delay_loop_x code for AVR32,

I did some simple test, it seems it's working.

can you guys check it?

Attachment(s): 

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

any opinion?

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

Quote:
any opinion?

Why? How many AVR32 users do you think will read this AVR8 forum?

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

yes, right, I will repolish this to AVR32 forum.