使用Makefile在Windows环境下编译STM32F103C8T6标准库工程,脱离Keil的指南
2025-10-17 来源:cnblogs
引言
前段时间学习了Makefile,了解到其批量编译的功能。所以去尝试复刻到keil编译文件。这里实现了window下实现makefile对标准库来编译。之后也会在Linux环境下继续探索。
这里的核心软件还是gcc-arm-none-eabi的arm环境,与MinGW64提供的make软件。
核心修改点为makefile文件的修改与标准库函数里面的两个函数的修改。个人认为这种方法应该也可以用到别的芯片上面,不过目前没有去验证。我会提供此次使用的全部工程文件。每次上传文件到csdn,别人下载都会要收费或者收积分,我真的:)-
一、软件与环境准备
我们需要准备三个环境包与一个软件(提供一个Make file模板)
MInGW64 —- 提供make软件(全名:mingw32-make.exe)
openocd —- 提供烧录的环境(支持stlink,jlink等多种方式)
gcc-arm-none-eabi —- 为ARM的板子如STM32编译必须的编译器
STM32CubeMx —- 提供一个Makefile模板和一点必须的文件
下载地址:
MInGW64
mingw-w64 Releases · niXman/mingw-builds-binaries · GitHub

openocd
https://sourceforge.net/projects/openocd/ Releases · openocd-org/openocd · GitHub

gcc-arm-none-eabi
Downloads | GNU Arm Embedded Toolchain Downloads – Arm Developer

STM32CubeMx
https://www.st.com/en/development-tools/stm32cubemx.html
为避免文章拖沓,这里默认已经安装或者下载好这些必备的软件或者压缩包。
然后需要分别将MINGW64、openocd、gcc-arm-none-eabi 的bin目录配置到系统环境中

MInGW64
openocd
gcc-arm-none-eabi
STM32CubeMx
安装完STM32CubeMX后面,我们需要它去下载库文件。这个库文件一般会保存在以下路径内

让其去生成一个工程,不过工程选择Makefile工程。下面简单配置一下:





到达这一步,cubemx的使命已经完成。
二、修改Makefiel文件
1.我们把上面的这三个文件单独拿出来

2.我们把标准库从零搭建一个STM32的工程模板_stm32固件库下载及安装-CSDN博客,或者你的或者其它人的,只要是一个标准模板。全部拿过来就可以,这里我把我的拿过来


完成图:

那么开始吧,修改makefile文件
四个需要修改的makefile文件地方
makefile文件总共有四个地方需要修改
step1
第一处

