Basic makefile for avr-gcc

It's no different to working with Make and any other form of GCC. Just set your CC variable and CFLAGS variable accordingly and work as per normal.

For instance, I just knocked this one up:

CC=avr-gcc
OBJCOPY=avr-objcopy

CFLAGS=-Os -DF_CPU=16000000UL -mmcu=atmega328p
PORT=/dev/ttyACM0

led.hex: led.elf
    ${OBJCOPY} -O ihex -R .eeprom led.elf led.hex

led.elf: led.o
    ${CC} -o led.elf led.o

install: led.hex
    avrdude -F -V -c arduino -p ATMEGA328P -P ${PORT} -b 115200 -U flash:w:led.hex

That's saying that any automatic C compilation will be done with avr-gcc and the flags specified in CFLAGS. It will by default make the hex file using OBJCOPY, which is set to the avr one, which relies on the file led.elf - so to get that file it runs the led.elf target, which links the object file led.o with the default libraries using whatever was set in CC. To do that it needs led.o, and it makes that automatically using the program specified in CC and the flags in CFLAGS. You can then optionally to make install which will run avrdude to install the hex file into the chip.

You can make it even more generic so you can copy it in to other projects and make the minimum changes necessary:

BIN=led
OBJS=led.o test.o

CC=avr-gcc
OBJCOPY=avr-objcopy
CFLAGS=-Os -DF_CPU=16000000UL -mmcu=atmega328p
PORT=/dev/ttyACM0

${BIN}.hex: ${BIN}.elf
    ${OBJCOPY} -O ihex -R .eeprom $< $@

${BIN}.elf: ${OBJS}
    ${CC} -o $@ $^

install: ${BIN}.hex
    avrdude -F -V -c arduino -p ATMEGA328P -P ${PORT} -b 115200 -U flash:w:$<

clean:
    rm -f ${BIN}.elf ${BIN}.hex ${OBJS}

That uses "automatic variables" and simple name replacement. BIN contains the "base" of your binary files, OBJS contains the list of object files. $@ is the name of the current target, $< is the name of the first prerequisite, and $^ is the list of all the prerequisites. Just change BIN and OBJS to suit. As a bonus I have thrown in make clean to remove the compiled files and just leave you with the source.


The accepted answer is great as it has given me a valuable lesson in all kinds of debugging tools (avr-objdump -D has become a close friend). Namely, the line:

${OBJCOPY} -O ihex -R .eeprom $< $@

is missing the architecture flag and should read

${OBJCOPY} -mmcu=atmega328p -O ihex -R .eeprom $< $@

Without the -mmcu architecture flag, avr-gcc guesses we are compiling for 8515 architecture (definitely not) and it produces the .elf file without initial instructions for initializing, i.e. without instructions to call the "main" function etc.

This results in confusing behavior as any simple program (e.g. blink) with only the "main" function works perfectly, but if you define another function before or after the "main", it runs that function and never calls "main" or it restarts all the time etc.

I am also not a particular fan of avoiding the verification of correct MCU type and uploaded program, so I'd advocate not to use -F and -V and use -v instead.

So, the improved answer could be:

PKG=led
BIN=${PKG}
OBJS=${PKG}.o
MCU=atmega328p

CC=avr-gcc
OBJCOPY=avr-objcopy
CFLAGS=-Os -DF_CPU=16000000UL -mmcu=${MCU} -Wall
PORT=/dev/ttyACM0

${BIN}.hex: ${BIN}.elf
        ${OBJCOPY} -O ihex $< $@

${BIN}.elf: ${OBJS}
        ${CC} -mmcu=${MCU} -o $@ $^

install: ${BIN}.hex
        avrdude -v -c arduino -p ${MCU} -P ${PORT} -b 115200 -U flash:w:$<

clean:
        rm -f ${BIN}.elf ${BIN}.hex ${OBJS}