Sunday, April 21, 2013

Compiling Arduino program using Standard GNU toolchains

Arduino IDE has simplified our life when developing firmware for Atmel-based MCU, but for more advanced programmers, it's not a quick-and-fast way to compile.  A code developed using "Processing", a thin layer between C++ and Arduino code cannot be compiled using avr-gcc toolchains straightforward.

The following steps are needed to make the compilation becomes standard compilation as other open-source code compilation.

  1. Copy the folder <arduino root>/arduino/hardware/arduino/cores/* and <arduino root>/arduino/hardware/arduino/variants/* to a new folder, e.g. /home/myname/src/arduino/include
  2. Run the Makefile below to build a library.  In my case, I tried to build a lib for my AtMega328p-based Arduino Uno.  Goto the folder and do "make"
  3. Once the library is built, we can link it to our program (we need to rename our program's extension from *.ino to *.cpp).  See the second Makefile for an example how to build and link with the newly created lib file.
  4. To upload the program to the target (Arduino Uno), we need an ICSP programmer (available for cheap on eBay) and use "avrdude" command to do the programming.

The Makefile to build atmega328p

#MCU_TARGET     = at90s2313
#MCU_TARGET     = at90s2333
#MCU_TARGET     = at90s4414
#MCU_TARGET     = at90s4433
#MCU_TARGET     = at90s4434
#MCU_TARGET     = at90s8515
#MCU_TARGET     = at90s8535
#MCU_TARGET     = atmega128
#MCU_TARGET     = atmega1280
#MCU_TARGET     = atmega1281
#MCU_TARGET     = atmega1284p
#MCU_TARGET     = atmega16
#MCU_TARGET     = atmega163
#MCU_TARGET     = atmega164p
#MCU_TARGET     = atmega165
#MCU_TARGET     = atmega165p
#MCU_TARGET     = atmega168
#MCU_TARGET     = atmega169
#MCU_TARGET     = atmega169p
#MCU_TARGET     = atmega2560
#MCU_TARGET     = atmega2561
#MCU_TARGET     = atmega32
#MCU_TARGET     = atmega324p
#MCU_TARGET     = atmega325
#MCU_TARGET     = atmega3250
#MCU_TARGET     = atmega329
#MCU_TARGET     = atmega3290
MCU_TARGET      = atmega328p
#MCU_TARGET     = atmega48
#MCU_TARGET     = atmega64
#MCU_TARGET     = atmega640
#MCU_TARGET     = atmega644
#MCU_TARGET     = atmega644p
#MCU_TARGET     = atmega645
#MCU_TARGET     = atmega6450
#MCU_TARGET     = atmega649
#MCU_TARGET     = atmega6490
#MCU_TARGET     = atmega8
#MCU_TARGET     = atmega8515
#MCU_TARGET     = atmega8535
#MCU_TARGET     = atmega88
#MCU_TARGET     = attiny2313
#MCU_TARGET     = attiny24
#MCU_TARGET     = attiny25
#MCU_TARGET     = attiny26
#MCU_TARGET     = attiny261
#MCU_TARGET     = attiny44
#MCU_TARGET     = attiny45
#MCU_TARGET     = attiny461
#MCU_TARGET     = attiny84
#MCU_TARGET     = attiny85
#MCU_TARGET     = attiny861

#DEFS           = -DF_CPU=16000000UL
DEFS           = -D__AVR_ATmega328P__ -DF_CPU=16000000UL
ARDUINO           = /opt/arduino-1.0.4
MYARDUINO       = .
INCS           = -I $(MYARDUINO)/ \
                 -I $(MYARDUINO)/arduino \
                 -I $(MYARDUINO)/variants \
                 -I $(MYARDUINO)/variants/standard

LIBS           = -lm -lc

OPTIMIZE       = -O2 -finline-small-functions

# You should not have to change anything below here.

CC             = avr-gcc
CXX               = avr-c++
AR               = avr-ar

# Override is only needed by avr-lib build system.

override CFLAGS        = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS) $(INCS)
override CXXFLAGS      = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS) $(INCS)
override LDFLAGS       = -Wl,-Map,$(PRG).map

OBJCOPY        = avr-objcopy
OBJDUMP        = avr-objdump


PRG            = libatmega328p
SRCS           = \
                 $(MYARDUINO)/arduino/Print.cpp \
                 $(MYARDUINO)/arduino/WString.cpp \
                 $(MYARDUINO)/arduino/Stream.cpp \
                 $(MYARDUINO)/arduino/CDC.cpp \
                 $(MYARDUINO)/arduino/HardwareSerial.cpp \
                 $(MYARDUINO)/arduino/HID.cpp \
                 $(MYARDUINO)/arduino/IPAddress.cpp \
                 $(MYARDUINO)/arduino/main.cpp \
                 $(MYARDUINO)/arduino/new.cpp \
                 $(MYARDUINO)/arduino/Tone.cpp \
                 $(MYARDUINO)/arduino/USBCore.cpp \
                 $(MYARDUINO)/arduino/WMath.cpp \
                 $(MYARDUINO)/arduino/malloc.c \
                 $(MYARDUINO)/arduino/WInterrupts.c \
                 $(MYARDUINO)/arduino/wiring_analog.c \
                 $(MYARDUINO)/arduino/wiring_digital.c \
                 $(MYARDUINO)/arduino/wiring_pulse.c \
                 $(MYARDUINO)/arduino/wiring_shift.c \
                 $(MYARDUINO)/arduino/wiring.c

BUILDDIR = ./build

all: $(PRG).a $(OBJS)

#OBJS := $(patsubst %.cpp, obj/%.o, $(SRCS))
#OBJS := $(patsubst %.c, obj/%.o, $(SRCS))

OBJS := $(notdir $(SRCS))
OBJS := $(OBJS:.cpp=.o)
OBJS := $(OBJS:.c=.o)
OBJS := $(patsubst %.o, $(BUILDDIR)/%.o, $(OBJS))

#----------------------------- dependency:

$(OBJS): | $(BUILDDIR)

$(BUILDDIR):
      @mkdir -p $@

$(BUILDDIR)/%.o: $(MYARDUINO)/arduino/%.c
    @echo $<
    $(CC) -c $(CFLAGS) $< -o $@

$(BUILDDIR)/%.o: $(MYARDUINO)/arduino/%.cpp
    @echo $<
    $(CXX) -c $(CXXFLAGS) $< -o $@

#$(OBJS): $(SRCS)
#    @echo "Compiling $< to $@"
#    $(CXX) -S $(CXXFLAGS) $< -o $@


$(PRG).a: $(OBJS)
    @echo "***********************************************************"
    @echo OBJS=$(OBJS)
    @echo "***********************************************************"
    @echo "Building LIB=$(PRG).a from $?"
    @echo "-----------------------------------------------------------"
    $(AR) qusv $@ $?


clean:
    rm -rf *.o $(PRG).a *.eps *.png *.pdf *.bak $(OBJS)
    rm -rf *.lst *.map $(EXTRA_CLEAN_FILES)

lst:  $(PRG).lst

%.lst: %.a
    $(OBJDUMP) -h -S $< > $@

# Rules for building the .text rom images

text: hex bin srec

hex:  $(PRG).hex
bin:  $(PRG).bin
srec: $(PRG).srec

%.hex: %.elf
    $(OBJCOPY) -j .text -j .data -O ihex $< $@

%.srec: %.elf
    $(OBJCOPY) -j .text -j .data -O srec $< $@

%.bin: %.elf
    $(OBJCOPY) -j .text -j .data -O binary $< $@

# Rules for building the .eeprom rom images

eeprom: ehex ebin esrec

ehex:  $(PRG)_eeprom.hex
ebin:  $(PRG)_eeprom.bin
esrec: $(PRG)_eeprom.srec

%_eeprom.hex: %.elf
    $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ \
    || { echo empty $@ not generated; exit 0; }

%_eeprom.srec: %.elf
    $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O srec $< $@ \
    || { echo empty $@ not generated; exit 0; }

%_eeprom.bin: %.elf
    $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O binary $< $@ \
    || { echo empty $@ not generated; exit 0; }

# Every thing below here is used by avr-libc's build system and can be ignored
# by the casual user.

FIG2DEV                 = fig2dev
EXTRA_CLEAN_FILES       = *.hex *.bin *.srec

dox: eps png pdf

eps: $(PRG).eps
png: $(PRG).png
pdf: $(PRG).pdf

%.eps: %.fig
    $(FIG2DEV) -L eps $< $@

%.pdf: %.fig
    $(FIG2DEV) -L pdf $< $@

%.png: %.fig
    $(FIG2DEV) -L png $< $@



A Makefile to build our application:

#MCU_TARGET     = at90s2313
#MCU_TARGET     = at90s2333
#MCU_TARGET     = at90s4414
#MCU_TARGET     = at90s4433
#MCU_TARGET     = at90s4434
#MCU_TARGET     = at90s8515
#MCU_TARGET     = at90s8535
#MCU_TARGET     = atmega128
#MCU_TARGET     = atmega1280
#MCU_TARGET     = atmega1281
#MCU_TARGET     = atmega1284p
#MCU_TARGET     = atmega16
#MCU_TARGET     = atmega163
#MCU_TARGET     = atmega164p
#MCU_TARGET     = atmega165
#MCU_TARGET     = atmega165p
#MCU_TARGET     = atmega168
#MCU_TARGET     = atmega169
#MCU_TARGET     = atmega169p
#MCU_TARGET     = atmega2560
#MCU_TARGET     = atmega2561
#MCU_TARGET     = atmega32
#MCU_TARGET     = atmega324p
#MCU_TARGET     = atmega325
#MCU_TARGET     = atmega3250
#MCU_TARGET     = atmega329
#MCU_TARGET     = atmega3290
MCU_TARGET      = atmega328p
#MCU_TARGET     = atmega48
#MCU_TARGET     = atmega64
#MCU_TARGET     = atmega640
#MCU_TARGET     = atmega644
#MCU_TARGET     = atmega644p
#MCU_TARGET     = atmega645
#MCU_TARGET     = atmega6450
#MCU_TARGET     = atmega649
#MCU_TARGET     = atmega6490
#MCU_TARGET     = atmega8
#MCU_TARGET     = atmega8515
#MCU_TARGET     = atmega8535
#MCU_TARGET     = atmega88
#MCU_TARGET     = attiny2313
#MCU_TARGET     = attiny24
#MCU_TARGET     = attiny25
#MCU_TARGET     = attiny26
#MCU_TARGET     = attiny261
#MCU_TARGET     = attiny44
#MCU_TARGET     = attiny45
#MCU_TARGET     = attiny461
#MCU_TARGET     = attiny84
#MCU_TARGET     = attiny85
#MCU_TARGET     = attiny861
OPTIMIZE       = -O2

DEFS           = -D__AVR_ATmega328P__ -DF_CPU=16000000UL
ARDUINO           = /opt/arduino-1.0.4
MYARDUINO       = /home/myname/src/arduino

INCS           = -I $(MYARDUINO)/include \
                 -I $(MYARDUINO)/include/arduino \
                 -I $(MYARDUINO)/include/variants \
                 -I $(MYARDUINO)/include/variants/standard \
                 -I /opt/arduino-1.0.4/libraries/LiquidCrystal

LIBS           = -L $(MYARDUINO)/include -lm -lc

# You should not have to change anything below here.

CC             = avr-gcc
CXX               = avr-c++

# Override is only needed by avr-lib build system.

CFLAGS        = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS) $(INCS)
override LDFLAGS       = -Wl,-Map,$(PRG).map

OBJCOPY        = avr-objcopy
OBJDUMP        = avr-objdump


PRG            = HelloWorld
SRCS           = \
                 HelloWorld.cpp \
                 /opt/arduino-1.0.4/libraries/LiquidCrystal/LiquidCrystal.cpp


all: $(PRG).elf lst text eeprom

OBJS := $(SRCS:.cpp=.o)
OBJS := $(OBJS:.c=.o)

%.o: %.c
    $(CC) -c $(CFLAGS) $? -o $@

%.o: %.cpp
    $(CXX) -c $(CFLAGS) $? -o $@

$(PRG).elf: $(OBJS)
    @echo "***********************************************************"
    @echo OBJS=$(OBJS)
    @echo "***********************************************************"
    @echo "Compiling $?"
    @echo "-----------------------------------------------------------"
    $(CXX) $(CFLAGS) $(LDFLAGS) -o $@ $(LIBS) $? ./include/libatmega328p.a

# dependency:


clean:
    rm -rf *.o $(PRG).elf *.eps *.png *.pdf *.bak
    rm -rf *.lst *.map $(EXTRA_CLEAN_FILES)

lst:  $(PRG).lst

%.lst: %.elf
    $(OBJDUMP) -h -S $< > $@

# Rules for building the .text rom images

text: hex bin srec

hex:  $(PRG).hex
bin:  $(PRG).bin
srec: $(PRG).srec

%.hex: %.elf
    $(OBJCOPY) -j .text -j .data -O ihex $< $@

%.srec: %.elf
    $(OBJCOPY) -j .text -j .data -O srec $< $@

%.bin: %.elf
    $(OBJCOPY) -j .text -j .data -O binary $< $@

# Rules for building the .eeprom rom images

eeprom: ehex ebin esrec

ehex:  $(PRG)_eeprom.hex
ebin:  $(PRG)_eeprom.bin
esrec: $(PRG)_eeprom.srec

%_eeprom.hex: %.elf
    $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ \
    || { echo empty $@ not generated; exit 0; }

%_eeprom.srec: %.elf
    $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O srec $< $@ \
    || { echo empty $@ not generated; exit 0; }

%_eeprom.bin: %.elf
    $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O binary $< $@ \
    || { echo empty $@ not generated; exit 0; }

# Every thing below here is used by avr-libc's build system and can be ignored
# by the casual user.

FIG2DEV                 = fig2dev
EXTRA_CLEAN_FILES       = *.hex *.bin *.srec

dox: eps png pdf

eps: $(PRG).eps
png: $(PRG).png
pdf: $(PRG).pdf

%.eps: %.fig
    $(FIG2DEV) -L eps $< $@

%.pdf: %.fig
    $(FIG2DEV) -L pdf $< $@

%.png: %.fig
    $(FIG2DEV) -L png $< $@
 

No comments:

Post a Comment