如何使用 Makefile 构建固件?
发布: (2025年12月21日 GMT+8 20:46)
5 min read
原文: Dev.to
Source: Dev.to
请提供您希望翻译的完整文本(除代码块和 URL 之外),我将按照要求将其翻译成简体中文并保留原有的格式。
项目布局
$ tree
.
├── bin/
├── drivers/
│ ├── CMSIS/
│ ├── STM32F446RETX_FLASH.ld
│ └── STM32F4xx_HAL_Driver/
├── lib/
├── Makefile
└── src/
├── it.c
├── main.c
├── main.h
└── peripheral.c
必需的库和工具
- HAL库 – 外设驱动。
- CMSIS库 – Cortex‑M 核心支持。
- 链接脚本 – MCU 的内存布局。
- 启动 / 系统代码 – 汇编复位处理程序和系统初始化。
- 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 提供核心定义。
Makefile 逐步讲解
以下是完整的 Makefile,逐段进行说明。
1. 工具链定义
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
说明
-mthumb使用 Thumb 指令集。-mcpu=cortex-m4目标为 Cortex‑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)
# 示例 flash 目标(根据需要调整编程器/命令)
flash: $(FIRMWARE_BIN)
st-flash write $(FIRMWARE_BIN) 0x08000000
工作原理
- 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 连接或类似工具)。