Interesting “feature” re: _delay_ms in avr-libc

January 31, 2012 | Arduino, Atmel AVR, Microcontrollers | By: Mark VandeWettering

A couple of my projects have used the tiniest of the Atmel ATtiny chips: the ATtiny13. I have written one or two programs in assembler for these chips, but I prefer to work with avr-gcc whenever possible. What’s amazing is that you actually can use a sophisticated C compiler to generate code for such a tiny chip (only room for 512 instructions).

Yesterday’s project was to implement a small QRSS beacon controller, similar to one’s sold by Hans Summer as part of his QRSS kits. He actually published his code, so I could just use that, but I found it a little bit obscure, and thought I could do something, well, if not better, at least less obscure to me. I wanted to retain the basic interface (Hans’ chip provides a sidetone on one pin, a key on another, and uses three pins as an input to select one of eight speeds). The basic loop fetches a pattern of dots and dashes, looks up the speed from the input switches, and then sends the pattern of dots and dashes. Easy peasy. I’ve written similar Arduino code before. Toggle pin high, delay, toggle pin low, delay, out goes a dot or dash.

But when I tried to use the _delay_ms() macro, I found that the codesize I had used grew enormously. It appeared to be dragging in a bunch of floating point arithmetic routines. I had used this function on my Christmas tree hat project without incident: what was going on?

Well, first of all, it appears that _delay_ms takes a floating point value as an argument. The compiler is smart enough that if the value passed to the function is a constant, it unfolds the code into a simple integer only loop. But if you use a variable as the argument (as I do, since the dit times are varying in length) you end up dragging all the floating point baggage into your code. It seems ridiculous to use floating point in this situation.

I got around this by noting that in my case, all my delays were multiples of 100ms. So, instead of just calling _delay_ms(n*100), I implemented the delays as a simple loop which executes _delay_ms(100) multiple times. Code is small, all is well.

You would think that a small, integer only delay would be possible. Oh well.

Comments

Comment from Kenneth Finnegan, W6KWF
Time 1/31/2012 at 2:34 pm

If memory serves, _delay_ms is also an inline function, so if you call it in more than once place, it will inline more than one time. This is why you’ll usually see a wrapper delay_ms(int) function which takes an int and simply loops over calling _delay_ms(1). A single fixed inline, and sane variable handling throughout the rest of your code.