当前位置: 首页 > ops >正文

STM32F407VGT6从零建立一个标准库工程模板+VSCode或Keil5

一、前言

下载平台:STM32F407ZGT6
代码使用平台:VSCode
编译器:arm-none-aebi-gcc ---- 默认你已经安装
程序下载工具:STlink ---- 默认你拥有
批处理工具:make ---- 默认你已经安装
使用此方法可以不借助其它插件,例如:STM32EIDE。这个方法已经经过验证可以在STM32F103C8T6、STM32F103C6T6与STM32F407ZET6使用,因此个人认为此方法可以跨系列通用。
这里简要说明一下使用VSCode且不借助第三方插件的实现思路:
1.使用arm-none-aebi-gcc来编译工程
2.使用openocd通过STLink或者JLink来讲固件上传到STM32的Flash中
本文也会介绍Keil5创建标准库工程的方法。如果并不想了解VSCode创建工程,可以直接跳到Keil5创建工程部分。

二、开始新建工程

1、下载库文件

我们需要去ARM官网下载库文件,这个里面藏得有点深。不过我这里把流程也说一下吧!
我们最好把HAL库与标准库都下载下来吧,未来可以两个库一起学习!

1.1来到官网下载标准库文件

一步到位下载STM32F4的标准库:STSW-STM32065 | Product - STMicroelectronics
ST官网:https://www.st.com/
![[Pasted image 20250819195318.png]]在这里插入图片描述
![[Pasted image 20250819200007.png]]
![[Pasted image 20250819200101.png]]

![[Pasted image 20250819200200.png]]

![[Pasted image 20250819200446.png]]

![[Pasted image 20250819200522.png]]

![[Pasted image 20250819200714.png]]

1.2来到官网下载HAL库文件

一步到位下载STM32F4的HAL库文件:www.st.com
ST官网:https://www.st.com/
![[Pasted image 20250819195318.png]]

![[Pasted image 20250819200007.png]]

![[Pasted image 20250819200101.png]]

![[Pasted image 20250819201743.png]]

![[Pasted image 20250819201840.png]]

![[Pasted image 20250819202108.png]]

完成之后
![[Pasted image 20250819202543.png]]

2、开始配置VSCOde工程

我们这里配置的能在VSCode中,不借助Keil5工具的方法。当然我后面也会提供配置Keil5配置的方法。
我们先创建5个文件吧,精简工程
![[Pasted image 20250819203604.png]]

结构如下:
![[Pasted image 20250819203541.png]]
这里讲述一下各个文件的作用,然后我们开始迁移文件吧!

文件夹作用
CMSIS放置核心的文件与汇编文件
Library放置外设的头文件与源文件
Start放置启动的文件
User放置用户编写文件
ThirdPart放置第三方库,比如:FreeRtos、LVGL

2.1、配置CMSIS文件夹核心文件与汇编文件

2.1.1来到标准库

![[Pasted image 20250819210028.png]]

路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\CMSIS\Include
全部复制到,我们的CMSIS文件夹中
![[Pasted image 20250819211125.png]]

2.1.2、来到HAL库

![[Pasted image 20250819212012.png]]

路径:
.\STM32Cube_FW_F4_V1.28.0\Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\gcc
我们的芯片是STM32F407VGT6所以我们需要复制startup_stm32f407xx.s到CMSIS
![[Pasted image 20250819212215.png]]

3.CMSIS文件夹完成图

![[Pasted image 20250819212514.png]]

2.2、配置Library文件夹标准外设库文件

2.2.1、来到标准库

![[Pasted image 20250819210028.png]]

路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\STM32F4xx_StdPeriph_Driver
我们直接将inc与src直接复制到我们的Library文件夹中
![[Pasted image 20250819212840.png]]

不过我们需要在inc与src中把stm32f4xx_fmc.h和stm32f4xx_fmc.c文件移除,因为此文件与工程中的另外一个文件有重复定义部分,我们暂时移除。
![[Pasted image 20250819213140.png]]

2.2.2、Library文件夹完成图

![[Pasted image 20250819213439.png]]

2.3、配置Start文件夹启动文件

2.3.1、来到标准库

![[Pasted image 20250819210028.png]]

在标准库中的路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\CMSIS\Device\ST\STM32F4xx\Include
我们把此文件夹下的文件复制到Start文件夹中
![[Pasted image 20250819213805.png]]

在标准库中的路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates
我们把此文件夹下的文件system_stm32f4xx.c复制到Start文件夹中
![[Pasted image 20250819214114.png]]

在标准库中的路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Project\STM32F4xx_StdPeriph_Templates
我们把此文件夹下的文件stm32f4xx_conf.h复制到Start文件夹中
![[Pasted image 20250819214545.png]]

2.3.2、Start启动文件夹完成图

![[Pasted image 20250819214710.png]]

2.4、配置User文件夹用户文件

2.4.1、来到标准库

![[Pasted image 20250819210028.png]]

在标准库中的路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Project\STM32F4xx_StdPeriph_Templates
我们把此文件夹下的文件mian.h、stmf4xx_it.h复制到User/inc文件夹中
我们把此文件夹下的文件mian.c、stmf4xx_it.c复制到User/src文件夹中
![[Pasted image 20250819215107.png]]

2.4.2、User用户文件夹的配置完成图

![[Pasted image 20250819215505.png]]

2.5、配置部分文件

此部分的前提是你安装过arm-none-aebi-gcc与openocd并且配置好了环境。如果不会或者其它,我会专门出一份教程来讲述arm-none-aebi-gcc与openocd。这里你只要知道,我会在这本部分的openocd的放置路径说明就行。

提前创建好Makefile文件与STM32F407VGTx_FLASH.ld文件
![[Pasted image 20250819223132.png]]

