笔者来聊聊Makefile编译以及CortexM4命令行STLINK 烧录
Makefile编译以及CortexM4命令行STLINK 烧录
- 1、简单Makefile编译STM32工程
- 1.1 makefile 变量
- 1.2 makefile 关键字
- 1.3 makefile 自动推导
- 2、ST-Link命令行烧录
- 2.1 常见的界面烧录方式
- 2.1 STLink CLI 命令行烧录方式
1、简单Makefile编译STM32工程
先放一个笔者的makefile工程,笔者之前介绍过一个makefile语法,不过比较简单。
# ###########################################################
# File : makfile for project compiler
# Author: guoqing.zhang
# date : 2022/11/20
###########################################################
# Target
TARGET = AdvancedClock
DEBUG = -O2
# directoty for compiler object file
BUILD_OBJ_DIR :=obj
BUILD_DEP_DIR :=dep
OUT_DIR :=out
# directoty for compiler source file
CORE := ../Core
DRIVER := ../Driver
SOURCECODE := ../SourceCode
# source file path
C_SOURCE = \
$(wildcard $(CORE)/*.c) \
$(wildcard $(DRIVER)/src/misc.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_adc.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_dac.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_dma.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_dma2d.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_exti.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_flash.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_gpio.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_iwdg.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_pwr.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_rcc.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_rtc.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_spi.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_syscfg.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_tim.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_usart.c) \
$(wildcard $(DRIVER)/src/stm32f4xx_wwdg.c) \
$(wildcard $(SOURCECODE)/*.c) \
$(wildcard $(SOURCECODE)/bluetooth/*.c) \
$(wildcard $(SOURCECODE)/cJson/*.c) \
$(wildcard $(SOURCECODE)/common/*.c) \
$(wildcard $(SOURCECODE)/ht_sensor/*.c) \
$(wildcard $(SOURCECODE)/key/*.c) \
$(wildcard $(SOURCECODE)/led/*.c) \
$(wildcard $(SOURCECODE)/msg_deal/*.c) \
$(wildcard $(SOURCECODE)/nrf24l01/*.c) \
$(wildcard $(SOURCECODE)/oled/*.c) \
$(wildcard $(SOURCECODE)/queue/*.c) \
$(wildcard $(SOURCECODE)/rtc/*.c) \
$(wildcard $(SOURCECODE)/timer/*.c) \
$(wildcard $(SOURCECODE)/uart/*.c) \
$(wildcard $(SOURCECODE)/wdg/*.c) \
$(wildcard $(SOURCECODE)/wifi/*.c) \
$(wildcard $(SOURCECODE)/show/*.c)
# asm source file path
ASM_SOURCE = \
$(wildcard $(CORE)/startup_stm32f40_41xxx.s)
# inlcude file path
C_INCLUDE = \
-I$(CORE)/ \
-I$(DRIVER)/inc/ \
-I$(SOURCECODE)/ \
-I$(SOURCECODE)/bluetooth/ \
-I$(SOURCECODE)/cJson/ \
-I$(SOURCECODE)/common/ \
-I$(SOURCECODE)/key/ \
-I$(SOURCECODE)/ht_sensor/ \
-I$(SOURCECODE)/led/ \
-I$(SOURCECODE)/msg_deal/ \
-I$(SOURCECODE)/nrf24l01/ \
-I$(SOURCECODE)/oled/ \
-I$(SOURCECODE)/queue/ \
-I$(SOURCECODE)/radio/ \
-I$(SOURCECODE)/rtc/ \
-I$(SOURCECODE)/timer/ \
-I$(SOURCECODE)/uart/ \
-I$(SOURCECODE)/wdg/ \
-I$(SOURCECODE)/wifi/ \
-I$(SOURCECODE)/show/ \
# compiler exe
CC_EXEC := armcc.exe
AR_EXEC := armar.exe
ASM_EXEC := armasm.exe
LINK_EXEC := armlink.exe
FROMELF_EXEC := fromelf.exe
DOWNLOAD_EXEC := ST-LINK_CLI.exe
RM := rm -rf
ECHO := echo
CMN_CFLAGS = --C99 -c --cpu Cortex-M4.fp.sp -g --apcs=interwork --split_sections \
$(DEBUG) $(C_INCLUDE) \
--diag_suppress=1295,111,1293,167,513,177 \
-ID:/Software/Keil/pack/Keil/STM32F4xx_DFP/2.13.0/Drivers/CMSIS/Device/ST/STM32F4xx/Include \
-D__UVISION_VERSION="537" \
-DSTM32F405xx \
-DSTM32F40_41xxx \
-DUSE_STDPERIPH_DRIVER \
CMN_AFLAGS = --cpu Cortex-M4.fp.sp -g --apcs=interwork \
-I D:\Software\Keil\pack\Keil\STM32F4xx_DFP\2.13.0\Drivers\CMSIS\Device\ST\STM32F4xx\Include \
--pd "__UVISION_VERSION SETA 537" --pd "STM32F405xx SETA 1" \
LINK_SCRIPT := AdvanceClock.sct
CMN_LFLAGS = --cpu Cortex-M4.fp.sp \
--strict --scatter $(LINK_SCRIPT)
# compiler objects
OBJECTS = $(addprefix $(BUILD_OBJ_DIR)/,$(notdir $(C_SOURCE:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCE)))
OBJECTS += $(addprefix $(BUILD_OBJ_DIR)/,$(notdir $(ASM_SOURCE:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCE)))
$(OUT_DIR)/$(TARGET).axf: $(OBJECTS)
@$(ECHO) Link $@ ...
@$(LINK_EXEC) $(OBJECTS) $(CMN_LFLAGS) -o $@ --map --info totals --list_mapping_symbols --list=$(OUT_DIR)/$(TARGET).map
@$(FROMELF_EXEC) --bin --output $(OUT_DIR)/$(TARGET).bin $@
$(BUILD_OBJ_DIR)/%.o : %.s Makefile | $(BUILD_OBJ_DIR)
@$(ECHO) 'Compiling' $<
@$(ASM_EXEC) $(CMN_AFLAGS) $< -o $@
# compiler process
$(BUILD_OBJ_DIR)/%.o : %.d Makefile | $(BUILD_DEP_DIR)
$(BUILD_OBJ_DIR)/%.o : %.c Makefile | $(BUILD_OBJ_DIR)
@$(ECHO) 'Compiling' $<
@$(CC_EXEC) -c $(CMN_CFLAGS) $< -o $@
$(BUILD_DEP_DIR)/%.d : OBJ_BNAME=$(basename $(notdir $@))
$(BUILD_DEP_DIR)/%.d : %.c Makefile | $(BUILD_DEP_DIR)
$(CC_EXEC) -M $(CMN_CFLAGS) $< -o $(BUILD_OBJ_DIR)/$(OBJ_BNAME).o > $@
$(CC_EXEC) -c $(CMN_CFLAGS) $< -o $(BUILD_OBJ_DIR)/$(OBJ_BNAME).o
$(OUT_DIR):
mkdir $@
$(BUILD_OBJ_DIR):
mkdir $@
$(BUILD_DEP_DIR):
mkdir $@
# dwonload
flash:
$(DOWNLOAD_EXEC) -c -SE 0 4 -V "while_programming" -P $(OUT_DIR)/$(TARGET).bin 0x08000000
$(DOWNLOAD_EXEC) -Rst
# clean
clean:
$(RM) obj/*
$(RM) out/*
1.1 makefile 变量
简单接着上面的makefile,看一下变量的定义,变量的引用需要加$符号,建议加()或者{}
- = 为常见的变量赋值,左侧是变量,右边是变量的值,值得说的是:右侧变量的值可以是后面定义的值。
# makfile_test.mak
Target = $(Target_def)
Target_def = AdvancedClock
all:
@echo $(Target)
13952@DESKTOP-91JBMMS MINGW64 /d/Workspace/DesignProject/AdvancedClock/Advanced-Clock/Advanced Clock Project/Advanced Clock1027/Advanced Clock Project/build (feature/code-clear)
$ make -f makfile_test.mak
AdvancedClock
但是其也有一个不好的地方,假如两个变量互相赋值,则会造成make编译比较慢,但幸好的是make可以检测这样的错误
# makfile_test.mak
Target = $(Target_def)
Target_def = AdvancedClock
Dependency = $(Module)
Module = $(Dependency)
all:
@echo $(Target)
@echo $(Module)
$ make -f makfile_test.mak
makfile_test.mak:8: *** Recursive variable `Module' references itself (eventually). Stop.
上面那种错误就可以:= 解决。
- :=,其赋值只是是前面定义好的值,如果没有定义,那么该值就是空,相当于没有赋值。
Target = $(Target_def)
Target_def = AdvancedClock
Dependency := $(Module)
Module := $(Dependency)
all:
@echo $(Target)
@echo $(Module)
$ make -f makfile_test.mak
AdvancedClock
- ?= 如果该变量没有赋值,就赋值成右值,否则,就不赋值
Target = $(Target_def)
Target_def = AdvancedClock
Dependency := $(Module)
Module := $(Dependency)
Target ?= 123456
all:
@echo $(Target)
@echo $(Module)
$ make -f makfile_test.mak
AdvancedClock
如果变量没有定义,那么就赋值,采用=赋值,所以只要定义过,就可以被赋值
#Target = $(Target_def)
Target_def = AdvancedClock
Dependency := $(Module)
Module := $(Dependency)
Target ?= $(Compiler)
Compiler := armcc
all:
@echo $(Target)
@echo $(Module)
$ make -f makfile_test.mak
armcc
- += 如果之前没有被定义,就采用=赋值,如果之前是:=赋值,那么就是:+=,就是采用:=的赋值,如果之前是+=,那么之后就采用+=赋值。
Compiler 为:=赋值,+= armclang之后,继续+= 不存在的变量,结果就无法添加,说明其属性延续上面的:=
#Target = $(Target_def)
Target_def = AdvancedClock
#$(info HOMEPATH=$(HOMEPATH))
#$(info HOMEDRIVE=$(HOMEDRIVE))
Dependency := $(Module)
Module := $(Dependency)
Target ?= $(Compiler)
Compiler := armcc
Compiler += armclang
Compiler += $(cc)
cc = gcc
all:
@echo $(Target)
@echo $(Module)
@echo $(Compiler)
$ make -f makfile_test.mak
armcc armclang
armcc armclang
对比上面,Compiler=赋值,所以其+=之后,可以加上cc的变量的值
#Target = $(Target_def)
Target_def = AdvancedClock
#$(info HOMEPATH=$(HOMEPATH))
#$(info HOMEDRIVE=$(HOMEDRIVE))
Dependency := $(Module)
Module := $(Dependency)
Target ?= $(Compiler)
Compiler = armcc
Compiler += armclang
Compiler += $(cc)
cc = gcc
all:
@echo $(Target)
@echo $(Module)
@echo $(Compiler)
$ make -f makfile_test.mak
armcc armclang gcc
armcc armclang gcc
关注DIR变量,如果其为定义,则按=处理,可以+上后面的定义的变量
#Target = $(Target_def)
Target_def = AdvancedClock
#$(info HOMEPATH=$(HOMEPATH))
#$(info HOMEDRIVE=$(HOMEDRIVE))
Dependency := $(Module)
Module := $(Dependency)
Target ?= $(Compiler)
Compiler = armcc
Compiler += armclang
Compiler += $(cc)
cc = gcc
DIR += $(C_DIR)
C_DIR = /123
all:
@echo $(Target)
@echo $(Module)
@echo $(Compiler)
@echo $(DIR)
$ make -f makfile_test.mak
armcc armclang gcc
armcc armclang gcc
D:/Software/MinGW/msys/1.0/123
- 变量的高级用法,多个变量后缀统一替换
src_file = 1.c 2.c 3.c
object = $(src_file:%.c=%.o)
all:
@echo $(object)
$ make -f makfile_test.mak
1.o 2.o 3.o
- 变量的嵌套
A = $(B)
B = C
C = hello
D = $($(A))
all:
@echo $(D)
$ make -f makfile_test.mak
hello
- 环境变量
1.2 makefile 关键字
1.3 makefile 自动推导
2、ST-Link命令行烧录
对于ST的芯片来说,其支持ST-Link烧录(走SWD协议)同时也支持JLink,其他厂商的芯片通常都是用JLink(支持Jtag协议来烧录)。
使用命令行来进行烧录,完全就是为了自动化进行,图形界面毕竟需要自己去点,
2.1 常见的界面烧录方式
常见的烧录方式比如
- keil IDE 烧录
- IAR IDE 烧录
- STLINK-Unity 图形界面烧录
- Ozone 调试软件烧录
- JFlash 去烧录
2.1 STLink CLI 命令行烧录方式
废话不多说,直接上命令行的烧录方式。
ST-LINK_CLI.exe -c -SE 0 4 -V "after_programming" -P out/AdvancedClock.bin 0x08000000
- -c:连接指令,命令格式:-c [ID=/SN=] [JTAG/SWD] [FREQ=] [UR/HOTPLUG] [LPM]
ST-LINK_CLI.exe -c HOTPLUG
ST-LINK SN: 213E03002C135737334D4E00
ST-LINK Firmware version: V2J38S7
Connected via SWD.
SWD Frequency = 4000K.
Target voltage = 3.2 V
Connection mode: **HotPlug**
Reset mode: Software reset
Device ID: 0x413
Device flash Size: **1024 Kbytes**
Device family: **STM32F405xx/F407xx/F415xx/F417xx**
连接后可以看到:连接后的MCU的 Flash Size、MCU的类型,以及MCU的ID等。
- -SE:命令格式:-SE <开始sector> <结束sector>。擦除sector,每个sector 16K,地址从0x0800 0000开始,所以0表示第0个sector,0 4表示擦除从sector 0到sector 4。所以总的擦除大小就是16K*5=80K,具体的大小可根据bin的大小来定。
Memory Sector @0x08000000 erased
Memory Sector @0x08004000 erased
Memory Sector @0x08008000 erased
Memory Sector @0x0800C000 erased
Memory Sector @0x08010000 erased
笔者系列的STM32系列为405,Flash空间为1MB。
- -V:该参数表示编程成功后进行校验是否正确写入,可跟参数:
- -P:命令格式:-P <bin文件路径> <写入地址>
还有其他的命令,
- -CoreReg:读取MCU寄存器,
$ ST-LINK_CLI.exe -c HOTPLUG -Halt -CoreReg
STM32 ST-LINK CLI v3.6.0.0
STM32 ST-LINK Command Line Interface
ST-LINK SN: 213E03002C135737334D4E00
ST-LINK Firmware version: V2J38S7
Connected via SWD.
SWD Frequency = 4000K.
Target voltage = 3.2 V
Connection mode: HotPlug
Reset mode: Software reset
Device ID: 0x413
Device flash Size: 1024 Kbytes
Device family: STM32F405xx/F407xx/F415xx/F417xx
R0 = 0x00000000
R1 = 0x00000001
R2 = 0x00000082
R3 = 0x20000094
R4 = 0x40013000
R5 = 0x00000007
R6 = 0x00000000
R7 = 0x00000000
R8 = 0x00000000
R9 = 0x00000000
R10 = 0x08009C60
R11 = 0x00000000
R12 = 0x00000000
R13 = 0x2001A510
R14 = 0x08003C1F
APSR = 0x60000000
IPSR = 0x00000000
EPSR = 0x01000000
MSP = 0x2001A510
PSP = 0x00000000
XPSR = 0x61000000
PC = 0x08003C16
- -Halt:停住CPU,但是笔者多次尝试,显示有日志,但是程序继续运行
$ ST-LINK_CLI.exe -c HOTPLUG -Halt
STM32 ST-LINK CLI v3.6.0.0
STM32 ST-LINK Command Line Interface
ST-LINK SN: 213E03002C135737334D4E00
ST-LINK Firmware version: V2J38S7
Connected via SWD.
SWD Frequency = 4000K.
Target voltage = 3.2 V
Connection mode: HotPlug
Reset mode: Software reset
Device ID: 0x413
Device flash Size: 1024 Kbytes
Device family: STM32F405xx/F407xx/F415xx/F417xx
- -r8:命令格式:-r8 <地址> <长度>
$ ST-LINK_CLI.exe -c HOTPLUG -r8 0x08000000 0x32
STM32 ST-LINK CLI v3.6.0.0
STM32 ST-LINK Command Line Interface
ST-LINK SN: 213E03002C135737334D4E00
ST-LINK Firmware version: V2J38S7
Connected via SWD.
SWD Frequency = 4000K.
Target voltage = 3.2 V
Connection mode: HotPlug
Reset mode: Software reset
Device ID: 0x413
Device flash Size: 1024 Kbytes
Device family: STM32F405xx/F407xx/F415xx/F417xx
0x08000000 : 68 A5 01 20 1D 03 00 08 F9 2B 00 08 11 27 00 08
0x08000010 : 41 2B 00 08 B9 21 00 08 19 4D 00 08 00 00 00 00
0x08000020 : 00 00 00 00 00 00 00 00 00 00 00 00 AF 3C 00 08
0x08000030 : B5 23
- -ME:全部擦除Flash。