Strange behaviour in AtMega328P regarding where to initialise pins

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

Hi everyone, this is my first post :) I started programming an AtMega328p in an Xplained Mini board. I wrote a very simple program (almost a blinking led program):

#define F_CPU 16000000UL
#define SW_PIN  7 // PB7
#define LED_PIN 5 // PB5
#include <avr/io.h>
#include <util/delay.h>
/*
void init_pins(void){
	DDRB |= (1<<5);
}*/
int main(void){
	//init_pins();
	DDRB |= (1<<5);
	while(1){
		PORTB ^= (1<<5);
		_delay_ms(100);
	}
}

This works fine. The thing is that I would like to initialise PB5 at the DDRB register, inside a function (init_pins). If I do this:

#define F_CPU 16000000UL
#define SW_PIN  7 // PB7
#define LED_PIN 5 // PB5
#include <avr/io.h>
#include <util/delay.h>
// let's try with init_pins
void init_pins(void){
	DDRB |= (1<<5);
}
int main(void){
	init_pins();
	//DDRB |= (1<<5);
	while(1){
		PORTB ^= (1<<5);
		_delay_ms(100);
	}
}

the led starts blinking in a very strange way (two times fast, and later nothing, and again and again, with a period of 1second approx.). I'm compiling with these settings:

avr-gcc -Wall -Wextra -Wpedantic --std=gnu99 -Os -mmcu=atmega328p    -c -o main.o main.c

It's supposed that doing DDRB |= blah, should not depend whether I did it in a function or in main, right? Can you give a hand, guys? I would be grateful.

 

Sebasthian.

This topic has a solution.
Last Edited: Mon. Aug 28, 2017 - 01:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Welcome to AVR Freaks!

 

May I first suggest that you not use "magic numbers". Its pretty transparent in the above code but it is a really bad habit to get into. Try, instead of DDRB |= (1<<5), to use DDRB |= (1<<PB5). Most anyone can tell what you mean, here, but with other registers, its really unpleasant.

 

That  kind of behavior is usually caused by watchdog. Do you happen to have WDTON fuse set? However, it makes no sense that it would work with with the init  function and a different way "bare". Are you changing ANYTHING else than those two lines?

 

Cheers

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Try slowing it down to delay(600); ,  also try having the LED on some other pin besides PB5 and see if the behavior is the same.

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

I'm with Jim.

Watchdog timer seems to be a logical culprit.

& I also don't like magic numbers.

 

Also: I really don't see much use of using an init function which is only going to be called once when the program starts up.

You might just as wel put it in the front of main() as int the first example.

try this:

 

#define F_CPU 16000000UL
#define SW_PIN  (1<<7) // Note: 7 is PB7 no need to add it in comment.
#define LED_PIN (1<<5) // Actually, it's an opportunity to have code and comment going out of sync.
#include <avr/io.h>
#include <util/delay.h>
/*
void init_pins(void){
	DDRB |= (1<<5);
}*/
int main(void){
	//init_pins();
	DDRB |= SW_PIN | LED_PIN;
	PORTB &= ~ SW_PIN;      // I believe this is the default, to lazy to look up.
	_delay_ms( 500);
	PORTB |= SW_PIN;        // If this pin goes low, we know the uC is being reset somehow. (WDT, noise, etc).
	while(1){
		PORTB ^= LED_PIN;
		_delay_ms(100);
	}
}

 

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

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

Thanks everyone for your answers. I will reply separately:

 

ka7ehk wrote:

May I first suggest that you not use "magic numbers". Its pretty transparent in the above code but it is a really bad habit to get into. Try, instead of DDRB |= (1<<5), to use DDRB |= (1<<PB5). Most anyone can tell what you mean, here, but with other registers, its really unpleasant.

 

That  kind of behavior is usually caused by watchdog. Do you happen to have WDTON fuse set? However, it makes no sense that it would work with with the init  function and a different way "bare". Are you changing ANYTHING else than those two lines?

Okay, I agree about the magic numbers, thanks :). About the second paragraph, I didn't make any other change. I don't know about the WDTON fuse. I suppose it is a register, right?

 