2.5.1、配置Makefile文件
1、懒得看那么多字,直接一步到位

不过还是建议,看一遍后文哦!

################################################################################################
# Generic Makefile for STM32F407VGT6 (GCC)
################################################################################################
TARGET = template
DEBUG = 1
OPT   = -Og
BUILD_DIR = build
######################################
# source
######################################
C_SOURCES = \
$(wildcard ./CMSIS/*.c) \
$(wildcard ./Library/src/*.c) \
$(wildcard ./Start/*.c) \
$(wildcard ./User/src/*.c)ASM_SOURCES = \
./CMSIS/startup_stm32f407xx.s
######################################
# binaries
######################################
PREFIX  = arm-none-eabi-
CC      = $(PREFIX)gcc
CXX     = $(PREFIX)g++  # 添加 C++ 支持
AS      = $(PREFIX)gcc -x assembler-with-cpp
CP      = $(PREFIX)objcopy
SZ      = $(PREFIX)size
HEX     = $(CP) -O ihex
BIN     = $(CP) -O binary -S
######################################
# CFLAGS
######################################
CPU        = -mcpu=cortex-m4
FPU        = -mfpu=fpv4-sp-d16 -mfloat-abi=hard
MCU        = $(CPU) -mthumb $(FPU)C_DEFS = \
-DSTM32F40_41xxx \
-DUSE_STDPERIPH_DRIVER \
-DHSE_VALUE=8000000  # 添加时钟定义C_INCLUDES = \
-ICMSIS \
-ILibrary/inc \
-IStart \
-IUser/inc# 添加 C++ 支持标志
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti -fno-threadsafe-statics -fno-use-cxa-atexitASFLAGS  = $(MCU) $(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
CFLAGS  += -MMD -MP -MF"$(@:%.o=%.d)"
# 添加链接时优化
#LTO_FLAGS = -flto -ffat-lto-objects
# 1. 关闭 LTO
LTO_FLAGS =
######################################
# LDFLAGS
######################################
LDSCRIPT = STM32F407VGTx_FLASH.ld
LIBS     = -lc -lm -lnosys -lstdc++  # 添加 C++ 库支持
LDFLAGS  = $(MCU) -specs=nano.specs -specs=nosys.specs -T$(LDSCRIPT) $(LIBS) \-Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections \-Wl,--print-memory-usage  # 添加内存使用报告
# 2. 关闭 --gc-sections
#LDFLAGS  = $(MCU) -specs=nano.specs -specs=nosys.specs -T$(LDSCRIPT) $(LIBS) \-Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref \-Wl,--print-memory-usage
######################################
# build rules
######################################
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)$(CC) -c $(CFLAGS) $(LTO_FLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)$(AS) -c $(CFLAGS) $< -o $@
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile$(CC) $(OBJECTS) $(LDFLAGS) $(LTO_FLAGS) -o $@$(SZ) $@@echo "Memory usage:"@arm-none-eabi-size $@
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)$(HEX) $< $@
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)$(BIN) $< $@
$(BUILD_DIR):mkdir $@
######################################
# clean
######################################
ifeq ($(OS),Windows_NT)RM = rmdir /S /Q
elseRM = rm -rf
endif
clean:-$(RM) $(BUILD_DIR)
######################################
# download (openocd)
######################################
download:openocd -f "D:/SoftWare/Environment/openocd/scripts/interface/stlink-v2.cfg" -f "D:/SoftWare/Environment/openocd/scripts/target/stm32f4x.cfg" \-c init -c halt \-c "program $(BUILD_DIR)/$(TARGET).elf verify reset exit"
######################################
# J-Link flash (auto generate script)
######################################
jlink:@echo starting...@echo r > temp_script.jlink@echo h >> temp_script.jlink@echo loadfile $(BUILD_DIR)/$(TARGET).hex >> temp_script.jlink@echo r >> temp_script.jlink@echo q >> temp_script.jlink@echo using J-Link..."C:/Progra~1/SEGGER/JLink_V856a/JLink.exe" -device STM32F407ZE -if SWD -speed 4000 -CommanderScript temp_script.jlink@del temp_script.jlink@echo === success ===
# 添加调试目标
debug:@echo r > temp_debug.jlink@echo h >> temp_debug.jlink@echo loadfile $(BUILD_DIR)/$(TARGET).elf >> temp_debug.jlink@echo r >> temp_debug.jlink@echo g >> temp_debug.jlink"C:/Progra~1/SEGGER/JLink_V856a/JLink.exe" -device STM32F407ZE -if SWD -speed 4000 -CommanderScript temp_debug.jlink@del temp_debug.jlink
-include $(wildcard $(BUILD_DIR)/*.d)
2、开始编写Makefile文件

我们在编写Makefile文件之前,一定要明确5件事
1.谁编译 ---- arm-none-aebi-xxx
2.源文件在哪
3.头文件在哪
4.汇编文件在哪
5.链接文件在哪
当然,这是看懂一个Makefile文件的前提,可以不会写,但是一定要能看懂大概在干什么。
下文会大致介绍,但是实际实现的Makefile不会那么简单,但是核心逻辑就是如此。
即便换成其它系列的芯片,核心改的地方大致也就是这几个地方!

2.1、谁编译

这里不仅添加了arm-none-eabi支持C还加入了C++支持,并且为了编译汇编文件支持。

######################################
# binaries
######################################
PREFIX  = arm-none-eabi-
CC      = $(PREFIX)gcc
CXX     = $(PREFIX)g++  # 添加 C++ 支持
AS      = $(PREFIX)gcc -x assembler-with-cpp
CP      = $(PREFIX)objcopy
SZ      = $(PREFIX)size
HEX     = $(CP) -O ihex
BIN     = $(CP) -O binary -S
2.2、源文件在哪
######################################
# source
######################################
C_SOURCES = \
$(wildcard ./CMSIS/*.c) \
$(wildcard ./Library/src/*.c) \
$(wildcard ./Start/*.c) \
$(wildcard ./User/src/*.c)
2.3、头文件在哪
C_INCLUDES = \
-ICMSIS \
-ILibrary/inc \
-IStart \
-IUser/inc
2.4、汇编文件在哪
ASM_SOURCES = \
./CMSIS/startup_stm32f407xx.s
2.5、链接文件在哪
######################################
# LDFLAGS
######################################
LDSCRIPT = STM32F407VGTx_FLASH.ld
2.6、这里提供一份现成的Makefile
################################################################################################
# Generic Makefile for STM32F407VGT6 (GCC)
################################################################################################
TARGET = template
DEBUG = 1
OPT   = -Og
BUILD_DIR = build
######################################
# source
######################################
C_SOURCES = \
$(wildcard ./CMSIS/*.c) \
$(wildcard ./Library/src/*.c) \
$(wildcard ./Start/*.c) \
$(wildcard ./User/src/*.c)ASM_SOURCES = \
./CMSIS/startup_stm32f407xx.s
######################################
# binaries
######################################
PREFIX  = arm-none-eabi-
CC      = $(PREFIX)gcc
CXX     = $(PREFIX)g++  # 添加 C++ 支持
AS      = $(PREFIX)gcc -x assembler-with-cpp
CP      = $(PREFIX)objcopy
SZ      = $(PREFIX)size
HEX     = $(CP) -O ihex
BIN     = $(CP) -O binary -S
######################################
# CFLAGS
######################################
CPU        = -mcpu=cortex-m4
FPU        = -mfpu=fpv4-sp-d16 -mfloat-abi=hard
MCU        = $(CPU) -mthumb $(FPU)C_DEFS = \
-DSTM32F40_41xxx \
-DUSE_STDPERIPH_DRIVER \
-DHSE_VALUE=8000000  # 添加时钟定义C_INCLUDES = \
-ICMSIS \
-ILibrary/inc \
-IStart \
-IUser/inc# 添加 C++ 支持标志
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti -fno-threadsafe-statics -fno-use-cxa-atexitASFLAGS  = $(MCU) $(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
CFLAGS  += -MMD -MP -MF"$(@:%.o=%.d)"
# 添加链接时优化
#LTO_FLAGS = -flto -ffat-lto-objects
# 1. 关闭 LTO
LTO_FLAGS =
######################################
# LDFLAGS
######################################
LDSCRIPT = STM32F407VGTx_FLASH.ld
LIBS     = -lc -lm -lnosys -lstdc++  # 添加 C++ 库支持
LDFLAGS  = $(MCU) -specs=nano.specs -specs=nosys.specs -T$(LDSCRIPT) $(LIBS) \-Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections \-Wl,--print-memory-usage  # 添加内存使用报告
# 2. 关闭 --gc-sections
#LDFLAGS  = $(MCU) -specs=nano.specs -specs=nosys.specs -T$(LDSCRIPT) $(LIBS) \-Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref \-Wl,--print-memory-usage
######################################
# build rules
######################################
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)$(CC) -c $(CFLAGS) $(LTO_FLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)$(AS) -c $(CFLAGS) $< -o $@
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile$(CC) $(OBJECTS) $(LDFLAGS) $(LTO_FLAGS) -o $@$(SZ) $@@echo "Memory usage:"@arm-none-eabi-size $@
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)$(HEX) $< $@
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)$(BIN) $< $@
$(BUILD_DIR):mkdir $@
######################################
# clean
######################################
ifeq ($(OS),Windows_NT)RM = rmdir /S /Q
elseRM = rm -rf
endif
clean:-$(RM) $(BUILD_DIR)
######################################
# download (openocd)
######################################
download:openocd -f "D:/SoftWare/Environment/openocd/scripts/interface/stlink-v2.cfg" -f "D:/SoftWare/Environment/openocd/scripts/target/stm32f4x.cfg" \-c init -c halt \-c "program $(BUILD_DIR)/$(TARGET).elf verify reset exit"
######################################
# J-Link flash (auto generate script)
######################################
jlink:@echo starting...@echo r > temp_script.jlink@echo h >> temp_script.jlink@echo loadfile $(BUILD_DIR)/$(TARGET).hex >> temp_script.jlink@echo r >> temp_script.jlink@echo q >> temp_script.jlink@echo using J-Link..."C:/Progra~1/SEGGER/JLink_V856a/JLink.exe" -device STM32F407ZE -if SWD -speed 4000 -CommanderScript temp_script.jlink@del temp_script.jlink@echo === success ===
# 添加调试目标
debug:@echo r > temp_debug.jlink@echo h >> temp_debug.jlink@echo loadfile $(BUILD_DIR)/$(TARGET).elf >> temp_debug.jlink@echo r >> temp_debug.jlink@echo g >> temp_debug.jlink"C:/Progra~1/SEGGER/JLink_V856a/JLink.exe" -device STM32F407ZE -if SWD -speed 4000 -CommanderScript temp_debug.jlink@del temp_debug.jlink
-include $(wildcard $(BUILD_DIR)/*.d)

那么肯定有人问我,这个Makefile是你写的吗?我的回答是,不完全是,这是STM32CubeMX自动生成并且我经过修改,获得而成的。这里有一些HAL库与标准库的一些兼容性问题,我把其修改成符合标准库的Makefile,这里主要的内容是LTO与链接文件的编写。有兴趣的同学可以去研究研究这里面的区别哦。
这里也提供一份AI帮忙优化的Makefile,验证过使用没问题:

################################################################################################
# Generic Makefile for STM32F407VGT6 (GCC)
################################################################################################
# 目标名称
TARGET = template
# 构建选项
DEBUG = 1
OPT   = -Og
# 构建目录
BUILD_DIR = build
# 工具链前缀
PREFIX  = arm-none-eabi-
# 工具定义
CC      = $(PREFIX)gcc
CXX     = $(PREFIX)g++
AS      = $(PREFIX)gcc -x assembler-with-cpp
CP      = $(PREFIX)objcopy
SZ      = $(PREFIX)size
HEX     = $(CP) -O ihex
BIN     = $(CP) -O binary -S
GDB     = $(PREFIX)gdb
######################################
# 源文件
######################################
# 使用更精确的文件查找方式,避免包含不需要的文件
C_SOURCES = \
$(wildcard ./CMSIS/*.c) \
$(wildcard ./Library/src/*.c) \
$(wildcard ./Start/*.c) \
$(wildcard ./User/src/*.c)# 汇编源文件
ASM_SOURCES = ./CMSIS/startup_stm32f407xx.s######################################
# 编译选项
######################################
# MCU 选项
CPU        = -mcpu=cortex-m4
FPU        = -mfpu=fpv4-sp-d16 -mfloat-abi=hard
MCU        = $(CPU) -mthumb $(FPU)# C 定义
C_DEFS = \
-DSTM32F40_41xxx \
-DUSE_STDPERIPH_DRIVER \
-DHSE_VALUE=8000000 \
-D__weak="__attribute__((weak))" \
-D__packed="__attribute__((__packed__))"# 包含路径
C_INCLUDES = \
-ICMSIS \
-ILibrary/inc \
-IStart \
-IUser/inc# 汇编标志
ASFLAGS  = $(MCU) $(OPT) -Wall -fdata-sections -ffunction-sections# C 标志
CFLAGS   = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections# 调试选项
ifeq ($(DEBUG), 1)
CFLAGS  += -g -gdwarf-2
endif# 依赖生成
CFLAGS  += -MMD -MP -MF"$(@:%.o=%.d)"# C++ 标志
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti -fno-threadsafe-statics -fno-use-cxa-atexit# 链接时优化 (可选)
# LTO_FLAGS = -flto
######################################
# 链接选项
######################################
LDSCRIPT = STM32F407VGTx_FLASH.ld
LIBS     = -lc -lm -lnosys -lstdc++
LDFLAGS  = $(MCU) -specs=nano.specs -specs=nosys.specs -T$(LDSCRIPT) $(LIBS) \-Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections \-Wl,--print-memory-usage# 可选: 移除未使用段以减小代码大小
# LDFLAGS += -Wl,--gc-sections
######################################
# 构建目标
######################################
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin# 对象文件列表
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))# C 编译规则
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)@echo "Compiling $<"@$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@# 汇编编译规则
$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)@echo "Assembling $<"@$(AS) -c $(CFLAGS) $< -o $@# 链接规则
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile@echo "Linking $@"@$(CC) $(OBJECTS) $(LDFLAGS) -o $@@echo "=== Build completed ==="@$(SZ) $@@echo "Memory usage:"@arm-none-eabi-size --format=sysv $@# 生成 HEX 文件
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)@$(HEX) $< $@# 生成 BIN 文件
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)@$(BIN) $< $@# 创建构建目录
$(BUILD_DIR):@mkdir -p $@######################################
# 清理规则
######################################
ifeq ($(OS),Windows_NT)RM = rmdir /S /Q
elseRM = rm -rf
endifclean:-$(RM) $(BUILD_DIR)######################################
# 烧录和调试
######################################
# OpenOCD 烧录
OPENOCD = openocd
OPENOCD_SCRIPT_DIR = D:/SoftWare/Environment/openocd/scripts
OPENOCD_INTERFACE = stlink-v2.cfg
OPENOCD_TARGET = stm32f4x.cfgdownload:$(OPENOCD) -f $(OPENOCD_SCRIPT_DIR)/interface/$(OPENOCD_INTERFACE) \-f $(OPENOCD_SCRIPT_DIR)/target/$(OPENOCD_TARGET) \-c init -c halt \-c "program $(BUILD_DIR)/$(TARGET).elf verify reset exit"# J-Link 烧录
JLINK = "C:/Program Files/SEGGER/JLink_V856a/JLink.exe"
JLINK_DEVICE = STM32F407VG
JLINK_IF = SWD
JLINK_SPEED = 4000jlink:@echo "Generating J-Link script..."@echo "r" > temp_script.jlink@echo "h" >> temp_script.jlink@echo "loadfile $(BUILD_DIR)/$(TARGET).hex" >> temp_script.jlink@echo "r" >> temp_script.jlink@echo "q" >> temp_script.jlink@echo "Flashing with J-Link..."@$(JLINK) -device $(JLINK_DEVICE) -if $(JLINK_IF) -speed $(JLINK_SPEED) -CommanderScript temp_script.jlink@rm -f temp_script.jlink@echo "=== Flash completed ==="# 调试
debug:@echo "r" > temp_debug.jlink@echo "h" >> temp_debug.jlink@echo "loadfile $(BUILD_DIR)/$(TARGET).elf" >> temp_debug.jlink@echo "r" >> temp_debug.jlink@echo "g" >> temp_debug.jlink@$(JLINK) -device $(JLINK_DEVICE) -if $(JLINK_IF) -speed $(JLINK_SPEED) -CommanderScript temp_debug.jlink@rm -f temp_debug.jlink# GDB 调试 (可选)
gdb:$(GDB) $(BUILD_DIR)/$(TARGET).elf -ex "target extended-remote :3333"
# 依赖包含
-include $(wildcard $(BUILD_DIR)/*.d)# 显示帮助信息
help:@echo "=== Makefile 帮助 ==="@echo "make all       - 构建所有目标 (默认)"@echo "make clean     - 清理构建文件"@echo "make download  - 使用 OpenOCD 烧录"@echo "make jlink     - 使用 J-Link 烧录"@echo "make debug     - 使用 J-Link 调试"@echo "make gdb       - 启动 GDB 调试"@echo "make help      - 显示此帮助信息".PHONY: all clean download jlink debug gdb help
2.5.2.配置STM32F407VGTx_FLASH.ld(此文件自己编写或者AI生成)

这里我提供一份写好的链接文件:

/** STM32F407VGT6 链接脚本* 硬件配置: 1024KB FLASH, 128KB SRAM, 64KB CCMRAM* 支持标准库、CCMRAM使用、调试信息*/
/* 入口点定义 */
ENTRY(Reset_Handler)
/* 栈顶地址定义 */
_estack = ORIGIN(RAM) + LENGTH(RAM);
/* 堆和栈大小配置 */
_Min_Heap_Size  = 0x800;   /* 2KB堆 */
_Min_Stack_Size = 0x1000;  /* 4KB栈 */
/* 内存布局定义 */
MEMORY
{FLASH  (rx)  : ORIGIN = 0x08000000, LENGTH = 1024K  /* 主FLASH - STM32F407VGT6有1024KB */RAM    (xrw) : ORIGIN = 0x20000000, LENGTH = 128K   /* 主SRAM - STM32F407VGT6有128KB */CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K    /* CCMRAM - STM32F407VGT6有64KB */
}
/* 段配置 */
SECTIONS
{/* 1. 中断向量表 - 必须放在FLASH起始位置 */.isr_vector :{. = ALIGN(4);KEEP(*(.isr_vector))                 /* 中断向量表必须保留 */. = ALIGN(4);} > FLASH/* 2. 代码段和只读数据 */.text :{. = ALIGN(4);*(.text)                             /* 程序代码 */*(.text*)                            /* 所有.text子段 */*(.rodata)                           /* 只读数据 */*(.rodata*)                          /* 所有.rodata子段 */*(.glue_7)                           /* ARM与Thumb指令交互 */*(.glue_7t)                          /* ARM与Thumb指令交互 */*(.gnu.linkonce.t*)/* 函数构造与析构 */KEEP(*(.init))KEEP(*(.fini)). = ALIGN(4);_etext = .;                          /* 代码段结束标记 */} > FLASH/* 3. ARM异常处理段 */.ARM.extab :{*(.ARM.extab* .gnu.linkonce.armextab.*)} > FLASH.ARM :{__exidx_start = .;*(.ARM.exidx*)__exidx_end = .;} > FLASH/* 4. 初始化数组 */.preinit_array :{PROVIDE_HIDDEN(__preinit_array_start = .);KEEP(*(.preinit_array*))PROVIDE_HIDDEN(__preinit_array_end = .);} > FLASH.init_array :{PROVIDE_HIDDEN(__init_array_start = .);KEEP(*(SORT(.init_array.*)))KEEP(*(.init_array*))PROVIDE_HIDDEN(__init_array_end = .);} > FLASH/* 5. 反初始化数组 */.fini_array :{PROVIDE_HIDDEN(__fini_array_start = .);KEEP(*(SORT(.fini_array.*)))KEEP(*(.fini_array*))PROVIDE_HIDDEN(__fini_array_end = .);} > FLASH/* 6. 已初始化数据段 (RAM中,从FLASH加载) *//* 使用与启动文件匹配的符号名称 */_sidata = LOADADDR(.data);.data :{. = ALIGN(4);_sdata = .;                          /* 数据段起始地址 - 与启动文件匹配 */*(.data)                             /* 数据段 */*(.data*)                            /* 所有.data子段 */*(.gnu.linkonce.d*). = ALIGN(4);_edata = .;                          /* 数据段结束地址 - 与启动文件匹配 */} > RAM AT > FLASH/* 7. CCMRAM已初始化数据 (高速RAM) */_siccmram = LOADADDR(.ccmdata);.ccmdata :{. = ALIGN(4);_sccmdata = .;                       /* CCM数据起始 */*(.ccmdata)*(.ccmdata*). = ALIGN(4);_eccmdata = .;                       /* CCM数据结束 */} > CCMRAM AT > FLASH/* 8. 未初始化数据段 (BSS) - 使用与启动文件匹配的符号名称 */.bss :{. = ALIGN(4);__bss_start__ = .;                   /* BSS起始 */_sbss = .;                           /* 与启动文件匹配 */*(.bss)*(.bss*)*(COMMON)                            /* 公共未初始化数据 */. = ALIGN(4);_ebss = .;                           /* 与启动文件匹配 */__bss_end__ = .;                     /* BSS结束 */} > RAM/* 9. CCMRAM未初始化数据 */.ccmbss (NOLOAD) :{. = ALIGN(4);_sccmbss = .;                        /* CCM BSS起始 */*(.ccmbss)*(.ccmbss*). = ALIGN(4);_eccmbss = .;                        /* CCM BSS结束 */} > CCMRAM/* 10. 堆和栈 */._user_heap_stack (NOLOAD) :{. = ALIGN(8);PROVIDE(end = .);PROVIDE(_end = .);. = . + _Min_Heap_Size;              /* 堆区域 */. = . + _Min_Stack_Size;             /* 栈区域 */. = ALIGN(8);} > RAM/* 11. 特殊段定义 */.ARM.attributes 0 : { *(.ARM.attributes) }  /* 架构属性 */.debug_info     0 : { *(.debug_info) }      /* 调试信息 */.debug_line     0 : { *(.debug_line) }      /* 调试行号 */.debug_frame    0 : { *(.debug_frame) }     /* 调试帧信息 *//* 12. 丢弃不需要的段 *//DISCARD/ :{libc.a ( * )libm.a ( * )libgcc.a ( * )}
}
/* 提供给启动文件的堆/栈符号 */
PROVIDE(__heap_size = _Min_Heap_Size);
PROVIDE(__stack_size = _Min_Stack_Size);
PROVIDE(__heap_base = _ebss);
PROVIDE(__heap_limit = _ebss + _Min_Heap_Size);
2.5.3.删除部分可有可无的内容(集中在main.c与main.h文件)
1、删除main.h与main.c的部分代码

感觉有点麻烦,可以直接全部删除,区别只是想不想保留TimingDelay_Decrement函数
这里提供一下保留的mian.c代码,可以直接使用

#include "main.h"
#include <stm32f4xx_rcc.h>
#include <stm32f4xx_gpio.h>static __IO uint32_t uwTimingDelay;
RCC_ClocksTypeDef RCC_Clocks;
static void Delay(__IO uint32_t nTime);
int main(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_GetClocksFreq(&RCC_Clocks);SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);Delay(5);RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  GPIO_Init(GPIOA, &GPIO_InitStructure);/* Infinite loop */while (1){GPIO_ToggleBits(GPIOA,GPIO_Pin_0);//此处延时1秒Delay(100);}
}void Delay(__IO uint32_t nTime)
{uwTimingDelay = nTime;while(uwTimingDelay != 0);
}void TimingDelay_Decrement(void)
{if (uwTimingDelay != 0x00){uwTimingDelay--;}
}

值得一提的是,如果全部删除main.c与main.h的内容,需要在stm32f4xx_it.c
注释函数TimingDelay_Decrement();
否则会报错哦!
![[Pasted image 20250819232836.png]]

2、官方文件system_stm32f4xx.c的一个很奇怪的while,我们只需要删除掉一个分号

![[Pasted image 20250819232156.png]]

3、在stm32f4xx.h中注释掉如下宏

如果不注释会出现解决40多个警告!
![[Pasted image 20250819231711.png]]

2.5.4、配置系统正确时钟

正确的把时钟配置,需要修改两个地方,并且要明确知道你的板子的外部高速晶振的频率,这里我的板子晶振频率是8MHz。

1、在文件stm32f4xx.h中修改HSE_VALUE的值

![[Pasted image 20250819234650.png]]

2、在文件system_stm32f4xx.c中修改PLL_M的值

![[Pasted image 20250819234909.png]]

3、简述为何需要如此设置

STM32 通常被认为是 有 4 个独立时钟源

  • HSI(高速内部时钟):内部 RC 振荡器,8 MHz,精度较低,无需外部元件。
  • HSE(高速外部时钟):可接外部晶振或时钟源,频率范围 4 MHz~16 MHz。
  • LSI(低速内部时钟):内部 RC 振荡器,约 40 kHz,用于独立看门狗或 RTC。
  • LSE(低速外部时钟):外部 32.768 kHz 晶振,主要用于 RTC

众所周知,STM32F407VGT6能支持的最大时钟是168MHz,但是这4个独立时钟源却没有一个频率能达到168MHz,那么这个168MHz的频率是哪里来的呢?
这部分的知识涉及到stm32里面的时钟的一个最为特殊的时钟源----倍频锁相环时钟(PLLCLK)。它常常被系统选择为时钟源,它可以做到将低频输入时钟通过锁相环(PLL)倍频,输出高频稳定时钟,供系统、外设或CPU使用。
这里的知识可以去stm32f4xx参考手册中的时钟树学习,这里文章篇幅问题就不深入探索,如果感兴趣,我会专门出文章讲解。你只要知道,经过上面的配置之后,你的MCU就达到168MHz的频率运行。这里贴出时钟树的图片:
![[Pasted image 20250820000253.png]]

3、开始配置Keil5工程

配置Keil5工程总共分为两个大类,创建工程目录与Keil5软件里面的配置,我们在进行的时候,务必要先去KEIL官方下载,对应的芯片包,否则在创建工程时无法选择芯片型号。
![[Pasted image 20250820003943.png]]

去官网,下载安装运行即可Arm Keil | Keil STM32F4xx_DFP
![[Pasted image 20250820004801.png]]

3.1、工程目录配置

我们先创建5个文件吧,精简工程
![[Pasted image 20250819203604.png]]

结构如下:
![[Pasted image 20250819203541.png]]

这里讲述一下各个文件的作用,然后我们开始迁移文件吧!

文件夹作用
CMSIS放置核心的文件与汇编文件
Library放置外设的头文件与源文件
Start放置启动的文件
User放置用户编写文件
ThirdPart放置第三方库,比如:FreeRtos、LVGL(这个可以先空着)
3.1.1、配置CMSIS文件夹核心文件与汇编文件
1、来到标准库

![[Pasted image 20250819210028.png]]

路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\CMSIS\Include
全部复制到,我们的CMSIS文件夹中
![[Pasted image 20250820083024.png]]

在标准库中的路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm
我们把此文件夹下的startup_stm32f40_41xxx.s文件复制到CMSIS文件夹中
![[Pasted image 20250820082953.png]]

2、配置CMSIS文件夹核心文件与汇编文件完成图

![[Pasted image 20250820083117.png]]

3.1.2、配置Library文件夹标准外设库文件
1、来到标准库

![[Pasted image 20250819210028.png]]

在标准库路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\STM32F4xx_StdPeriph_Driver
我们把此路径下的inc与src文件夹直接复制到我们的Library文件夹中
![[Pasted image 20250820083352.png]]

2、配置Library文件夹标准外设库文件完成图

![[Pasted image 20250820083446.png]]

3.1.3、配置Start文件夹启动文件
1、来到标准库

![[Pasted image 20250819210028.png]]

在标准库路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\CMSIS\Device\ST\STM32F4xx\Include
我们复制此路径下的stm32f4xx.h与system_stm32f4xx.h文件到Start文件夹下
![[Pasted image 20250820083756.png]]

在标准库路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates
我们复制此路径下的system_stm32f4xx.c文件到Start文件夹下
![[Pasted image 20250820083908.png]]

2、配置Start文件夹启动文件完成图

![[Pasted image 20250820084012.png]]

3.1.4、配置User文件夹用户文件
1、来到标准库

![[Pasted image 20250819210028.png]]

在标准库路径:
.\STM32F4xx_DSP_StdPeriph_Lib_V1.9.0\Project\STM32F4xx_StdPeriph_Templates
复制main.c、main.h、stm32f4xx_it.c、stm32f4xx_it.h、stm32f4xx_conf.h到我们的User文件夹下,把头文件放置到inc中,源文件放置到src中
![[Pasted image 20250820084137.png]]

2、配置User文件夹用户文件完成图

![[Pasted image 20250820084512.png]]

以上工程文件配置完成!

3.2、Keil5工程配置

我们在来到Keil5之前,给我们的工程创建一个文件夹Link保存中间文件,防止工程变成混乱!等下直接Keil5的工程文件放置到Link文件夹中。
![[Pasted image 20250820084903.png]]

3.2.1、开始创建工程

创建工程
![[Pasted image 20250820085031.png]]

选择创建到Link文件夹中
![[Pasted image 20250820085131.png]]

![[Pasted image 20250820085158.png]]

选择好自己的芯片
![[Pasted image 20250820085322.png]]

3.2.2、开始配置工程源文件路径

![[Pasted image 20250820085513.png]]

创建与工程目录匹配的工程结构
![[Pasted image 20250820090427.png]]

开始加入源文件与汇编文件
![[Pasted image 20250820085843.png]]

![[Pasted image 20250820090012.png]]

![[Pasted image 20250820090340.png]]

![[Pasted image 20250820090544.png]]

配置完成
![[Pasted image 20250820090628.png]]

![[Pasted image 20250820090656.png]]

3.2.3、开始配置工程头文件路径与宏配置

配置魔术棒
![[Pasted image 20250820090820.png]]

![[Pasted image 20250820091015.png]]

![[Pasted image 20250820091426.png]]

来到C/C++加入宏

STM32F40_41xxx,USE_STDPERIPH_DRIVER

![[Pasted image 20250820091712.png]]

增加头文件路径
![[Pasted image 20250820091856.png]]

![[Pasted image 20250820091957.png]]

![[Pasted image 20250820092036.png]]

对了记得正确配置好STlink下载哦!
![[Pasted image 20250820095750.png]]

![[Pasted image 20250820095858.png]]

![[Pasted image 20250820095945.png]]

以上魔法棒全部配置完成!

3.2.4、解决报警告问题

![[Pasted image 20250820092241.png]]

1、在stm32f4xx.h中注释掉如下宏

![[Pasted image 20250819231711.png]]

2、官方文件system_stm32f4xx.c的一个很奇怪的while,我们只需要删除掉一个分号

![[Pasted image 20250819232156.png]]

报警告已解决!
![[Pasted image 20250820095032.png]]

3.2.5、删除部分可有可无的内容(集中在main.c与main.h文件)
1、删除main.h与main.c的部分代码

感觉有点麻烦,可以直接全部删除,区别只是想不想保留TimingDelay_Decrement函数
这里提供一下保留的mian.c代码,可以直接使用

#include "main.h"
#include <stm32f4xx_rcc.h>
#include <stm32f4xx_gpio.h>static __IO uint32_t uwTimingDelay;
RCC_ClocksTypeDef RCC_Clocks;
static void Delay(__IO uint32_t nTime);
int main(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_GetClocksFreq(&RCC_Clocks);SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);Delay(5);RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  GPIO_Init(GPIOA, &GPIO_InitStructure);/* Infinite loop */while (1){GPIO_ToggleBits(GPIOA,GPIO_Pin_0);//此处延时1秒Delay(100);}
}void Delay(__IO uint32_t nTime)
{uwTimingDelay = nTime;while(uwTimingDelay != 0);
}void TimingDelay_Decrement(void)
{if (uwTimingDelay != 0x00){uwTimingDelay--;}
}

值得一提的是,如果全部删除main.c与main.h的内容,需要在stm32f4xx_it.c
注释函数TimingDelay_Decrement();
否则会报错哦!
![[Pasted image 20250819232836.png]]

3.2.6、配置系统正确时钟

正确的把时钟配置,需要修改两个地方,并且要明确知道你的板子的外部高速晶振的频率,这里我的板子晶振频率是8MHz。

1、在文件stm32f4xx.h中修改HSE_VALUE的值

![[Pasted image 20250819234650.png]]

2、在文件system_stm32f4xx.c中修改PLL_M的值

![[Pasted image 20250819234909.png]]

3、简述为何需要如此设置

STM32 通常被认为是 有 4 个独立时钟源

  • HSI(高速内部时钟):内部 RC 振荡器,8 MHz,精度较低,无需外部元件。
  • HSE(高速外部时钟):可接外部晶振或时钟源,频率范围 4 MHz~16 MHz。
  • LSI(低速内部时钟):内部 RC 振荡器,约 40 kHz,用于独立看门狗或 RTC。
  • LSE(低速外部时钟):外部 32.768 kHz 晶振,主要用于 RTC

众所周知,STM32F407VGT6能支持的最大时钟是168MHz,但是这4个独立时钟源却没有一个频率能达到168MHz,那么这个168MHz的频率是哪里来的呢?
这部分的知识涉及到stm32里面的时钟的一个最为特殊的时钟源----倍频锁相环时钟(PLLCLK)。它常常被系统选择为时钟源,它可以做到将低频输入时钟通过锁相环(PLL)倍频,输出高频稳定时钟,供系统、外设或CPU使用。
这里的知识可以去stm32f4xx参考手册中的时钟树学习,这里文章篇幅问题就不深入探索,如果感兴趣,我会专门出文章讲解。你只要知道,经过上面的配置之后,你的MCU就达到168MHz的频率运行。这里贴出时钟树的图片:
![[Pasted image 20250820000253.png]]

以上全部完成!

三、后记

如果完全按照本文完成工程配置,你可以获得一个完全脱离Keil与VSCode第三方插件的STM32VGT6工程,与一个常规的Keil5工程模板。
本文初衷也是希望能够让更多人了解STM32工程的配置过程与提供其它思路。当然本文也有很多的纰漏,比如没有说明arm-none-aebi-gcc、make、openocd等何处下载与配置,这些都是基于篇幅做的取舍,可以看我往期的文章,里面其实有介绍。
https://blog.csdn.net/weixin_65437579/article/details/145797256
https://blog.csdn.net/weixin_65437579/article/details/146296055
那么这个工程有用吗?我的回答是有用!
这是main.c文件
PA0为LED
PA1为按键

#include "main.h"
#include <stm32f4xx_rcc.h>
#include <stm32f4xx_gpio.h>
#include "stm32f4xx_exti.h"
#include "misc.h"
#include <stm32f4xx_syscfg.h>
void EXTI1_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line1) != RESET){GPIO_ToggleBits(GPIOA, GPIO_Pin_0);EXTI_ClearITPendingBit(EXTI_Line1); // 清中断标志}
}
static void GPIO_EXTI_Config(void)
{GPIO_InitTypeDef gpio;EXTI_InitTypeDef exti;NVIC_InitTypeDef nvic;/* 1. 使能时钟 */RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);/* 2. 配置 PA1 为上拉输入 */gpio.GPIO_Pin = GPIO_Pin_1;gpio.GPIO_Mode = GPIO_Mode_IN;gpio.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(GPIOA, &gpio);/* 3. 将 PA1 映射到 EXTI1 */SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource1);/* 4. EXTI1 下降沿触发 */exti.EXTI_Line = EXTI_Line1;exti.EXTI_Mode = EXTI_Mode_Interrupt;exti.EXTI_Trigger = EXTI_Trigger_Falling;exti.EXTI_LineCmd = ENABLE;EXTI_Init(&exti);/* 5. NVIC 配置 */nvic.NVIC_IRQChannel = EXTI1_IRQn;nvic.NVIC_IRQChannelPreemptionPriority = 0x0F;nvic.NVIC_IRQChannelSubPriority = 0x0F;nvic.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&nvic);
}
static __IO uint32_t uwTimingDelay;
RCC_ClocksTypeDef RCC_Clocks;
static void Delay(__IO uint32_t nTime);int main(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_GetClocksFreq(&RCC_Clocks);SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);Delay(5);RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_EXTI_Config();while (1){Delay(100);}
}void Delay(__IO uint32_t nTime)
{uwTimingDelay = nTime;while(uwTimingDelay != 0);
}void TimingDelay_Decrement(void)
{if (uwTimingDelay != 0x00){uwTimingDelay--;}
}

