Windows环境中,脱离Keil,使用Makefile来编译标准库STM32工程 ---- STM32F103C8T6

引言

前段时间学习了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固件库下载及安装-优快云博客,或者你的或者其它人的,只要是一个标准模板。全部拿过来就可以,这里我把我的拿过来

完成图:

那么开始吧,修改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的启动编译文件-优快云博客

 这两个文件在使用gcc-arm-none-eabi 是必须拥有的。那么这两个文件在哪里用上去了呢?

我们重新回到makefile文件中

 注意观察哦,这里都是提供了型号芯片系列的名字f103,这也是我认为可以复现在其它芯片的原因。而且这里都没有加路径,那是因为这里我在根目录处,与User、Library等文件在同一位置。如果把这两个文件放在其它地方,这里也需要修改路径。说来也是有点好玩,用HAL里面的文件驱动标准库里面的东西,也就是说,它们在本质上面的一些东西是互通的。

这里是在HAL提供的库里面找到的,但是遗憾的是标准库也有类似的东西,不过即便带有gcc的标注,标准库提供的也不能通过gcc-arm-none-eabi 来编译。这里的原因我尚且没有找到。我个人推测,应该需要对应的编译器才能使用对应的启动文件,这里gcc-arm-none-eabi可能目前没有支持到这里。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值