Simonetta wrote:

Try slowing it down to delay(600); ,  also try having the LED on some other pin besides PB5 and see if the behavior is the same.

I tried actually with all the PORTB but I got the same result.

 

Paulvdh wrote:

I'm with Jim.

Watchdog timer seems to be a logical culprit.

& I also don't like magic numbers.

 

Also: I really don't see much use of using an init function which is only going to be called once when the program starts up.

You might just as wel put it in the front of main() as int the first example.

try this:

 

#define F_CPU 16000000UL
#define SW_PIN  (1<<7) // Note: 7 is PB7 no need to add it in comment.
#define LED_PIN (1<<5) // Actually, it's an opportunity to have code and comment going out of sync.
#include <avr/io.h>
#include <util/delay.h>
/*
void init_pins(void){
	DDRB |= (1<<5);
}*/
int main(void){
	//init_pins();
	DDRB |= SW_PIN | LED_PIN;
	PORTB &= ~ SW_PIN;      // I believe this is the default, to lazy to look up.
	_delay_ms( 500);
	PORTB |= SW_PIN;        // If this pin goes low, we know the uC is being reset somehow. (WDT, noise, etc).
	while(1){
		PORTB ^= LED_PIN;
		_delay_ms(100);
	}
}

 

The reason is because I want to make my code portable also to a MSP430 (part of a homework), so I will need two different init_pin, but that's not important for this now. I tried a slightly different version of your code, because in the Xplained Mini I don't have (as far as i know) direct access to PB7. As you said, the uC is being reset for some reason, but sincerely I don't understand why:

 

#define F_CPU 16000000UL
#define SW_PIN  (1<<7) // Note: 7 is PB7 no need to add it in comment.
#define LED_PIN (1<<5) // Actually, it's an opportunity to have code and comment going out of sync.
#include <avr/io.h>
#include <util/delay.h>

void init_pins(void){
	DDRB |= (1<<5);
}
int main(void){
	//init_pins();
	DDRB |= SW_PIN | LED_PIN;
	PORTB &= ~ LED_PIN;      // I believe this is the default, to lazy to look up.
	_delay_ms( 500);
	PORTB |= LED_PIN;        // If this pin goes low, we know the uC is being reset somehow. (WDT, noise, etc).
	while(1){
		PORTB |= LED_PIN;
		_delay_ms(100);
	}
}

What surprises me the most is that i'm not using the function init_pins, but anyway the LED is blinking. Until now, the only solution (not exactly a good solution) is to comment out the init_pins. I tried it and the LED stops blinking.

Last Edited: Mon. Aug 28, 2017 - 12:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Agree with Paul on both suggestions.

 

1. Try slowing down the flash by making the delay longer. Does the time that it operates stay about the same and the time that it is off stay about the same? If this is true, then even stronger suggestion of watchdog.

 

2. Different pin is a useful experiment. PB5 also happens to be the ISP clock pin and you MIGHT be getting tangled up with that. Try something other than PB3, PB4, or PB5.

 

Another thought: what do you have connected to the RESET pin?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

ka7ehk wrote:

Agree with Paul on both suggestions.

 

1. Try slowing down the flash by making the delay longer. Does the time that it operates stay about the same and the time that it is off stay about the same? If this is true, then even stronger suggestion of watchdog.

 

2. Different pin is a useful experiment. PB5 also happens to be the ISP clock pin and you MIGHT be getting tangled up with that. Try something other than PB3, PB4, or PB5.

 

Another thought: what do you have connected to the RESET pin?

 

Jim

Thanks for the fast answer, Jim. I tried with the following code, with the increased delay and another pin (PB3). The results are the same.

#define F_CPU 16000000UL
#define SW_PIN  (1<<7) // Note: 7 is PB7 no need to add it in comment.
#define LED_PIN (1<<5) // Actually, it's an opportunity to have code and comment going out of sync.
#include <avr/io.h>
#include <util/delay.h>

