使用GCC編譯STM32工程

一、STM32在linux環境編譯

STM32等arm芯片的工程編譯環境比較多,類似於keil、IAR等成熟的MDK集成環境被大部分開發者所使用的,但是如果要在linux在開發,或者使用的芯片不是stm32芯片,需要自己搭建編譯環境。
本文將基於STM32芯片使用arm-none-eabi-gcc編譯器,並使用工具makefile完成。

二、編譯文件介紹

STM32工程建立,可以是在keil中使用的工程基礎上修改。在gcc編譯器下與在keil中的區別主要是3個文件:
1、Makefile文件,gcc獨有文件;
2、startup_stm32f407xx.s,有區別文件,主要是gcc和keil編譯彙編格式不同;
3、STM32F407IGHX_FLASH.ld,gcc獨有文件,keil環境自帶無需工程配置;

三、解析Makefile文件

本文將對STM32CUBMX生成的Makefile文件進行詳細的解釋,如果要直接使用下面的Makefile文件驗證,要將所有備註//刪除。

##########################################################################################################################
# File automatically-generated by tool: [projectgenerator] version: [3.0.0] date: [Wed Jun 10 23:45:01 CST 2020]
##########################################################################################################################

# ------------------------------------------------
# Generic Makefile (based on gcc)
#
# ChangeLog :
#	2017-02-10 - Several enhancements + project update mode
#   2015-07-22 - first version
# ------------------------------------------------

######################################
# target
######################################
/* 編譯生成目標燒寫軟件的名稱 */
TARGET = GccProject

######################################
# building variables
######################################
# debug build?
/* 編譯選項:是否debug模式,如果DEBUG=1,則可以後續使用調試軟件gdb等工具進行在線調試
如果DEBUG=0,則不能支持在線調試,
且DEBUG=1,生成的文件比DEBUG=0大,因爲裏面包含了調試信息。 */
DEBUG = 1

# optimization
/* 編譯選項:優化等級
-O0:無任何優化,
-O1:1級優化,
-O2: 2級優化,
-Os: 2.5級優化,
-O3: 最高級優化。 */
OPT = -O0


#######################################
# paths
#######################################
# Build path
/* 編譯路徑: 生成的編譯文件保存在build文件夾中,
這樣做的好處是工程框架比較清晰,且清除編譯文件比較簡單。*/
BUILD_DIR = build

######################################
# source
######################################
# C sources
/* 工程所有需要編譯的C文件: 指定需要編譯的C文件名稱相對路徑。*/
C_SOURCES =  \
Src/main.c \
Src/stm32f4xx_it.c \
Src/stm32f4xx_hal_msp.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c \
Src/system_stm32f4xx.c  

# ASM sources
/* 工程所有需要編譯的彙編文件: 指定需要編譯的彙編文件名稱相對路徑。*/
ASM_SOURCES =  \
startup_stm32f407xx.s


#######################################
# binaries
#######################################
/* 工程使用編譯的類型: arm-none-eabi-是基於arm芯片開發的編譯器,
none表示無操作系統,eabi表示交叉編譯器,即在linux上編譯嵌入式arm芯片的代碼
生成可燒寫文件。*/
PREFIX = arm-none-eabi-
# The gcc compiler bin path can be either defined in make command via GCC_PATH variable (> make GCC_PATH=xxx)
# either it can be added to the PATH environment variable.
/* 編譯器路徑宏:表示arm-none-eabi-gcc在linux中調用是否需要帶路徑,
一般情況安裝好arm-none-eabi-gcc後,系統將安裝的可執行程序路徑放在了系統的環境變量中,
無需要路徑即可執行,所以GCC_PATH不用定義。*/
ifdef GCC_PATH
CC = $(GCC_PATH)/$(PREFIX)gcc
AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
CP = $(GCC_PATH)/$(PREFIX)objcopy
SZ = $(GCC_PATH)/$(PREFIX)size
else
/* 編譯C語言文件編譯器選型 :arm-none-eabi-gcc */
CC = $(PREFIX)gcc
/* 編譯彙編語言文件編譯器選型 :arm-none-eabi-gcc -x assembler-with-cpp 
-x:表示指定編譯語言的選型,包含c cpp ObJECT-C assembler go assemble-with-cpp等不用語言 */
AS = $(PREFIX)gcc -x assembler-with-cpp
/* 編譯器將可執行文件elf(linux中可執行文件常見格式)轉換成其他格式的工具:arm-none-eabi-objcopy */
CP = $(PREFIX)objcopy
/* 編譯器計算可執行文件中存儲分配的工具:arm-none-eabi-size */
SZ = $(PREFIX)size
endif
/* elf文件轉換成HEX文件:arm-none-eabi-objcopy -O ihex */
HEX = $(CP) -O ihex
/* elf文件轉換成bin文件:arm-none-eabi-objcopy -O binary -S */
BIN = $(CP) -O binary -S
 
