为了简单上手,我所知的有两种方法获得makefile文件。
#工程的名称及最后生成文件的名字
TARGET = project_name
# Build path
BUILD_DIR = build
#######################################
# 编译器参数设置
#######################################
# 选择自己的编译器版本
# GCC_PATH=
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
#读取当前工作目录
TOP=$(shell pwd)
#设定包含文件目录
INC_FLAGS= -I $(TOP)/Core/CMSIS/Include \
-I $(TOP)/Core/CMSIS/Device/ST/STM32F10x/Include \
-I $(TOP)/Hardware \
-I $(TOP)/Core/STM32F10x_StdPeriph_Driver/inc \
-I $(TOP)/Driver \
-I $(TOP)/User
#######################################
# ASM sources
# 注意,如果使用.c的startup文件,请把下面两句注释掉,避免编译出现错误
#######################################
ASM_SOURCES = \
startup_stm32f103xe.s
#######################################
# CFLAGS
#######################################
# debug build?
DEBUG = 1
# 代码优化级别
OPT = -Og
# cpu
CPU = -mcpu=cortex-m3
# fpu
# NONE for Cortex-M0/M0+/M3
# float-abi
# mcu
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)
# C defines
# 这个地方的定义根据实际情况来,如果在代码里面有定义(如#define STM32F10X_HD),这里可以不写
C_DEFS = \
-D USE_STDPERIPH_DRIVER \
-D STM32F10X_HD
# PreProcess
CFLAGS = $(MCU) $(C_DEFS) $(INC_FLAGS) $(OPT) -std=gnu11 -W -Wall -fdata-sections -ffunction-sections
ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif
# 在$(BUILD_DIR)目录下生成依赖关系信息,依赖关系以.d结尾
CFLAGS += -MMD -MP -MF"$(addprefix $(BUILD_DIR)/, $(notdir $(@:%.o=%.d)))"
#######################################
# LDFLAGS
#######################################
# LD文件
LDSCRIPT = STM32F103ZETx_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
#######################################
# C/CPP源代码
#######################################
C_SRC = $(shell find ./ -name '*.c')
C_SRC += $(shell find ./ -name '*.cpp')
C_OBJ = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SRC:%.c=%.o)))
vpath %.c $(sort $(dir $(C_SRC)))
C_OBJ += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))
.PHONY: all clean update
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
# 从vpath中读取所有的.c文件,编译成.o文件
$(BUILD_DIR)/%.o: %.c | $(BUILD_DIR)
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) -o $@ $<
# 从vpath中u读取所有的.s文件,编译成.o文件
$(BUILD_DIR)/%.o: %.s | $(BUILD_DIR)
$(AS) -c $(CFLAGS) $< -o $@
# 将所有的.o文件依据.ld文件,编译组成.elf文件
$(BUILD_DIR)/$(TARGET).elf: $(C_OBJ)
$(CC) $(C_OBJ) $(LDFLAGS) -o $@
$(SZ) $@
# 将.elf文件转为.hex格式
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf
$(HEX) $< $@
# 将.elf文件转为.bin格式
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf
$(BIN) $< $@
# 用于生成BUILD_DIR目录
$(BUILD_DIR):
mkdir $@
clean:
rm -f $(shell find ./ -name '*.o')
rm -f $(shell find ./ -name '*.d')
rm -f $(shell find ./ -name '*.lst')
rm -f $(shell find ./ -name '*.map')
rm -f $(shell find ./ -name '*.elf')
rm -f $(shell find ./ -name '*.bin')
rm -f $(shell find ./ -name '*.hex')
update:
openocd -f interface/jlink.cfg -f target/stm32f1x.cfg -c init -c halt -c "flash write_image erase $(TOP)/$(TARGET).hex" -c reset -c shutdown
ld文件也就是编译链,这个在GCC交叉编译中比较常见。如下给出一个例子,需要修改的地方不多,主要是修改Memory这一块,要根据自己的芯片来。对于STM32芯片,只要少量修改都能做到通用
/*
*****************************************************************************
**
** File : LinkerScript.ld
**
** Abstract : Linker script for STM32F103ZETx Device with
** 512KByte FLASH, 64KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
** (c)Copyright Ac6.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Ac6 permit registered System Workbench for MCU users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the System Workbench for MCU toolchain.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
/*
* 这个地方需要自己修改,_estack也就是RAM的最高地址,是RAM.ORIGIN+RAM.ORIGIN.LENGTH相加的结果
* 在这里表示为 0x20000000+(Dec2Hex)64*1024=0x20010000
*/
_estack = 0x20010000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
/*
* Memory的大小和具体的芯片相关,可以看STM的选型手册
*/
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K
}
/* Define output sections */
/*
* 这里是ld脚本的SECTIONS命令,指明了各个.o文件在单片机中如何存放,如果不了解,不要修改
*/
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.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
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
startup文件是一段用汇编写的文件,极少去修改它。目前也有用C语言写的版本,大同小异。一般不用管就行。只要在编译的时候.s编译成了.o文件,剩下的事情交给编译器按照ld文件的描述来安排就行了。(详情请见ld文件中的“.isr_vector"和startup中的"isr_vector"
程序运行过程中,按照ld的设计,先运行startup的代码进行初始化,然后在starup中跳转到main函数。如果希望在main函数前进行一些别的初始化操作,那就修改startup文件吧
这里还需要注意的是,不同的编译器startup文件会有所不同,一定要找对应编译器所支持的startup文件。startup文件和ld文件配合使用。这个在官网有提供
如果遇到无法调试的问题,可以参考我的文章
openocd的使用问题汇总
如果仍然有问题无法调试,可以给我留言
使用vscode开发,需要关注几个点
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"cwd": "${workspaceRoot}",
"executable": "./build/ZET6-base-develop.elf", //注意修改自己的elf文件
"request": "launch",
"type": "cortex-debug",
"servertype": "openocd",
"device": "STM32F103ZE", //这个不是很重要,写不写应该无所谓
"configFiles": [
"interface/jlink.cfg",
"target/stm32f1x.cfg"
]
}
]
}
PlatformIO IDE
这个看个人喜好吧,目前我不是很习惯
2. C/C++插件的配置文件
这里面主要关注的是includePath,compilePath
includePath,是为了让C++ Intellisense代码提示工作正常,避免编译的时候知道位置,但是vscode不知道库函数在哪。
compilePath,指定使用的编译器,仍然是为了让C++ Intellisense运行正常,有时候一些类型定义在gcc和arm-none-eabi-gcc中有差异,如果还用gcc,那么vscode满屛红字达成
重定向函数不再是fputc,在GCC下,重写函数_write
int _write(int fd, char *pBuffer, int size)
{
for (int i = 0; i < size; i++)
{
while (!(USART1->SR & USART_SR_TXE))
{
}
USART_SendData(USART1, pBuffer[i]);
}
return size;
}
请参考这个地方的说明:https://blog.csdn.net/zhengyangliu123/article/details/54966402