void init_pins(void){
	DDRB |= (1<<PB3);
}
int main(void){
	//init_pins();
	DDRB |= (1<<PB3);
	PORTB &= ~(1<<PB3);     // I believe this is the default, to lazy to look up.
	_delay_ms(10000);
	PORTB |= (1<<PB3);        // If this pin goes low, we know the uC is being reset somehow. (WDT, noise, etc).
	while(1){
		PORTB |= (1<<PB3);
		_delay_ms(10000);
	}
}

 

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

Your while(1) loop does nothing and may well be optimized out. How long does the LED stay on? You turn the LED off, wait 10 seconds, then turn it on, after which it should stay on with no flash. What does it actually do?

 

How are the fuses set?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Aug 28, 2017 - 01:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:

Your while(1) loop does nothing and may well be optimized out. How long does the LED stay on?

 

How are the fuses set?

 

Jim

 

If I don't comment out the init_pins: PB3 blinks twice in 0.2s approx and later stays off a second, and again and again. It does like this: .I.I.............I.I........... (2.5seconds approx the entire line). 

 

About the fuses, I was trying to figure out now which ones are set reading the user manual of the Xplained Mini board, but I didn't find information. I just knew that they are customisable in Atmel Studio, but I'm not using it. I am using only avrdude and avr-gcc in the command line.

Last Edited: Mon. Aug 28, 2017 - 01:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

So you are not using Atmel Studio to do the programming?

 

There is a command line flag to AVRDude to read the three fuse values. Don't know what it is, though (rarely use AVRDude).

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Aug 28, 2017 - 01:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:

So you are not using Atmel Studio to do the programming?

 

That's right. I'm not using Atmel Studio.

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

You will need to look up what the command is to AVRDude to report the fuse values.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

ka7ehk wrote:

There is a command line flag to AVRDude to read the three fuse values. Don't know what it is, though (rarely use AVRDude).

Jim

 

Got it. 

$ avrdude -c stk500v1 -p m328p -P /dev/tty.usbmodem1452 -b57600 -U lfuse:r:-:i -v

avrdude: safemode: lfuse reads as 0
avrdude: safemode: hfuse reads as 0
avrdude: safemode: efuse reads as 0
avrdude: safemode: Fuses OK (H:00, E:00, L:00)

 

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

Somehow I don't believe that UNLESS someone inadvertently wrote to the fuses. It is certainly NOT the default, from the factory, fuse setting. That must turn WDTON on as "on" is zero. I can't even think of what this does to the clock settings.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Aug 28, 2017 - 02:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
Your while(1) loop does nothing and may well be optimized out.
???  There are a few while(1) loops posted.  Unless I missed something, each does an operation on a volatile I/O register, and has a delay.  Which part of that "does nothing" and can be optimized away?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Lee, you are probably correct. PORTx is volatile, I guess, so that, alone, should guarantee that the contents of the while loop are included.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
avr-gcc -Wall -Wextra -Wpedantic --std=gnu99 -Os -mmcu=atmega328p    -c -o main.o main.c

Well that's not the whole story, is it?  That just gets you main.o with no linking.  That's what the -c option means.

 

 

avrdude: safemode: Fuses OK (H:00, E:00, L:00)

This suggests you're using a bootloader, which doesn't seem right for an xplained mini board.

 

Post the .hex file that you're >>actually<< uploading to you target.  Better yet, fetch that .hex file from the target itself:

 

$ avrdude -c stk500v1 -p m328p -P /dev/tty.usbmodem1452 -b57600 -U flash:r:foo.hex:i

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Do you mean to post directly the .hex? This is it.

:10000000239A0895239A2B982AEA36EE86E991E0FE
:100010002150304080409040D9F700002B9A2B9A15
:100020002AEA36EE86E991E0215030408040904047
:06003000D9F70000F4CF37
:00000001FF

I still cannot understand, what's the deal with putting the assignment outside the main function? Now I'm playing with the Atmega and a for loop inside main, which has a volatile iteration variable called i. This for loop doesn't work at all (i is not updated) when i declare it as a global variable, but i moved the declaration to the main and it worked inmediately.

 

Thanks for your answers

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

There can be a number of reasons for spurios resets.

 

- WDT is enabled without kicking the dog.