#######################################
# CFLAGS
#######################################
# cpu
/* 編譯選型:CPU類型,只STM32芯片的內核是cortex-n4,指定該內核對應的寄存器庫 */
CPU = -mcpu=cortex-m4

# fpu
/* 編譯選型:FPU浮點計算器,STM32支持浮點運算 */
FPU = -mfpu=fpv4-sp-d16

# float-abi
/* 編譯選型:浮點計算類型,設定硬件浮點運算,還可選型純軟件浮點計算,或者結合形式 */
FLOAT-ABI = -mfloat-abi=hard

# mcu
/* 編譯MCU總選項: M4內核,精簡指令集mthumb , 支持浮點運算 ,浮點計算採用硬件浮點計算器 */
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)

# macros for gcc
# AS defines
/* 彙編編譯宏定義:該Makefile宏定義將在彙編代碼中有效 */
AS_DEFS = 

# C defines
/* C文件編譯宏定義:該Makefile宏定義將在C代碼中有效,\符號表示Makefile同1條指令換行 */
C_DEFS =  \
-DUSE_HAL_DRIVER \
-DSTM32F407xx


# AS includes
/* 彙編頭文件路徑:編譯過程中文件內的頭文件搜索路徑 */
AS_INCLUDES = 

# C includes
/* C文件的頭文件路徑:編譯過程中文件內的頭文件搜索路徑 */
C_INCLUDES =  \
-IInc \
-IDrivers/STM32F4xx_HAL_Driver/Inc \
-IDrivers/STM32F4xx_HAL_Driver/Inc/Legacy \
-IDrivers/CMSIS/Device/ST/STM32F4xx/Include \
-IDrivers/CMSIS/Include \
-IDrivers/CMSIS/Include


# compile gcc flags
/* 彙編編譯選型:s彙編文件編譯成Obj文件需要的設置選項 */
ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
/* C編譯選型:C文件編譯成Obj文件需要的設置選項 */
CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections

/* Debug模式下的C編譯選型:僅僅在開啓DEBUG模式下有效 */
ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif


# Generate dependency information
/* C文件自動依賴關係 :-MMD -MP -MF"$(@:%.o=%.d)" 
自動生成.d文件,裏面保存了改源文件C代碼中包含的非標準庫的頭文件路徑和名稱,
生成.d文件的目的是產生C文件生成obj的依賴文件,
當關聯的頭文件發生變化時,觸發make重新生成obj文件。
-MMD等同於-MM -MF,-MM表示依賴的頭文件(不包括標準頭文件夾,-M則是所有頭文件),
-MF生成依賴文件。  */
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"


#######################################
# LDFLAGS
#######################################
# link script
/* 可執行文件鏈接腳本:  STM32F407IGHx_FLASH.ld 
文件中詳細給出了芯片的RAM和ROM片區分類區間與大小,
代碼、全局變量、常數、堆棧等的分配區間。*/
LDSCRIPT = STM32F407IGHx_FLASH.ld

# libraries
/* 編譯選型: 依賴的標準庫*/
LIBS = -lc -lm -lnosys 
/* 編譯選型: 依賴的指定路徑庫,.a庫文件(window中的lib文件需要轉換成.a文件才能識別)*/
LIBDIR = 
/* 鏈接工具的總選項: 
MCU 芯片類型,
-specs=nano.specs 精簡版C庫 ,
-T$(LDSCRIPT)依賴的可執行文件鏈接腳本,
$(LIBDIR) 標準庫文件 , $(LIBS) 指定庫文件 ,
-Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref 生成map文件 ,
-Wl,--gc-sections 鏈接使用的分段方式,需要配合C文件/彙編生成obj的時候同樣選型分段方式,好處是鏈接的時候源文件中的未使用變量和未調用函數將不會被鏈接到elf文件中,最終可執行文件elf會很精簡。
*/
LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections

# default action: build all
/* Makefile總目標: 這是僞目標,在第一依賴關係位置,輸入make指令時必定指令該目標 
可執行文件elf , hex 和 bin 是arm的常用燒寫文件 。*/
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin


#######################################
# build the application
#######################################
# list of objects
/* 將所有C文件獲取OBJ文件: 將待編譯的所有C文件生成OBJ文件集
$(notdir $(C_SOURCES:.c=.o)):將源文件集中所有c文件的後綴替換成o文件,並去除所有路徑信息,
addprefix函數 :將無路徑的o文件集(字符串)添加制定路徑前綴信息,生成最終的目標obj文件的路徑和名稱集合。*/
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))

/* 指定C文件的搜索路徑: $(sort $(dir $(C_SOURCES))) 
$(dir $(C_SOURCES)):所有源文件只保留文件路徑,
sort:對所有路徑排序 ,‘d g a’ -> 'a d g' */
vpath %.c $(sort $(dir $(C_SOURCES)))

# list of ASM program objects
/* 將所有彙編文件獲取OBJ文件: 將待編譯的所有彙編文件生成OBJ文件集
$(notdir $(ASM_SOURCES:.s=.o)) :將源文件集中所有編譯文件後綴替換成o文件,並去除所有路徑信息,
addprefix函數 :將無路徑的o文件集(字符串)添加制定路徑前綴信息,生成最終的目標obj文件的路徑和名稱集合。*/
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
/* 指定彙編文件的搜索路徑: $(sort $(dir $(ASM_SOURCES)))
$(dir $(C_SOURCES)):所有彙編文件只保留文件路徑,
sort:對所有路徑排序 ,‘d g a’ -> 'a d g' */
vpath %.s $(sort $(dir $(ASM_SOURCES)))

/* 通配符指定所有c文件編譯成OBJ文件: 
$(BUILD_DIR)/%.o: 生成OBJ文件的路徑固定不變,在BUILD_DIR文件夾,
%.c:依賴源文件C文件,地址未指定,Makefile將在本地目錄和vpath %c目錄下搜索源文件,
Makefile :Makefile文件自己也是生成obj文件的依賴文件,Makefile文件變化時會重新編譯,
| $(BUILD_DIR): 豎線座標的依賴文件是正常依賴文件,豎線右邊的依賴文件是命令提前的依賴文件,即BUILD_DIR會自動執行,編譯生成OBJ前生成build文件夾。 
-Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)):生成臨時中間文件
-c:僅編譯不鏈接 $<:第一個依賴文件即C文件   $@ 目標文件 */
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 
	$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@

/* 通配符指定所有彙編文件編譯成OBJ文件: 
$(BUILD_DIR)/%.o: 生成OBJ文件的路徑固定不變,在BUILD_DIR文件夾,
%.c:依賴源文件C文件,地址未指定,Makefile將在本地目錄和vpath %c目錄下搜索源文件,
Makefile :Makefile文件自己也是生成obj文件的依賴文件,Makefile文件變化時會重新編譯,
| $(BUILD_DIR): 豎線座標的依賴文件是正常依賴文件,豎線右邊的依賴文件是命令提前的依賴文件,
-c:僅編譯不鏈接 $<:第一個依賴文件即C文件   $@ 目標文件 */
$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
	$(AS) -c $(CFLAGS) $< -o $@

/* 生成可執行文件ELF文件:依賴於所有OBJ文件 
$(CC) $(OBJECTS) $(LDFLAGS) -o $@:生成elf文件
$(SZ) $@:計算和打印elf文件的存儲分配信息 。*/
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
	$(CC) $(OBJECTS) $(LDFLAGS) -o $@
	$(SZ) $@
	
/* 生成HEX文件:依賴於elf文件和build文件夾
build文件:優先於目標,直接執行*/
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(HEX) $< $@

/* 生成BIN文件:依賴於elf文件和build文件夾
build文件:優先於目標,直接執行*/	
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(BIN) $< $@	

/* 生成build文件夾:僞指令
通過上面的優先依賴關係生成。*/		
$(BUILD_DIR):
	mkdir $@		

#######################################
# clean up
#######################################
/* 清除編譯結果:將build文件中所有文件和子文件夾刪除。*/
clean:
	-rm -fR $(BUILD_DIR)
  
#######################################
# dependencies
#######################################
/* 添加所有.d依賴文件:include 添加其他makefile文件
-include 表示如果無d文件不報錯繼續執行makefile命令,
wildcard函數:表示在函數中讓通配符生效,即在include 選型中讓‘*’生效,搜索到build文件中所有d文件。
最終生成所有c文件的依賴關係,
make工具將依賴關係與上面定義的 %.o:%.c makefile 顯性目標關係合併,
make工具將合併後的執行。*/
-include $(wildcard $(BUILD_DIR)/*.d)

# *** EOF ***
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章