一、目的
本文是为了记录使用 stm32CubeMX
、arm-none-eabi-gcc
和 vscode
配置 arm 的 C++ 环境。
参考了下面的两篇文章。
- 在arm-gcc的环境下使用C/C++混合编程开发stm32 - 哔哩哔哩 (bilibili.com)
- (31条消息) STM32 C++编程系列一:STM32 C++编程介绍c++开发stm32江雨潇潇下的博客-CSDN博客
二、CubeMX 基本配置
有两点记要:
- SYS 的 Debug 选项
不然只能下载一次程序。
- Toolchain 工具链
要选择 Makefile
三、VScode 配置
使用 vscode 打开项目后,按下 <Ctrl+Shift+P>
,选择 C/C++ Edit Configurations (UI)
,进入 C/C++ 的基本配置。
3.1 配置编译类型
Compiler path
编译器路径,我选择了 gcc
IntelliSense mode
应该是智能提示,我电脑是 win10 故选了 windows-gcc-arm
3.2 对比 makefile 来配置
因为 C/C++ 的项目配置比较灵活,其查找的头文件路径要手动提示给编译器。故还需要告诉智能提示怎样找到头文件和一些宏定义。
Include path
头文件查找路径,要对照 Makefile 文件里的 C_INCLUDES
注意的是,要把 -I
和后面 \
去掉。(-I
是给编译器用的, \
是 make 里取消换行)
Defines
宏定义,HAL 库 使用的宏。其在 Makefile 里的位置是,
完成上面的配置后,基本就可把 vscode 当成 IDE使用了(还差些插件,如cortex-debug之类,可以网上查找)。
四、C++ 的入口
网上有些介绍怎样使用 C++ 里,是直接把 main.c 文件改成 main.cpp 。这种方法可取,但是有一个弊端。如果你写写下代码,突然想使用 cube 工具修改一些配置,其就会再生成一个 main.c 文件,且里面没有了你之前写的逻辑代码。因为你的逻辑代码还在 main.cpp 里。故只好又把代码搬到 c 中,再修改 c 文件后缀为 cpp。
这多麻烦啊。于是,我取纳了另一种方法。定义一个 C++ 的入口函数,再又 c 代码来调用他。
下面举例。
在项目的 Core
里分别定义了两个文件:
- cpp_start.h 为 C++ 提供给 C 的接口文件
- start.cpp 为 C++ 的入口函数的实现
其中,cpp_start.h 的内容如下:
#ifndef __CPP_START_H__
#define __CPP_START_H__
#ifdef __cplusplus
extern "C" {
#endif
int cpp_start();
#ifdef __cplusplus
}
#endif
#endif
因为如果使用 g++ 来编译程序,编译器自身会定义了一个宏 __cplusplus
。不同语言之间,若想互相通信,互相调用,就要有相同类型的数据转换。而 C++ 自身是兼容 C 的,所以其调用 C 的函数或变量均是很容易。但是返过来就不一样了。
以函数为例,C++为了支持重载,其编译的函数名与我们定义的是不一样的,均包含有形参类型。例如
// 这个例子是举例,与实际名称是有差别。
// 现只是近似说明
void print(int a, int b); // -> print_int_int()
但是 C 对于函数名称的编译是很单纯的,你给什么,我就使用什么。
因此,这里是为了告诉编译,别改我名字,好不。
这样接口就实现了,那么你就可以在 start.cpp 里任意使用 C++ 的乐趣了。
五、makefile 的修改
接下来,到了很关键的一步,也是最容易错的一样,修改 Makefile 文件。
5.1 增加 C++ 文件资源
5.2 增加编译方式
5.3 增加编译的 flags
在 CFLAGS
的基础上,增加 -fno-rtti
和 -fno-exceptions
。表示不使用 rtti 和 异常功能。因为这两者会增加编译后的体积。
5.3 增加object
以 C 为模板,把原来的 .c
改成 .cpp
,C_SOURCES
改成 CPP_SOURCES
。
同时一定要注意,此处 OBJECTS
后接的是 +=
。且,不能放在 # list of ASM program objects
之后。
5.4 增加 C++ 文件的编译规则
5.5 改写 clean 指令(只限 win 系统)
此命令为删除 build 文件夹下的中间文件。
5.6 完整如下
##########################################################################################################################
# File automatically-generated by tool: [projectgenerator] version: [3.18.0-B7] date: [Sun Apr 16 20:59:25 CST 2023]
##########################################################################################################################
# ------------------------------------------------
# Generic Makefile (based on gcc)
#
# ChangeLog :
# 2017-02-10 - Several enhancements + project update mode
# 2015-07-22 - first version
# ------------------------------------------------
######################################
# target
######################################
TARGET = study-5
######################################
# building variables
######################################
# debug build?
DEBUG = 1
# optimization
OPT = -Og
#######################################
# paths
#######################################
# Build path
BUILD_DIR = build
######################################
# source
######################################
# C++ sources
CPP_SOURCES = \
Core/Src/start.cpp
# C sources
C_SOURCES = \
Core/Src/main.c \
Core/Src/gpio.c \
Core/Src/stm32f1xx_it.c \
Core/Src/stm32f1xx_hal_msp.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio_ex.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim_ex.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc_ex.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_pwr.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash_ex.c \
Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_exti.c \
Core/Src/system_stm32f1xx.c
# ASM sources
ASM_SOURCES = \
startup_stm32f103xe.s
#######################################
# 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
CPP = $(GCC_PATH)/$(PREFIX)g++
else
CC = $(PREFIX)gcc
AS = $(PREFIX)gcc -x assembler-with-cpp
CP = $(PREFIX)objcopy
SZ = $(PREFIX)size
CPP = $(PREFIX)g++
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 = \
-DUSE_HAL_DRIVER \
-DSTM32F103xE
# AS includes
AS_INCLUDES =
# C includes
C_INCLUDES = \
-ICore/Inc \
-IDrivers/STM32F1xx_HAL_Driver/Inc \
-IDrivers/STM32F1xx_HAL_Driver/Inc/Legacy \
-IDrivers/CMSIS/Device/ST/STM32F1xx/Include \
-IDrivers/CMSIS/Include
# 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)"
CPP_FLAGS = $(CFLAGS) -fno-rtti -fno-exceptions
#######################################
# LDFLAGS
#######################################
# link script
LDSCRIPT = STM32F103VETx_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 C++ objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(CPP_SOURCES:.cpp=.o)))
vpath %.cpp $(sort $(dir $(CPP_SOURCES)))
# list of ASM program objects
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) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
$(BUILD_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR)
$(CPP) -c $(CPP_FLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.cpp=.lst)) $< -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
#######################################
clean:
# -rm -fR $(BUILD_DIR)
rmdir /s/q $(BUILD_DIR)
#######################################
# dependencies
#######################################
-include $(wildcard $(BUILD_DIR)/*.d)
# *** EOF ***
六、遇过的错误
遇过的错误大都和 Makefile 有关。
6.1 路径书写错误
make: *** No rule to make target 'build/start.o', needed by 'build/study-5.elf'. Stop.
这个错误里的 build/start.o ,指的是 Core/Src/start.cpp
这个文件没找到,因此编译不出 build/start.o
这个规则来。
这个规则是在
这里产生的。
形成这个错误的主要原因在于,你的 CPP_SOURCES
的路径书写出错 。
# 错误形式
# C++ sources
CPP_SOURCES = \
Core\Src\start.cpp
# 正确的应为
CPP_SOURCES = \
Core/Src/start.cpp
而我错的原因是使用 vscode 右键文件时的选项 Copy Relative Path
,因为其复制的是 win 的路径风格。
6.2 编译器选择错误
g++ -c -mcpu=cortex-m3 -mthumb -DUSE_HAL_DRIVER -DSTM32F103xE -ICore/Inc -IDrivers/STM32F1xx_HAL_Driver/Inc -IDrivers/STM32F1xx_HAL_Driver/Inc/Legacy -IDrivers/CMSIS/Device/ST/STM32F1xx/Include -IDrivers/CMSIS/Include -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2 -MMD -MP -MF"build/start.d" -fno-rtti -fno-exceptions -Wa,-a,-ad,-alms=build/start.lst Core/Src/start.cpp -o build/start.o
g++: warning: '-mcpu=' is deprecated; use '-mtune=' or '-march=' instead
g++: error: unrecognized command line option '-mthumb'; did you mean '-mtbm'?
make: *** [Makefile:181: build/start.o] Error 1
一开始,我对其中的 -mcpu=
百思不得其解,因为其他人的 makefile 也是这样用的,为何我的就不行。
后来我细看才发现 .cpp 的编译器使用了 g++,而不是 arm-none-eabi-g++。
另外,还会出现一种情况
CPP 的编译规则里,忘记改成 CPP
了。
6.3 链接错误
d:/programfiles/armgcc/12.2 rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld.exe: d:/programfiles/armgcc/12.2 rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/thumb/v7-m/nofp\libc_nano.a(libc_a-closer.o): in function `_close_r':
closer.c:(.text._close_r+0xc): warning: _close is not implemented and will always fail
d:/programfiles/armgcc/12.2 rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld.exe: d:/programfiles/armgcc/12.2 rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/thumb/v7-m/nofp\libc_nano.a(libc_a-lseekr.o): in function `_lseek_r':
lseekr.c:(.text._lseek_r+0x10): warning: _lseek is not implemented and will always fail
d:/programfiles/armgcc/12.2 rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld.exe: d:/programfiles/armgcc/12.2 rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/thumb/v7-m/nofp\libc_nano.a(libc_a-readr.o): in function `_read_r':
readr.c:(.text._read_r+0x10): warning: _read is not implemented and will always fail
d:/programfiles/armgcc/12.2 rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld.exe: d:/programfiles/armgcc/12.2 rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/thumb/v7-m/nofp\libc_nano.a(libc_a-writer.o): in function `_write_r':
writer.c:(.text._write_r+0x10): warning: _write is not implemented and will always fail
d:/programfiles/armgcc/12.2 rel1/bin/../lib/gcc/arm-none-eabi/12.2.1/../../../../arm-none-eabi/bin/ld.exe: warning: build/study-5.elf has a LOAD segment with RWX permissions
这个是什么原因,我也不太清楚。反正我重装了 arm-gcc
就解决了。
6.4 只编译 C++ 的文件,不会编译 C 的文件
这种情况,一般是 OBJECTS
改错了。
这里的 OBJECTS
后要跟 +=
。不然,会覆盖了原来 C
的内容。