- Excessive noise on the reset pin (add external pullup resistor, short leads)

- No or bad decoupling, too long wires on the decoupling capacitors. Expecially with an enabled brown out detector.

- Vcc is too low (to near Brown out threshold) or has excessive noise.

- Powerfull spikes / surges on the power supply from for example fluoresent ballasts switching or transformers.

 

For portability between different uC's you can just use different #defines.

something like:

 

#if CPU == ATMEGA8
  #define LEDDDR  DDRB
  #define LEDPORT PORTB
  #define LEDPIN (1<<4)
# else if CPU == MSP40
 #define ...
#endif


void main(void) {
    
    LED_DDR |= LEDPIN;  // Set as output.
    LEDPORT |= LEDPIN;
}

But this does not work well with any CPU architecture.

In the arduino world I/O port manipulation is done with functions "digitalWrite()", which makes it portable between any architecture, but it makes it very slow. Especially because those functions do more than they seem to do on first sight such as disabling pwm if the port pin was configured as a pwm output earlier.

 

There is also a little project which replaces the "digitalWrite()" functions with macros, which get optimised into single sbi / cbi instructions.

You might want to look into that. I believe it's called "fastdigitalwrite".

 

 

 

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

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

Yup.  That's your problem.  You didn't link.

$ avr-objdump -D -m avr -z /tmp/foo.hex 

/tmp/foo.hex:     file format ihex

Disassembly of section .sec1:

00000000 <.sec1>:
   0:   23 9a           sbi     0x04, 3 ; 4
   2:   08 95           ret
   4:   23 9a           sbi     0x04, 3 ; 4
   6:   2b 98           cbi     0x05, 3 ; 5
   8:   2a ea           ldi     r18, 0xAA       ; 170
   a:   36 ee           ldi     r19, 0xE6       ; 230
   c:   86 e9           ldi     r24, 0x96       ; 150
   e:   91 e0           ldi     r25, 0x01       ; 1
  10:   21 50           subi    r18, 0x01       ; 1
  12:   30 40           sbci    r19, 0x00       ; 0
  14:   80 40           sbci    r24, 0x00       ; 0
  16:   90 40           sbci    r25, 0x00       ; 0
  18:   d9 f7           brne    .-10            ;  0x10
  1a:   00 00           nop
  1c:   2b 9a           sbi     0x05, 3 ; 5
  1e:   2b 9a           sbi     0x05, 3 ; 5
  20:   2a ea           ldi     r18, 0xAA       ; 170
  22:   36 ee           ldi     r19, 0xE6       ; 230
  24:   86 e9           ldi     r24, 0x96       ; 150
  26:   91 e0           ldi     r25, 0x01       ; 1
  28:   21 50           subi    r18, 0x01       ; 1
  2a:   30 40           sbci    r19, 0x00       ; 0
  2c:   80 40           sbci    r24, 0x00       ; 0
  2e:   90 40           sbci    r25, 0x00       ; 0
  30:   d9 f7           brne    .-10            ;  0x28
  32:   00 00           nop
  34:   f4 cf           rjmp    .-24            ;  0x1e

OK, so it looks like you've just used objcopy to turn main.o into a .hex file, and pushed that up to the AVR.  The result is that the AVR is running incomplete and nonsensical code.

 

AVRs start executing at address 0x0000 (unless there's a bootloader, but even then the bootloader will pass control to 0x0000 when it's time).  That's called the 'reset vector'.  It's the first vector in a table of vectors, the reset of which are called interrupt vectors.  The names are meaningful.  The reset vector is executed after any reset (power-on, external, brown-out, watch-dog).  Each interrupt vector is executed after its associated (and enabled) interrupt event occurs.

 

Normally, the compiler (or assembler programmer) will have a jmp instruction at the reset vector to take execution to some intialisation code, and then on to the meat of the program in main.

 

In your case, you've skipped the step that links the reset vector, the interrupt vectors, the initialisation code (called the CRT, for 'C Run-Time'), with the rest of your written code.  The main.o file you've compiled, objcopied, and uploaded simply has your functions in the order they were written.  As such, your init_pins() function is there first, right where the reset vector is, so the AVR executes that immediately after a reset.  It sets PB3 as an output with the sbi instruction, then returns.

 

