单片机
返回首页

使用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可能目前没有支持到这里。

 


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章