보통은 AVR
을 MCU
로 사용하여 개발을 하게 되는 경우, 개발보드로 Arduino
를 사용하거나, 혹은 Atmel Studio
를 통하여 개발하는 경우가 많으나, 필자와 같이 바닥부터 개발하려 하는 경우에는 기초적인 빌드 시스템부터 구축해야 한다.
이를 위해 avr-gcc
와 avrdude
를 컴파일러로 사용하는 환경에서 이용 가능한 AVR
전용 makefile
을 작성하였다. AVR
전용이라고 했지만 조금만 수정하면 일반적인 빌드 시스템에서도 사용이 가능하다.
################################################################################
# Load dependancies and configuration files
################################################################################
BUILD_CONF ?= ./build.conf
-include $(BUILD_CONF)
-include $(DEPENDANCIES)
################################################################################
# Build configuration
################################################################################
# This settings can be overrided by `build.config` file
# and also exported to other sub-makefile.
#########################################
# Build settings
#########################################
MACHINE ?= atmega328p
PROGRAMMER ?= arduino
CONFIG ?= /etc/avrdude.conf
PORT ?= /dev/ttyACM0
UPLOAD_BAUD ?= 115200
SERIAL_BAUD ?= 9600
CPU_FREQ ?= 16000000UL
ARCH ?= elf32-avr
#########################################
# Compiler settings
#########################################
CC ?= avr-gcc
CXX ?= avr-g++
CFLAGS ?= -std=c99 -mmcu=$(MACHINE) \
-MMD -O2 -ffunction-sections \
-DF_CPU=$(CPU_FREQ) \
-DBAUD=$(SERIAL_BAUD)
CXXFLAGS ?=
LDFLAGS ?= -Wl,--gc-sections
################################################################################
# Prerequisite directories
################################################################################
LIBRARY_DIR := ./library
LIBRARY := miniAVRfreeRTOS
LIBRARIES := ${foreach lib,$(LIBRARY),$(LIBRARY_DIR)/$(lib)}
BUILD_DIR := ./build
SOURCE_DIR := ./source
INCLUDE_DIR := ./include ${addsuffix /include,$(LIBRARIES)}
################################################################################
# Build target files
################################################################################
# Core build target
#########################################
TARGET := output.elf
HEXTARGET := ${basename $(TARGET)}.hex
#########################################
# Source & Object files
#########################################
SOURCES := ${shell find $(SOURCE_DIR) -name '*.c' -or -name '*.cpp'}
OBJECTS := ${patsubst $(SOURCE_DIR)/%,$(BUILD_DIR)/%,$(SOURCES:%=%.o)}
LIBOBJS := ${foreach lib,$(LIBRARIES),${wildcard $(lib)/$(BUILD_DIR)/*.o}}
DEPENDANCIES := $(OBJECTS:%.o=%.d)
INCLUDE_FLAG := ${addprefix -I,$(INCLUDE_DIR)}
################################################################################
# Rules
################################################################################
all: library compile
compile: $(TARGET)
library: $(LIBRARIES)
${MAKE} -C $? BUILD_CONF=${realpath $(BUILD_CONF)} compile
$(TARGET): $(OBJECTS) $(LIBOBJS)
$(CC) -o $@ $? $(CFLAGS) $(LDFLAGS)
$(BUILD_DIR)/%.c.o: $(SOURCE_DIR)/%.c
mkdir -p $(dir $@)
$(CC) -c -o $@ $? $(CFLAGS) $(INCLUDE_FLAG)
$(BUILD_DIR)/%.cpp.o: $(SOURCE_DIR)/%.cpp
mkdir -p $(dir $@)
$(CXX) -c -o $@ $? $(CXXFLAGS) $(INCLUDE_FLAG)
$(BUILD_DIR):
mkdir -p $(BUILD_DIR)
################################################################################
# Phony
################################################################################
.PHONY: all library clean cleanall hex upload screen
hex: $(TARGET)
avr-objcopy -I $(ARCH) -O ihex $? $(HEXTARGET)
screen: ${basename $(TARGET)}.hex
sudo screen $(PORT) $(SERIAL_BAUD)
upload: ${basename $(TARGET)}.hex
avrdude -C$(CONFIG) -v -p$(MACHINE) \
-c$(PROGRAMMER) -P$(PORT) -b$(UPLOAD_BAUD) -D \
-Uflash:w:$?:i
clean:
rm -rf $(BUILD_DIR)
rm -rf $(TARGET)
rm -rf $(HEXTARGET)
cleanall: clean
cleanall: $(LIBRARIES)
${MAKE} -C $? clean
가장 먼저 위 makefile
을 사용하는 프로젝트가 적절한 양식을 갖춰야 한다. makefile
파일을 포함한 디렉토리 내에는 다음이 포함된다:
source
라는 이름의 디렉토리를 가져야 한다. 이는 프로젝트에서 사용하는 소스코드가 담겨있다. main()
함수를 포함하는 코드가 존재해야 한다.include
라는 이름의 디렉토리를 가져야 한다. 이는 프로젝트에서 사용하는 소스코드가 포함하는 헤더파일이 담겨있다.library
디렉토리가 존재할 수 있고 이는 프로젝트에서 사용하는 다수의 라이브러리들 중 하나이며 라이브리러, 프로젝트이므로 이 역시 1
, 2
, 3
의 양식을 가진다. 따라서 하나의 makefile
을 통해 프로젝트를 포함한 모든 라이브러리 또한 같은 방식으로 컴파일이 수행된다.
적절히 프로젝트를 구성했다면 이하의 명령을 입력하여 빌드를 수행할 수 있다:
# 이하의 두 줄의 명령어는 `make all` 혹은 `make` 와 동일하다.
make library
make compile
# 이는 avr-objcopy 를 통한 `hex` 변환 수행 명령이다.
make hex
# 완성된 `hex` 파일을 보드로 업로드한다.
make upload
현재는 Arduino UNO
에서 사용하기 위한 설정이 기본으로 잡혀 있다. 그러나 서로 다른 보드와 MPU
를 사용하는 경우에는 이러한 기본 설정을 변경해야 하는데 makefile
을 수정하지 않고 build.conf
라는 이름의 파일을 만들어 수정하는 것 또한 가능하다.
################################################################################
# Build configuration
################################################################################
# This settings can be overrided by `build.config` file
# and also exported to other sub-makefile.
#########################################
# Build settings
#########################################
MACHINE := atmega328p
PROGRAMMER := arduino
CONFIG := /etc/avrdude.conf
PORT := /dev/ttyACM0
UPLOAD_BAUD := 115200
SERIAL_BAUD := 9600
CPU_FREQ := 16000000UL
ARCH := elf32-avr
#########################################
# Compiler settings
#########################################
CC := avr-gcc
CXX := avr-g++
CFLAGS := -std=c99 -mmcu=$(MACHINE) \
-MMD -O2 -ffunction-sections \
-DF_CPU=$(CPU_FREQ) \
-DBAUD=$(SERIAL_BAUD)
CXXFLAGS :=
LDFLAGS := -Wl,--gc-sections
지금은 makefile
과 동일한 내용이 기록되어 있으나 필요에 따라 설정 파일의 값을 수정하는 것만으로 전체 빌드 과정에서 사용되는 변수의 값이 변경되는 효과를 볼 수 있다.
최종 변경 사항은 다음을 통해 확인이 가능하다:
https://www.mythos-git.com/Cruzer-S/makefile