Makefile을 사용하여 펌웨어를 빌드하는 방법
Source: Dev.to
위에 제공된 소스 링크 외에 번역할 텍스트가 보이지 않습니다. 번역을 원하는 본문 내용을 알려주시면 한국어로 번역해 드리겠습니다.
프로젝트 레이아웃
$ tree
.
├── bin/
├── drivers/
│ ├── CMSIS/
│ ├── STM32F446RETX_FLASH.ld
│ └── STM32F4xx_HAL_Driver/
├── lib/
├── Makefile
└── src/
├── it.c
├── main.c
├── main.h
└── peripheral.c
필수 라이브러리 및 도구
- HAL library – 주변 장치 드라이버.
- CMSIS library – Cortex‑M 코어 지원.
- Linker script – MCU의 메모리 레이아웃.
- Startup / system code – 어셈블리 리셋 핸들러 및 시스템 초기화.
- arm‑none‑eabi‑* 툴체인 (gcc, objcopy, size, …).
필요한 모든 라이브러리와 스크립트는 drivers 디렉터리에 위치합니다. 다음 명령으로 가져올 수 있습니다:
git clone --recurse-submodules https://github.com/STMicroelectronics/STM32CubeF4.git
sudo apt install gcc-arm-none-eabi
HAL 라이브러리는 주변 장치를 프로그래밍하는 데 사용되며, CMSIS는 코어 정의를 제공합니다.
Source:
Makefile Walk‑through
아래는 전체 Makefile이며, 섹션별로 설명합니다.
1. Toolchain 정의
CC := arm-none-eabi-gcc
OBJCOPY := arm-none-eabi-objcopy
SIZE := arm-none-eabi-size
RM := rm -f
FILE := file
2. MCU‑특정 정의
DEVICE_FAMILY := STM32F4xx
DEVICE_MODEL := STM32F446xx
DEVICE_VARIANT := STM32F446RETx
3. 컴파일러 및 링커 플래그
CORTEX_FLAGS := -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
COMMON_FLAGS := -g3 -Os -ffunction-sections -fdata-sections -Wall
AS_FLAGS := -x assembler-with-cpp
설명
-mthumbThumb 명령어 집합을 사용합니다.-mcpu=cortex-m4Cortex‑M4 코어를 대상으로 합니다.-mfpu=fpv4-sp-d16와-mfloat-abi=hard하드웨어 부동소수점 연산을 활성화합니다.
4. 포함 경로
MAIN_INC := ./src
CMSIS_INC := ./drivers/CMSIS/Include
CMSIS_DEV_INC := ./drivers/CMSIS/Device/ST/STM32F4xx/Include
HAL_INC := ./drivers/STM32F4xx_HAL_Driver/Inc
CMSIS_DSP_INC := ./drivers/CMSIS/DSP/Include
이 디렉터리들은 소스 코드에서 참조하는 헤더 파일들을 포함합니다.
5. 소스 및 특수 파일
MAIN_SRC := $(wildcard ./src/*.c)
HAL_SRC := $(wildcard ./drivers/STM32F4xx_HAL_Driver/Src/*.c)
SYSTEM_SRC := ./drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c
STARTUP_CODE := ./drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc/startup_stm32f446xx.S
LINKER_SCRIPT := ./drivers/STM32F446RETX_FLASH.ld
6. 약어 및 빌드 정의
DEFINES := -D$(DEVICE_FAMILY) -D$(DEVICE_MODEL) -D$(DEVICE_VARIANT) \
-DUSE_HAL_DRIVER
SOURCES := $(MAIN_SRC) $(HAL_SRC) $(SYSTEM_SRC)
OBJECTS := $(notdir $(patsubst %.c,%.o,$(SOURCES))) startup_stm32f446xx.o
INCLUDES := -I$(MAIN_INC) -I$(CMSIS_DEV_INC) -I$(CMSIS_INC) -I$(HAL_INC) \
-I$(CMSIS_DSP_INC)
CFLAGS := $(CORTEX_FLAGS) $(COMMON_FLAGS) $(DEFINES) $(INCLUDES)
AFLAGS := $(CORTEX_FLAGS) $(AS_FLAGS) $(DEFINES) $(INCLUDES)
LDFLAGS := $(CORTEX_FLAGS) -T $(LINKER_SCRIPT) \
-Wl,--gc-sections,--relax --specs=nano.specs --specs=nosys.specs \
-Wl,--start-group -lc -lm -lnosys -Wl,--end-group
핵심 포인트
SOURCES는 모든.c파일을 모으며,OBJECTS는 이를.o이름으로 변환합니다.CFLAGS,AFLAGS,LDFLAGS는 각각 컴파일, 어셈블, 링크에 사용됩니다.LDFLAGS는 newlib‑nano C 라이브러리(-lc), 수학 라이브러리(-lm), 그리고 nosys 스텁 라이브러리(-lnosys)를 링크합니다. 이는 펌웨어가 베어 메탈(운영체제 없음) 환경에서 실행되기 때문입니다.
7. 출력 파일
FIRMWARE_ELF := firmware.elf
FIRMWARE_BIN := firmware.bin
- ELF – 기계 코드, 디버그 정보, 심볼 테이블 등을 포함하며, 플래싱 및 디버깅에 사용됩니다.
- BIN – 플래시를 직접 프로그래밍하기에 적합한 순수 바이너리 이미지입니다.
8. 빌드 규칙
.PHONY: all clean flash
all: $(FIRMWARE_ELF) $(FIRMWARE_BIN)
$(FIRMWARE_ELF): $(OBJECTS)
$(CC) $(LDFLAGS) -o $@ $^
$(FIRMWARE_BIN): $(FIRMWARE_ELF)
$(OBJCOPY) -O binary $< $@
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
startup_stm32f446xx.o: $(STARTUP_CODE)
$(CC) $(AFLAGS) -c -o $@ $<
clean:
$(RM) *.o $(FIRMWARE_ELF) $(FIRMWARE_BIN)
# Example flash target (adjust the programmer/command as needed)
flash: $(FIRMWARE_BIN)
st-flash write $(FIRMWARE_BIN) 0x08000000
How It Works
- Compilation – 각
.c파일이$(CFLAGS)와 함께 객체 파일로 컴파일됩니다. - Assembly – 시작 어셈블리 파일이
$(AFLAGS)와 함께 어셈블됩니다. - Linking – 모든 객체가
$(LDFLAGS)와 제공된 링커 스크립트를 사용해 링크되어firmware.elf를 생성합니다. - Binary conversion –
objcopy가 ELF를 원시 바이너리(firmware.bin)로 변환합니다. - Flashing – 선택적인
flash타깃은 MCU를 프로그래밍하는 방법을 보여줍니다(여기서는st-flash사용; 필요에 따라 프로그래머를 교체하세요).
결론
- 컴파일하고, 링크하며, ELF와 원시 바이너리 이미지를 모두 생성합니다.
- 빌드 프로세스를 완전히 투명하게 유지합니다—숨겨진 IDE 매직이 없습니다.
- 파일 목록, 컴파일러 플래그, 또는 대상 MCU를 쉽게 조정할 수 있습니다.
각 단계를 이해하면 빌드 프로세스를 완전히 제어할 수 있게 되며, 이는 저수준 문제를 디버깅하거나 전체 IDE를 사용할 수 없는 환경에서 작업할 때 매우 귀중합니다. 즐거운 해킹 되세요!
머신 코드
두 파일의 크기를 보면 ELF 실행 파일이 훨씬 크다는 것을 알 수 있습니다!
빌드 레시피
.PHONY: all
all:
@echo "-------------------------------------"
@echo "----- Building the source files -----"
@echo "-------------------------------------"
@$(CC) $(AFLAGS) -c $(STARTUP_CODE)
@$(CC) $(CFLAGS) -c $(SOURCES)
@echo "\n------------------------------------"
@echo "----- Linking the object files -----"
@echo "------------------------------------"
@$(CC) $(LDFLAGS) $(OBJECTS) -o $(FIRMWARE_ELF)
@$(OBJCOPY) -O binary $(FIRMWARE_ELF) $(FIRMWARE_BIN)
@$(RM) $(OBJECTS)
@echo "\n-------------------------------------"
@echo "----- The firmware memory usage -----"
@echo "-------------------------------------"
@$(SIZE) $(FIRMWARE_ELF)
@echo "\n-------------------------------------"
@echo "----- The firmware binary format ----"
@echo "-------------------------------------"
@$(FILE) $(FIRMWARE_ELF)
펌웨어 빌드
$ make
그게 전부입니다 🥳🥳🥳. firmware.elf와 firmware.bin 두 파일을 모두 빌드했습니다.
펌웨어 플래시
$ openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
-c "program firmware.elf verify reset exit"
이제 빌드된 펌웨어를 마이크로컨트롤러에 플래시할 준비가 되었습니다 (ST‑Link 연결 또는 유사한 도구를 사용).