Returns where?  You may well ask.  Nowhere.  The ret instruction is meant to be called at the end of a piece of code that was reached by means of a call/rcall/icall instruction.  That 'call' places the return address of the caller on the stack.  The ret instruction retrieves that address from the stack and returns execution to the instruction after the call which got us there.  In your case, the stack contains nothing.  Or rather, nothing known.  SRAM contents are effectively random after a power-up, so your code runs amok, just as you've reported.

 

Show us your entire set of build/upload commands.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Mon. Aug 28, 2017 - 04:02 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow, I couln't get until that point by myself haha, thanks! Well, the commands I use to compile and upload are:

 

avr-gcc -Wall -Wextra -Wpedantic --std=gnu99 -Os -mmcu=atmega328p    -c -o main.o main.c
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -vvv -p atmega328p -F -c stk500v1 -P /dev/tty.usbmodem1452 -b57600 -D -V -U flash:w:main.hex:i

You say I need to link, but link what? I don't have other libs in this project, or I didn't understand.

Last Edited: Mon. Aug 28, 2017 - 05:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Building an executable is done in two steps:

 

1. Source files are compiled into object files.

2. All object files are linked together, and together with the run-time library to form the executable.

 

Even if you have just one source file, there will be two stages, compiling it and then linking the result with the run-time.

 

The "one source file" case is common enough for the GCC folks to supply a shortcut, where the command line does both steps:

 

1. Remove the -c option (which is the option to only compile, and not link)

2. Specify output with the file extension .elf (not required, but really recommended).

 

Your command line for compiling and linking a single-source-file project in one go would be:

 

avr-gcc -Wall -Wextra -Wpedantic --std=gnu99 -Os -mmcu=atmega328p -o main.elf main.c

After that create the .hex and flash it with avrdude as you stated above.

 

Curious: Where did you pick up that avr-gcc command line?

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Mon. Aug 28, 2017 - 08:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually I downloaded a Makefile from internet:

# Basic AVR Makefile
# Author: Zachary Voase
# License: Public Domain <http://unlicense.org/>
#
# Configured to work with the Arduino Uno R3, programmed from a Mac, using
# CrossPack (http://www.obdev.at/products/crosspack/index.html).

# This needs to be the TTY device on your Mac at which the Arduino is mounted.
# Mine is normally either 1421 or 1411.
PORT       = /dev/tty.usbmodem1452

# Consult Arduino.app/Contents/Resources/Java/hardware/arduino/boards.txt for
# values to use for these configuration parameters.
DEVICE     = atmega328p
F_CPU      = 16000000
AVRDUDE    = avrdude -vvv -p $(DEVICE) -F -c stk500v1 -P $(PORT) -b57600 -D -V

# These probably need to be left alone unless you know what you're doing.
AR=avr-ar
AS=avr-as
CC=avr-gcc
CXX=avr-g++
CFLAGS=-Wall -Wextra -Wpedantic --std=gnu99 -Os -mmcu=$(DEVICE)

.PHONY: all flash fuse install load clean

all:	main.hex

flash:	main.hex
	$(AVRDUDE) -U flash:w:main.hex:i

clean:
	rm -f main.hex main.elf *.o

# This will be made by an implicit Make rule.
main.o: main.c

main.elf: main.o
	$(CC) $(LDFLAGS) -o $@ $^

main.hex: main.elf
	rm -f main.hex
	avr-objcopy -j .text -j .data -O ihex main.elf main.hex
	#avr-size --format=avr --mcu=$(DEVICE) main.elf
# If you have an EEPROM section, you must also create a hex file for the
# EEPROM and add it to the "flash" target.

Actually it's strange that I can't find the "-c" option in the Makefile, but it certainly appears in the command line when compiling:

saogalde@seba-mbpro:~$ make clean; make; make flash
rm -f main.hex main.elf *.o
avr-gcc -Wall -Wextra -Wpedantic --std=gnu99 -Os -mmcu=atmega328p    -c -o main.o main.c

[...]

