I am trying to learn how to write programs to microcontroller, and started with very simple things like LED blinking with given frequency, or controlled by button. However, I have problems with using _delay_ms() from delay.h. As fas as I understood, for the correct work of this function, optimization should be turned on. When I write program to the microcontroller, instead of LED blinking, LED is constantly turned on with low brightness. When I tried just turn it on without delays, brightness is much higher, so looks like for some reason microcontroller gives some intermidiate low voltage, I am wondering why and what could be the problem. Below I describe what exactly I was doing. Code (main.c) looks like this (I am using attiny26, and asssume that it works at 1MHz).

#define F_CPU 1000000UL // 1 MHz clock speed
#include <avr/io.h>
#include <util/delay.h>
int main(void)
     DDRA |= (1<<PA0);  //A0 as output
     while(1) //infinite loop
          PORTA |= (1<<PA0);// LED at A0 ON
          _delay_ms(1000); // 1 second delay
          PORTA &= ~(1<<PA0); //LED at A0 OFF
          _delay_ms(1000); // 1 second delay

I make object file with

avr-gcc -mmcu=attiny26 -O1 -c main.c

where -O1 is for level 1 of optimization. Then

avr-objcopy -O ihex main.o main.hex

and write to microcontroller:

sudo avrdude -c usbasp -p t26 -B 100 -U flash:w:main.hex:i

All the steps were completely without any errors or warnings. As I told above, resulting behaviour is weird: LED is constantly on with lith low brightness. Meanwhile, if I write to microcontroller program, where I just turn LED on (remove all the delays), LED will be turned on with full brightness. I was thinking if the optimization itself can be a problem, but in the regime without delays program works correctly in cany case - with optimization or without.

Controlling anything with button would require debouncing, and the same _delay_ms() function, which doesn't work for me.

UPD: Commenting out #define F_CPU 1000000UL or increasing argument of _delay_ms() doesn't help. But increasing delay time by making iteration for (i=0; i<100000; i++) {_delay_ms(1000);} leads to LED being constantly turned on with full brightness. Tested smaller times of iteration (10, 100, 1000, 10000) do not give full brightness of LED.

  • 1
    \$\begingroup\$ The fact that it is brighter without the delay makes it seem like the delay is working but is so fast that it's just making the LED dim. What happens when you comment out #define F_CPU 1000000UL? \$\endgroup\$ – DigitalNinja Apr 8 '17 at 0:03
  • 1
    \$\begingroup\$ The LED is blinking, but it's blinking so fast that it just looks dim to your eyes. Try increasing the delay period by a factor of 10 or more. \$\endgroup\$ – kkrambo Apr 8 '17 at 0:04
  • \$\begingroup\$ The code looks good. What are your fuse settings? \$\endgroup\$ – Bruce Abbott Apr 8 '17 at 4:23
  • \$\begingroup\$ @DigitalNinja , I get warning /usr/lib/avr/include/util/delay.h:90:3: warning: #warning "F_CPU not defined for <util/delay.h> [-Wcpp] # warning "F_CPU not defined for <util/delay.h>" And it doesn't change anything - I see the same dimmed LED. According to the delay.h the F_CPU anyway would be the same by default: #ifndef F_CPU /* prevent compiler error by supplying a default */ # warning "F_CPU not defined for <util/delay.h>" # define F_CPU 1000000UL #endif \$\endgroup\$ – Antonio Apr 8 '17 at 8:22
  • 1
    \$\begingroup\$ The microcontroller cannot provide an intermediate low voltage on the gpio pin. It's either on or off. So if you are seeing dim then it is blinking very fast. Your delay function is not working like you expect. I don't know why. Try something else or step through it with the debugger and figure out what is happening. \$\endgroup\$ – kkrambo Apr 8 '17 at 12:58

The problem is you are creating an object file, but not linking it. Without linking, branch instructions in the machine code are not filled with the required destination addresses, so the code simply executes linearly until it 'falls off the end' into unallocated memory. Eventually it reaches the end of memory and wraps around to zero to execute your code again. The end result is the LED flashes with a very low duty cycle.

The simplest fix is to just remove the -c option from your compile command, ie:-

avr-gcc -mmcu=attiny26 -O1 main.c

This creates an ELF file called 'a.out' which you convert to HEX with:-

avr-objcopy -O ihex a.out main.hex

The maximum delay you can get with _delay_ms() should not exceed 262.14 ms / F_CPU in MHz. Which is 262.14ms in your case. Look at

void _delay_ms(double __ms)

in this link for more info.

A work around is to delay for a shorter amount of time, several times over.

For example:

for(int i=0; i<5; i++)
  • \$\begingroup\$ That link says the 262.14 limit applies to _delay_loop_2. The limit for _delay_ms is 65535 which should corresponds to 6.5535 seconds. \$\endgroup\$ – kkrambo Apr 8 '17 at 13:04
  • \$\begingroup\$ @kkrambo In his case, If the value passed to _delay_ms is bigger than 262ms, it is going to be divided by 10 automatically. So _delay_ms(1000); is a delay of 100ms. \$\endgroup\$ – Macit Apr 8 '17 at 13:43
  • \$\begingroup\$ @kkrambo So, to get the delay 6.5535 seconds he should type _delay_ms(65535); \$\endgroup\$ – Macit Apr 8 '17 at 13:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.