8

I would like to make a makefile for compiling c programs for the arduino. I am somewhat familiar with make but have never used it with avr-gcc. What is the simplest way I could put the commands below in a makefile?

$ avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o led.o led.c
$ avr-gcc -mmcu=atmega328p led.o -o led
$ avr-objcopy -O ihex -R .eeprom led led.hex
$ avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:led.hex
1
  • Since this is an Arduino forum, it would be more politically correct to compile with -I/usr/share/arduino/hardware/arduino/cores/arduino -I/usr/share/arduino/hardware/arduino/variants/standard and link with libcore.a. :-) Commented May 24, 2015 at 18:18

2 Answers 2

7

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.

4
  • the posted makefile needs another line near the top; that says: '.PHONY : install clean' Commented May 24, 2015 at 18:20
  • @user3629249 Why? The makefile as posted works perfectly. You only need .PHONY for targets called as prerequisites, not targets called manually which those are.
    – Majenko
    Commented May 24, 2015 at 18:21
  • 2
    @Majenko It's good practice to create a phony target for anything that's not actually a filename. If you create a file called install or a file called clean (shell scripts, perhaps?), then make may think that they're Up to date and do nothing.
    – wchargin
    Commented May 24, 2015 at 21:24
  • 1
    @WChargin If you do something as daft as that then you deserve it not to work. Your shell scripts should be clean.sh and install.sh if you must have them.
    – Majenko
    Commented May 24, 2015 at 21:27
5

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}
3
  • You wrote: “Without the -mmcu architecture flag, avr-gcc guesses we are compiling for 8515 architecture”. Actually it's avr2: “‘Classic’ devices with up to 8 KiB of program memory.” Commented Jul 2, 2017 at 11:51
  • I was referring the the Atmel reference atmel.com/webdoc/avrlibcreferencemanual/… Are they mistaken? Commented Jul 9, 2017 at 9:13
  • Maybe they mean the AT90S8515 MCU which, unlike its replacement (ATmega8515), has an avr2 architecture. The page you link to may have been written in a time when “8515” was not ambiguous. And they don't call it an ”architecture”, since it's only one of many MCUs sharing the avr2 architecture. Commented Jul 9, 2017 at 10:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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