实物图:
随便找的板子,无广!
![[IMG_20250820_101735.jpg]]

![[IMG_20250820_101737.jpg]]

达到程序预期效果!

http://www.xdnf.cn/news/18233.html

相关文章:

  • 详解MySQL中的多表查询:多表查询分类讲解、七种JOIN操作的实现
  • 《Linux运维总结:Shell脚本位置参数的具体使用》
  • 【笔记】动手学Ollama 第五章 Ollama 在 LangChain 中的使用 - Python 集成
  • 存储系统中清空日志文件的常用方法总结
  • vue3 el-select 默认选中第一个
  • 链表-24.两两交换链表中的结点-力扣(LeetCode)
  • 绕过 C 标准库限制执行系统命令:系统调用、Shellcode 和裸机二进制
  • 税务专业人员能力构建与发展路径指南
  • Qt5多线程编程详细讲解
  • [递归回溯]679. 24 点游戏
  • 基于RK3568/J6412的EMU多网口控制主机,助力储能工业互联管理和运维
  • PyTorch 社区贡献 和 设计原则
  • 第5课_Rust生命周期和泛型
  • Android MVVM(Model-View-ViewModel)架构
  • 从零开始的云计算生活——第四十七天,细水长流,kubernetes模块之ingress资源对象
  • 23TaskExecutor初始化
  • 【ansible】4.实施任务控制
  • AI 伦理的 “灰色地带”:当算法拥有决策权,公平与隐私该如何平衡?
  • 工地智能安全带让高空作业更安全
  • Kafka如何保证消费确认与顺序消费?
  • gcc 与 g++ 的区别:本身不是编译器而是编译器驱动
  • 数据库优化提速(一)之进销存库存管理—仙盟创梦IDE
  • 【Tech Arch】Apache Pig大数据处理的高效利器
  • 【JavaEE】多线程 -- 线程池
  • 基于单片机太阳能充电器/太阳能转换电能
  • 30. 技术专题-锁
  • HTTP的协议
  • .gitignore 文件 记录
  • Linux服务器性能优化总结
  • 【Tech Arch】Apache HBase分布式 NoSQL 数据库