下面替换上面
C_SOURCES = $(wildcard ./CMSIS/*.c) $(wildcard ./Library/src/*.c) $(wildcard ./Library/inc/*.c) $(wildcard ./Start/*.c) $(wildcard ./User/*.c)
改完后:

总而言之,这是我的工程文件,如果你的,也要根据自己的C文件在哪,全部加进去。比如我,为了省事,干脆把全部文件路径都加进去了,即便里面可能没有C文件
step2
第二处

下面替换上面 :
C_DEFS = -DSTM32F10X_MD -DUSE_STDPERIPH_DRIVER
改完之后:

其实这里也是我们在keil里面添加的宏,这样更容易理解我们为什么要这样去做。
makefile文件中
-D为添加宏定义,这里添加的宏为
STM32F10X_MD
USE_STDPERIPH_DRIVER

step3
第三处

下面替换上面:
C_INCLUDES = -ICMSIS -ILibrary/inc -ILibrary/src -IStart -IUser
改完之后

这里是添加头文件路径,不需要.*h,它会自己去找,这里只要提供路径即可,为了省事,我干脆把全部文件路径都加进去了,即便里面可能没有和h文件
makefile文件中
-I为添加头文件路径。
step4
第四处,这里主要是要让make clean起作用。因为window环境下,不认识rm -f命令,这是linux的bash命令

下面替换上面:
ifeq ($(OS),Windows_NT) RM = rmdir /S /Q else RM = rm -rf endif clean: -$(RM) $(BUILD_DIR)
改完之后:

makefile文件修改完成。这里贴出,我的完整文件
##########################################################################################################################
# File automatically-generated by tool: [projectgenerator] version: [4.3.0-B58] date: [Fri Mar 14 19:48:43 CST 2025]
##########################################################################################################################
# ------------------------------------------------
# Generic Makefile (based on gcc)
#
# ChangeLog :
# 2017-02-10 - Several enhancements + project update mode
# 2015-07-22 - first version
# ------------------------------------------------
######################################
# target
######################################
TARGET = make_again
######################################
# building variables
######################################
# debug build?
DEBUG = 1
# optimization
OPT = -Og
#######################################
# paths
#######################################
# Build path
BUILD_DIR = build
######################################
# source
######################################
#第一处需要修改的地方
# C sources
C_SOURCES =
$(wildcard ./CMSIS/*.c)
$(wildcard ./Library/src/*.c)
$(wildcard ./Library/inc/*.c)
$(wildcard ./Start/*.c)
$(wildcard ./User/*.c)
# ASM sources
ASM_SOURCES =
startup_stm32f103xb.s
# ASM sources
ASMM_SOURCES =
#######################################
# binaries
#######################################
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.
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
CC = $(PREFIX)gcc
AS = $(PREFIX)gcc -x assembler-with-cpp
CP = $(PREFIX)objcopy
SZ = $(PREFIX)size
endif
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S
#######################################
# CFLAGS
#######################################
# cpu
CPU = -mcpu=cortex-m3
# fpu
# NONE for Cortex-M0/M0+/M3
# float-abi
# mcu
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)
# macros for gcc
# AS defines
AS_DEFS =
# C defines
#第二处需要修改的地方
C_DEFS =
-DSTM32F10X_MD
-DUSE_STDPERIPH_DRIVER
# AS includes
AS_INCLUDES =
# C includes
#第三处需要修改的地方
C_INCLUDES =
-ICMSIS
-ILibrary/inc
-ILibrary/src
-IStart
-IUser
# compile gcc flags
ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
CFLAGS += $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif
# Generate dependency information
CFLAGS += -MMD -MP -MF'$(@:%.o=%.d)'
#######################################
# LDFLAGS
#######################################
# link script
LDSCRIPT = STM32F103C8Tx_FLASH.ld
# libraries
LIBS = -lc -lm -lnosys
LIBDIR =
LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections
# default action: build all
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
#######################################
# build the application
#######################################
# list of objects
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
# list of ASM program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASMM_SOURCES:.S=.o)))
vpath %.S $(sort $(dir $(ASMM_SOURCES)))
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
$(AS) -c $(CFLAGS) $< -o $@
$(BUILD_DIR)/%.o: %.S Makefile | $(BUILD_DIR)
$(AS) -c $(CFLAGS) $< -o $@
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
$(CC) $(OBJECTS) $(LDFLAGS) -o $@
$(SZ) $@
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(HEX) $< $@
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
$(BIN) $< $@
$(BUILD_DIR):
mkdir $@
#######################################
# clean up
#######################################
#第四处需要修改的地方
ifeq ($(OS),Windows_NT)
RM = rmdir /S /Q
else
RM = rm -rf
endif
clean:
-$(RM) $(BUILD_DIR)
#######################################
# dependencies
#######################################
-include $(wildcard $(BUILD_DIR)/*.d)
# *** EOF ***
三、稍微修改标准库函数文件中的两个函数
如果我们直接编译会出现报错,执行make32(如果你把mingw32-make.exe复制一份改为make32.exe)

我们需要回到模板里面的标准库函数里面的core_cm3.c文件。我目前只能通过改函数的手段来解决这个问题。这个问题我目前的能力还无法解释。
在core_cm3.c大概700多行的地方

下面替换上面:
uint32_t __STREXB(uint8_t value, uint8_t *addr)
{
uint32_t result;
register uint8_t *tmp_addr asm('r1') = addr;
register uint8_t tmp_value asm('r2') = value;
__ASM volatile (
'strexb %0, %2, [%1]'
: '=r' (result)
: 'r' (tmp_addr), 'r' (tmp_value)
: // No clobbered registers
);
return result;
}
uint32_t __STREXH(uint16_t value, uint16_t *addr)
{
uint32_t result;
register uint16_t *tmp_addr asm('r1') = addr;
register uint16_t tmp_value asm('r2') = value;
__ASM volatile (
'strexh %0, %2, [%1]'
: '=r' (result)
: 'r' (tmp_addr), 'r' (tmp_value)
: // No clobbered registers
);
return result;
} 
完成,不管你采用什么方式,覆盖还是其它。这里的函数必须要去修改。不然编译无法通过。
此时再次编译就成功了!

四、烧录
我们可以在build文件夹中获得.hex文件。其实如果串口烧录。就没必要干其它的了。
但是这里我们打算详细讲解stlink的烧录。

stlink烧录
这里我们前面配置的openocd就起作用了。我们要用到的openocd主要有两个地方
./openocd/scripts/interface/stlink-v2.cfg
./openocd/scripts/target/stm32f1x.cfg
第一个是选择stlink的版本,但是openocd并不是只支持stlink
第二个是stm32的版本,这里是stm32f103c8t6,所有选择stm32f1x.cfg
路径需要绝对路径,但是你如果精通脚本或者其它什么,也可以去改。

openocd -f 'openocd的路径/openocd/scripts/interface/stlink-v2.cfg' -f 'openocd的路径/openocd/scripts/target/stm32f1x.cfg' -c init -c halt -c 'program ./build/hex的程序名字.hex verify reset' -c shutdown
不能直接用,要去改!要去改!
连接stlink,确保连上了,然后执行改过的指令

写入成功! 脱离Keil,使用Makefile来编译标准库STM32工程阶段任务已经完成!
这里我执行的命令是:
openocd -f 'D:/SoftWare/Environment/openocd/scripts/interface/stlink-v2.cfg' -f 'D:/SoftWare/Environment/openocd/scripts/target/stm32f1x.cfg' -c init -c halt -c 'program ./build/make_again.hex verify reset' -c shutdown
openocd -f 'D:/SoftWare/Environment/openocd/scripts/interface/stlink-v2.cfg' -f 'D:/SoftWare/Environment/openocd/scripts/target/stm32f1x.cfg' -c init -c halt -c 'program ./build/make_again.hex verify reset' -c shutdown
五、配置VSCode(非必须)
老地方,launch.json与tasks.json文件,
老问题,路径要改,两个文件的路径都必须改。改成自己的openocd路径
'D:/SoftWare/Environment/openocd/scripts/interface/stlink-v2.cfg',
'D:/SoftWare/Environment/openocd/scripts/target/stm32f1x.cfg'
launch.json文件

{
'version': '0.2.0',
'configurations': [
{
'name': 'Debug Microcontroller',
'type': 'cortex-debug',
'request': 'launch',
'cwd': '${workspaceFolder}',
'executable': '${workspaceFolder}/build/*.elf',
'servertype': 'openocd',
'configFiles': [
'D:/SoftWare/Environment/openocd/scripts/interface/stlink-v2.cfg',
'D:/SoftWare/Environment/openocd/scripts/target/stm32f1x.cfg'
],
'preLaunchTask': 'build',
'runToEntryPoint': 'main', // 替换为 runToEntryPoint
'svdFile': '${workspaceFolder}/STM32F103xx.svd'
}
]
}
tasks.json文件

{
'version': '2.0.0',
'tasks': [
{
'type': 'shell',
'label': 'build',
'command': 'make',
'args': [
'-C', '${workspaceFolder}',
'all'
],
'problemMatcher': [
'$gcc'
],
'group': 'build'
},
{
'type': 'shell',
'label': 'download',
'command': 'openocd',
'args': [
'-f', 'D:/SoftWare/Environment/openocd/scripts/interface/stlink-v2.cfg',
'-f', 'D:/SoftWare/Environment/openocd/scripts/target/stm32f1x.cfg',
'-c', 'program ${workspaceFolder}/build/*.elf verify reset exit'
],
'problemMatcher': [
'$gcc'
],
'group': 'build'
},
{
'type': 'shell',
'label': 'clean',
'command': 'make',
'args': [
'-C', '${workspaceFolder}',
'clean'
],
'problemMatcher': [
'$gcc'
],
'group': 'build'
},
{
'type': 'shell',
'label': 'rebuild',
'command': 'make',
'args': [
'-C', '${workspaceFolder}',
'clean', 'all'
],
'problemMatcher': [
'$gcc'
],
'group': 'build'
}
]
}
ok,改完就可以了。不过这是不能在线调试的哦。
下个关于这个的文章应该就是在linux环境下实现了!
六、注意事项
回顾整个过程,你应该会对stm32CubeMX生成另外两个文件感兴趣,它们分别是

要搞清楚它们,回要库本身。这里一个是启动文件的汇编.s文件,一个是 链接脚本.ld文件。
STM32的启动编译文件-CSDN博客
这两个文件在使用gcc-arm-none-eabi 是必须拥有的。那么这两个文件在哪里用上去了呢?
我们重新回到makefile文件中

注意观察哦,这里都是提供了型号芯片系列的名字f103,这也是我认为可以复现在其它芯片的原因。而且这里都没有加路径,那是因为这里我在根目录处,与User、Library等文件在同一位置。如果把这两个文件放在其它地方,这里也需要修改路径。说来也是有点好玩,用HAL里面的文件驱动标准库里面的东西,也就是说,它们在本质上面的一些东西是互通的。
这里是在HAL提供的库里面找到的,但是遗憾的是标准库也有类似的东西,不过即便带有gcc的标注,标准库提供的也不能通过gcc-arm-none-eabi 来编译。这里的原因我尚且没有找到。我个人推测,应该需要对应的编译器才能使用对应的启动文件,这里gcc-arm-none-eabi可能目前没有支持到这里。


- 嵌入式C开发环境构建指南:工具链配置、Makefile编写与调试实战
- 【Linux】GCC+Makefile打造正点原子stm32单片机开发环境
- stm32 makefile
- vs2022 makefile 开发编译 stm32
- VScode开发STM32/GD32单片机-MakeFile工程JlinkRTT配置
- Makefile 解析
- 基于stm32f4的自制menuconfig及makefile工程
- 迅为4412开发板实验_Makefile编译(下)
- 迅为4412开发板实验_Makefile编译(上)
- ESP32 ADF windows开发环境搭建 适配ADF到ESP32A1S
- 六大全新产品系列推出,MCX A微控制器家族迎来创新
- 意法半导体全新STM32C5系列,重新定义入门级微控制器性能与价值,赋能万千智能设备
- 模组复用与整机重测在SRRC、CCC、CTA/NAL认证中的实践操作指南
- 有源晶振与无源晶振的六大区别详解
- 英飞凌持续巩固全球微控制器市场领导地位
- 使用 Keil Studio for Visual Studio Code开发 STM32 设备
- 从控制到系统:TI利用边缘AI重塑嵌入式MCU的边界
- 蓝牙信道探测技术原理与开发套件实践
- Microchip 推出生产就绪型全栈边缘 AI 解决方案,赋能MCU和MPU实现 智能实时决策
- LoRa、LoRaWAN、NB-IoT与4G DTU技术对比及工业无线方案选型分析