What to do? :/

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

saogalde wrote:
What to do? :/

What I told you above. For a single source file project (e.g. main.c) you just

avr-gcc -Wall -Wextra -Wpedantic --std=gnu99 -Os -mmcu=atmega328p -o main.elf main.c

and then

avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -vvv -p atmega328p -F -c stk500v1 -P /dev/tty.usbmodem1452 -b57600 -D -V -U flash:w:main.hex:

 


 

The makefile you shown can also handle this case, and if you want to use it you call on GNU Make to process it e.g. the way you showed

 

make clean; make; make flash

This will actually call GNU Make three times, first to clean the project (delete temporary/intermediate and output files), then to do the build, and finally to flash the AVR. The second call will involve several steps of which the first is what you quoted above:

avr-gcc -Wall -Wextra -Wpedantic --std=gnu99 -Os -mmcu=atmega328p    -c -o main.o main.c

This command comes from a built-in (implicit) rule in GNU Make which gets activated by the makefile simply stating that the .o file depends on the .c file:

main.o: main.c

 

But there will be more steps (that you have not shown), something like

 

avr-gcc -o main.elf main.o
rm -f main.hex
avr-objcopy -j .text -j .data -O ihex main.elf main.hex

The fist of those is the command to link, here as a separate step (linking implied by the absence of the -c switch, the input being the output from the compile command, and the output being the .elf file) . This is being done because there is an explicit rule in the makefile stating that main.elf depends on main.o, and the "recipe" for how to create main.elf from main.o:

main.elf: main.o
	$(CC) $(LDFLAGS) -o $@ $^

The $, @ and ^ stuff in there are referring to Make variables ($(CC) holds the name of the actual copiler, in this case "avr-gcc", the $(LDFLAGS) hold flags/options to the linker, the $@ is a variable that alsways holds the target of the rule i.e. "main.elf" in this case, and finally the $^ holds the list of dependencies of the rule - in this case the single filename "main.o". When the rule gets "expanded" it ends up looking something like what I showed above. 

 

 

Then there's housekeeping: Deleting any old .hex file.

 

Then follows the command  (objcopy) to create the .hex from the .elf .

 

Welcome to the wonderful world of GNU Make. (-: You might actually need to know a bit about it some day..

 


 

But bottom line on what to do:

EITHER use the makefile as you showed in your post,

OR build the .elf as a single line command as I showed in my previous post (and then you'd also have to do the .hex and flashing "by hand".

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Mon. Aug 28, 2017 - 12:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

While I am luddite and love the command line the fact is that sometimes it just pays to get an IDE and let it sort out all the build commands for you. Get Code::Blocks or Eclipse (CDT) or similar. 

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

saogalde wrote:

You say I need to link, but link what? I don't have other libs in this project, or I didn't understand.

You didn't read my post very carefully.

 

joeymorin wrote:

In your case, you've skipped the step that links the reset vector, the interrupt vectors, the initialisation code (called the CRT, for 'C Run-Time'), with the rest of your written code.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Thanks everyone :) It worked like a charm by taking the '-c' option out.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
avr-gcc -Wall -Wextra -Wpedantic --std=gnu99 -Os -mmcu=atmega328p    -c -o main.o main.c
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -vvv -p atmega328p -F -c stk500v1 -P /dev/tty.usbmodem1452 -b57600 -D -V -U flash:w:main.hex:i

 

Note that this sequence doesn't even give you your recent code in the .hex file.

The avr-gcc command will output main.o, and the objcopy will create the .hex file from whatever main.elf happened to still be sitting around.

 

 

You say I need to link, but link what?

for a single .c file, the "link" step adds the runtime environment (crtxxxx.o) that contains the interrupt vectors and the pre-main() initialization (initialize SP, clear .bss, load .data from flash, etc.)   And it does the final "relocation" of symbols to their actual absolute locations  (.o files have essentially instructions that say "at location X+<startaddress of this .o>, put a value that is  Y+<startaddress of this .o>")

 

It's a little obscure in makefiles, because the "avr-gcc" command is usually used to do the actual linking - you don't often see an actual "avr-ld" command.