这篇文章为本人结合网上教程,自行探索,如有错误敬请谅解
在Linux STM32初体验一文中,我们已经成功准备好了GCC的编译环境、StLink的烧录环境,已经可以来搭建一个工程了。
而为了实现自动化构建项目,我们也应该学会使用 Make 和 Makefile ,理解它的语法和用法,并亲手进行实践。囿于篇幅,推荐通过陈皓的跟我一起写Makefile来学习Makefile。
目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| ├── Algorithm(算法源码) │ ├── PID │ │ ├── pid.c │ │ └── pid.h │ ├── ..... │ └── IMU │ ├── imu.c │ └── imu.h ├── Drivers(底层驱动源码) │ ├── ADC │ ├── ..... │ └── WDG ├── Libraries(官方开发库) │ ├── CMSIS │ │ ├── startup │ │ │ └── gcc │ │ │ └── startup_stm32f10x_hd │ │ ├── cmsis_gcc.h │ │ ├── core_cm3.c │ │ ├── ..... │ │ └── mpu_ramv8.h │ ├── CORE │ │ ├── stm32f10x_conf.h │ │ ├── stm32f10x_it.c │ │ ├── stm32f10x_it.h │ │ ├── stm32f10x.h │ │ ├── system_stm32f10x.c │ │ └── system_stm32f10x.h │ └── STM32F10x_FWLib │ ├── inc │ │ └── ... │ └── src │ └── ... ├── Modules(外设模块源码) │ ├── HCSR04 │ ├── ..... │ └── SG90 ├── Project(构建成功文件) │ ├── Build │ │ ├── template.bin │ │ └── template.hex │ └── stm32f1xx_flash.ld ├── src(工程核心源码) │ └── main.c └── Makefile
|
- Algorithm
该目录下存放有嵌入式算法的源码,其中包含IMU融合算法、PID控制算法等等顶层代码。 - Drivers
该目录下存放的是硬件驱动层源码,为STM32的底层驱动的源码。 - Libraries
该目录下存放的是STM32官方开发库的相关内容,其中CMSIS子目录下包含有STM32内核源码、硬件寄存器和中断定义源码以及启动汇编源码等,CMSIS子目录下包含有STM32工程头文件引用、中断函数定义和中断函数实现等源码,FWLib子目录下包含有STM32提供的官方固件库源码。 - Modules
该目录下存放的是所有外设模块层中的源码。 - Project
该目录下主要存放根据Makefile中所定义的规则,工程在被成功编译之后还会在本目录下生成.hex和.bin等可供烧写的文件。以及Obj文件夹存放着编译生成的.o中间文件。 - src
该目录下存放的是工程的main.c 工程主源码文件。
Makefile详解
根据工程目录结构,我们可以编写工程的Makefile文件。
Makefile 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
| PROJECT := template
MCU := cortex-m3
BUILD_DIR = Project/Build OBJ_DIR = Project/Build/Obj
DDEFS += -DSTM32F10X_HD DDEFS += -DUSE_STDPERIPH_DRIVER
DIR_DRIVERS += ./Drivers/ADC/ DIR_DRIVERS += ./Drivers/EXTI/ DIR_DRIVERS += ./Drivers/DAC/ DIR_DRIVERS += ./Drivers/ITEMP/ DIR_DRIVERS += ./Drivers/KEY/ DIR_DRIVERS += ./Drivers/LED/ DIR_DRIVERS += ./Drivers/PWM/ DIR_DRIVERS += ./Drivers/RTC/ DIR_DRIVERS += ./Drivers/TIMER/ DIR_DRIVERS += ./Drivers/WDG/ DIR_DRIVERS += ./Drivers/SYSTEM/delay/ DIR_DRIVERS += ./Drivers/SYSTEM/sys/ DIR_DRIVERS += ./Drivers/SYSTEM/usart/
DIR_MODULES += ./Modules/HCSR04/ DIR_MODULES += ./Modules/SG90/ DIR_MODULES += ./Modules/ENCODER/
DIR_ALGORITHM +=
LINK_SCRIPT := ./Project/stm32f1xx_flash.ld
SRC_ASM := ./Libraries/CMSIS/startup/gcc/startup_stm32f10x_hd.s
CC_PREFIX := arm-none-eabi- st-link := st-flash
SRC_C += $(wildcard ./Libraries/CMSIS/*.c) SRC_C += $(wildcard ./Libraries/CORE/*.c) SRC_C += $(wildcard ./Libraries/STM32F10x_FWLib/src/*.c) SRC_C += $(wildcard $(addsuffix *.c, $(DIR_DRIVERS))) SRC_C += $(wildcard $(addsuffix *.c, $(DIR_MODULES))) SRC_C += $(wildcard $(addsuffix *.c, $(DIR_ALGORITHM))) SRC_C += $(wildcard ./src/*.c)
INCDIR += -I./Libraries/CMSIS/ \ -I./Libraries/CORE/ \ -I./Libraries/STM32F10x_FWLib/inc/ \ $(addprefix -I, $(DIR_DRIVERS)) \ $(addprefix -I, $(DIR_MODULES)) \ $(addprefix -I, $(DIR_ALGORITHM)) \ -I./src/ \
CC := $(CC_PREFIX)gcc CXX := $(CC_PREFIX)g++ CP := $(CC_PREFIX)objcopy GDB := $(CC_PREFIX)gdb SIZE := $(CC_PREFIX)size AS := $(CC) -x assembler-with-cpp HEX := $(CP) -O ihex BIN := $(CP) -O binary -S
OPT += -Os OPT += -fsingle-precision-constant OPT += -fno-common OPT += -ffunction-sections OPT += -fdata-sections
DEFS := $(DDEFS) -DRUN_FROM_FLASH=1
FLAGS_MCU := -mcpu=$(MCU) FLAGS_AS := $(FLAGS_MCU) $(OPT) -g -gdwarf-2 -mthumb FLAGS_C := -std=c11 -xc $(FLAGS_MCU) $(OPT) -mthumb -g -Wl,-u_printf_float \ -Wall -fverbose-asm -fomit-frame-pointer $(DEFS) FLAGS_CXX := -std=c++11 -xc++ $(FLAGS_MCU) $(OPT) \ -gdwarf-2 -mthumb -Wl,-u_printf_float -ffunction-sections -fdata-sections \ -fomit-frame-pointer -Wall -g -fverbose-asm -fno-exceptions \ -fno-rtti -fno-threadsafe-statics -fvisibility=hidden $(DEFS)
FLAGS_LD := $(FLAGS_MCU) $(OPT) -lm -g -gdwarf-2 -mthumb \ --specs=nosys.specs --specs=nano.specs -Wl,-u_printf_float -Wl,--print-memory-usage \ -Wl,--gc-sections -T$(LINK_SCRIPT) \ -Wl,-Map=$(OBJ_DIR)/$(PROJECT).map,--cref,--no-warn-mismatch \
OBJS = $(addprefix $(OBJ_DIR)/,$(notdir $(SRC_C:.c=.o))) $(addprefix $(OBJ_DIR)/,$(notdir $(SRC_ASM:.s=.o)))
vpath %.c $(sort $(dir $(SRC_C))) vpath %.s $(sort $(dir $(SRC_ASM)))
TYPE_BURN := stlink_swd_flash TYPE_DEBUG := openocd_stlink_debug TYPE_ERASE := stlink_swd_erase
.PHONY: all flash debug erase clean distclean
all: $(OBJS) $(BUILD_DIR)/$(PROJECT).elf $(BUILD_DIR)/$(PROJECT).hex $(BUILD_DIR)/$(PROJECT).bin @$(SIZE) $(BUILD_DIR)/$(PROJECT).elf
$(OBJ_DIR)/%.d: %.c Makefile │$(OBJ_DIR) @set -e; rm -f $@; \ $(CC) -M $(FLAGS_C) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@;
$(OBJ_DIR)/%.o: %.c Makefile │$(OBJ_DIR) @$(CC) -c $(FLAGS_C) $(INCDIR) $< -o $@ @echo "Compile $<"
$(OBJ_DIR)/%.o: %.s Makefile │$(OBJ_DIR) @$(AS) -c $(FLAGS_AS) $< -o $@
$(BUILD_DIR)/$(PROJECT).elf: $(OBJS) Makefile @$(CC) $(OBJS) $(FLAGS_LD) -o $@
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf │$(BUILD_DIR) @$(HEX) $< $@
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf │$(BUILD_DIR) @$(BIN) $< $@
$(OBJ_DIR) $(BUILD_DIR): @-mkdir $(BUILD_DIR) @-mkdir $(OBJ_DIR)
flash: $(TYPE_BURN)
erase: $(TYPE_ERASE)
stlink_swd_flash: $(BUILD_DIR)/$(PROJECT).hex $(st-link) --format ihex write $(BUILD_DIR)/$(PROJECT).hex
openocd_stlink_debug: $(BUILD_DIR)/$(PROJECT).elf openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c init \ -c halt -c "flash write_image erase $(BUILD_DIR)/$(PROJECT).elf" -c reset & \ $(GDB) --eval-command="target extended-remote localhost:3333" $(BUILD_DIR)/$(PROJECT).elf
stlink_swd_erase: $(st-link) --reset erase
clean: @echo "删除中间文件...." @-rm -fR $(OBJ_DIR)
distclean: @echo "删除全部文件...." @-rm -fR $(BUILD_DIR) @-rm -fR $(OBJ_DIR)
|
详解
工程配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| PROJECT := template
MCU := cortex-m3
BUILD_DIR = Project/Build OBJ_DIR = Project/Build/Obj
DDEFS += -DSTM32F10X_HD DDEFS += -DUSE_STDPERIPH_DRIVER
LINK_SCRIPT := ./Project/stm32f1xx_flash.ld
SRC_ASM := ./Libraries/CMSIS/startup/gcc/startup_stm32f10x_hd.s
CC_PREFIX := arm-none-eabi- st-link := st-flash
|
根据注释进行配置即可
目录引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| DIR_DRIVERS += ./Drivers/ADC/ .... DIR_DRIVERS += ./Drivers/SYSTEM/usart/
DIR_MODULES += ./Modules/HCSR04/
DIR_ALGORITHM += INCDIR += -I./Libraries/CMSIS/ \ -I./Libraries/CORE/ \ -I./Libraries/STM32F10x_FWLib/inc/ \ $(addprefix -I, $(DIR_DRIVERS)) \ $(addprefix -I, $(DIR_MODULES)) \ $(addprefix -I, $(DIR_ALGORITHM)) \ -I./src/ \
|
DIR_DRIVERS
、DIR_MODULES
和 DIR_ALGORITHM
三个变量是工程中需要使用的底层驱动、外设及算法。
INCDIR
的作用是为了获取工程目录中的.h头文件.
源文件搜索
1 2 3 4 5 6 7 8 9 10 11 12 13
| SRC_C += $(wildcard ./Libraries/CMSIS/*.c) SRC_C += $(wildcard ./Libraries/CORE/*.c) SRC_C += $(wildcard ./Libraries/STM32F10x_FWLib/src/*.c) SRC_C += $(wildcard $(addsuffix *.c, $(DIR_DRIVERS))) SRC_C += $(wildcard $(addsuffix *.c, $(DIR_MODULES))) SRC_C += $(wildcard $(addsuffix *.c, $(DIR_ALGORITHM))) SRC_C += $(wildcard ./src/*.c)
OBJS = $(addprefix $(OBJ_DIR)/,$(notdir $(SRC_C:.c=.o))) $(addprefix $(OBJ_DIR)/,$(notdir $(SRC_ASM:.s=.o)))
vpath %.c $(sort $(dir $(SRC_C))) vpath %.s $(sort $(dir $(SRC_ASM)))
|
$(wildcard $(addsuffix *.c, $(DIR_DRIVERS)))
函数的含义是首先在DIR_DRIVERS
变量的后边添加.c后缀,然后再调用wildcard
函数获取其中的所有.c源文件。
因此,SRC_C
和 SCR_ASM
这两个变量用于分别存储C源码和汇编文件。
$(notdir $(C_SOURCES:.c=.o))
函数是将源文件集中所有c文件的后缀替换成o文件,并去除所有路径信息.
addprefix
函数则将将无路径的o文件集(字符串)添加制定路径前缀信息,生成最终的目标obj文件的路径和名称集合。
因此,OBJS
变量用于存储所有通过.c和.s源文件生成的中间目标文件(.o)。
在接着,通过$(sort $(dir $(C_SOURCES)))
函数指定C文件和汇编文件的搜索路径.
编译器
1 2 3 4 5 6 7 8 9 10
| CC_PREFIX := arm-none-eabi-
CC := $(CC_PREFIX)gcc CXX := $(CC_PREFIX)g++ CP := $(CC_PREFIX)objcopy GDB := $(CC_PREFIX)gdb SIZE := $(CC_PREFIX)size AS := $(CC) -x assembler-with-cpp HEX := $(CP) -O ihex BIN := $(CP) -O binary -S
|
这些变量为ARM-GCC编译器相关可执行程序。
编译选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| MCU := cortex-m3
DDEFS += -DSTM32F10X_HD DDEFS += -DUSE_STDPERIPH_DRIVER
OPT += -Os
OPT += -fsingle-precision-constant
OPT += -fno-common
OPT += -ffunction-sections OPT += -fdata-sections
DEFS := $(DDEFS) -DRUN_FROM_FLASH=1
|
DDEFS
和 DEFS
变量是ARM-GCC编译器的预处理宏定义,用于将指定的功能编译到可执行程序中。
MCU
变量表示当前工程所用芯片的架构。
OPT(Optimization)
变量用于表示 编译优化 方面的选项。
编译标签
1 2 3 4 5 6 7 8 9 10 11 12 13
| FLAGS_MCU := -mcpu=$(MCU) FLAGS_AS := $(FLAGS_MCU) $(OPT) -g -gdwarf-2 -mthumb FLAGS_C := -std=c11 -xc $(FLAGS_MCU) $(OPT) -mthumb -g -Wl,-u_printf_float \ -Wall -fverbose-asm $(DEFS) FLAGS_CXX := -std=c++11 -xc++ $(FLAGS_MCU) $(OPT) \ -gdwarf-2 -mthumb -Wl,-u_printf_float -ffunction-sections -fdata-sections \ -fomit-frame-pointer -Wall -g -fverbose-asm -fno-exceptions \ -fno-rtti -fno-threadsafe-statics -fvisibility=hidden $(DEFS)
FLAGS_LD := $(FLAGS_MCU) $(OPT) -lm -g -gdwarf-2 -mthumb \ --specs=nosys.specs --specs=nano.specs -Wl,-u_printf_float -Wl,--print-memory-usage \ -Wl,--gc-sections -T$(LINK_SCRIPT) \ -Wl,-Map=$(OBJ_DIR)/$(PROJECT).map,--cref,--no-warn-mismatch \
|
这些变量都是编译标签,具体含义可参考GCC文档。
列出一些常用的标签含义:
- -fomit-frame-pointer
减少了栈帧的切换和栈地址的保存,可提高程序性能 - -fverbose-asm
在生成的汇编代码中加入额外的注释信息来使汇编代码更具可读性 - -specs=nano.specs
使用精简版C库 , - -T$(LINK_SCRIPT)
依赖的可执行文件链接脚本,即链接脚本 - -Wl,-Map=(OBJDIR)/(PROJECT).map,–cref
生成map文件 - -Wl,–gc-sections
链接使用的分段方式,需要配合C文件/汇编生成obj的时候同样选型分段方式。 - -Wl,-u_printf_float -Wl,–print-memory-usage
串口打印时打印出浮点数
总结
至此,一个完整的Makefile函数便完成了。
你可以在终端输入make
来编译生成hex文件,
输入make flash
来烧写固件;
输入make distclean
来清除全部文件…
尾声
总是想记录些什么,却又不知道要怎么写。在实验室的这些天,我不知道要做些什么,要学些什么,反而将大把时间放在了研究GCC编译STM32之上。
我并不是很确认,这是否有什么意义。但是在这个过程中,我已然初步掌握了其编译原理,学会了调试编译参数来生成不同固件。
就像上海交通大学生存手册中所说的那样“国内绝大部分大学的本科教学,不是濒临崩溃,而是早已崩溃”,这种闭源编译器垄断的学习模式是否正确,我无法评判,也没能力评判。
因stm32的gcc编译教程实在是少,便写两篇文章来记录下,也希望能让看到这些文章的人少走些歪路。