A resposta aceita é ótima, pois me deu uma lição valiosa em todos os tipos de ferramentas de depuração (avr-objdump -D tornou-se um amigo próximo). Ou seja, a linha:
${OBJCOPY} -O ihex -R .eeprom $< $@
está faltando o sinalizador de arquitetura e deve ler
$ {OBJCOPY} -mmcu = atmega328p -O ihex -R .eeprom $ <$ @
Sem o sinalizador de arquitetura -mmcu, o avr-gcc acha que estamos compilando a arquitetura 8515 (definitivamente não) e produz o arquivo .elf sem instruções iniciais para a inicialização, ou seja, sem instruções para chamar a função "principal" etc.
Isso resulta em comportamento confuso, pois qualquer programa simples (por exemplo, piscar) com apenas a função "principal" funciona perfeitamente, mas se você definir outra função antes ou depois da "principal", ela executa essa função e nunca chama "principal" ou reinicia o tempo todo etc.
Também não sou um fã em particular de evitar a verificação do tipo MCU correto e do programa carregado, portanto, eu recomendaria não usar -F e -V e usar -v.
Portanto, a resposta melhorada pode ser:
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}
-I/usr/share/arduino/hardware/arduino/cores/arduino -I/usr/share/arduino/hardware/arduino/variants/standard
e vincularlibcore.a
. :-)