STM32cubeMX 和 C++ 記要

一、目的

本文是為了記錄使用 stm32CubeMX 、arm-none-eabi-gccvscode 配置 arm 的 C++ 環(huán)境。

參考了下面的兩篇文章。

二、CubeMX 基本配置

有兩點(diǎn)記要:

  • SYS 的 Debug 選項(xiàng)
image.png

不然只能下載一次程序。

  • Toolchain 工具鏈
image.png

要選擇 Makefile

三、VScode 配置

使用 vscode 打開(kāi)項(xiàng)目后,按下 <Ctrl+Shift+P>,選擇 C/C++ Edit Configurations (UI) ,進(jìn)入 C/C++ 的基本配置。

3.1 配置編譯類(lèi)型

image.png

Compiler path 編譯器路徑,我選擇了 gcc

IntelliSense mode 應(yīng)該是智能提示,我電腦是 win10 故選了 windows-gcc-arm

3.2 對(duì)比 makefile 來(lái)配置

因?yàn)?C/C++ 的項(xiàng)目配置比較靈活,其查找的頭文件路徑要手動(dòng)提示給編譯器。故還需要告訴智能提示怎樣找到頭文件和一些宏定義。

image.png

Include path 頭文件查找路徑,要對(duì)照 Makefile 文件里的 C_INCLUDES

image.png

注意的是,要把 -I 和后面 \ 去掉。(-I 是給編譯器用的, \make 里取消換行)

image.png

Defines 宏定義,HAL 庫(kù) 使用的宏。其在 Makefile 里的位置是,

image.png

完成上面的配置后,基本就可把 vscode 當(dāng)成 IDE使用了(還差些插件,如cortex-debug之類(lèi),可以網(wǎng)上查找)。

四、C++ 的入口

網(wǎng)上有些介紹怎樣使用 C++ 里,是直接把 main.c 文件改成 main.cpp 。這種方法可取,但是有一個(gè)弊端。如果你寫(xiě)寫(xiě)下代碼,突然想使用 cube 工具修改一些配置,其就會(huì)再生成一個(gè) main.c 文件,且里面沒(méi)有了你之前寫(xiě)的邏輯代碼。因?yàn)槟愕倪壿嫶a還在 main.cpp 里。故只好又把代碼搬到 c 中,再修改 c 文件后綴為 cpp。

這多麻煩啊。于是,我取納了另一種方法。定義一個(gè) C++ 的入口函數(shù),再又 c 代碼來(lái)調(diào)用他。

下面舉例。

image.png

在項(xiàng)目的 Core 里分別定義了兩個(gè)文件:

  • cpp_start.h 為 C++ 提供給 C 的接口文件
  • start.cpp 為 C++ 的入口函數(shù)的實(shí)現(xiàn)

其中,cpp_start.h 的內(nèi)容如下:

#ifndef __CPP_START_H__
#define __CPP_START_H__ 

#ifdef __cplusplus
extern "C" {
#endif

int cpp_start();

#ifdef __cplusplus
}
#endif

#endif
image.png

因?yàn)槿绻褂?g++ 來(lái)編譯程序,編譯器自身會(huì)定義了一個(gè)宏 __cplusplus。不同語(yǔ)言之間,若想互相通信,互相調(diào)用,就要有相同類(lèi)型的數(shù)據(jù)轉(zhuǎn)換。而 C++ 自身是兼容 C 的,所以其調(diào)用 C 的函數(shù)或變量均是很容易。但是返過(guò)來(lái)就不一樣了。

以函數(shù)為例,C++為了支持重載,其編譯的函數(shù)名與我們定義的是不一樣的,均包含有形參類(lèi)型。例如

// 這個(gè)例子是舉例,與實(shí)際名稱是有差別。
// 現(xiàn)只是近似說(shuō)明
void print(int a, int b);  // -> print_int_int()

但是 C 對(duì)于函數(shù)名稱的編譯是很單純的,你給什么,我就使用什么。

因此,這里是為了告訴編譯,別改我名字,好不。

這樣接口就實(shí)現(xiàn)了,那么你就可以在 start.cpp 里任意使用 C++ 的樂(lè)趣了。

五、makefile 的修改

接下來(lái),到了很關(guān)鍵的一步,也是最容易錯(cuò)的一樣,修改 Makefile 文件。

5.1 增加 C++ 文件資源

image.png

5.2 增加編譯方式

image.png

5.3 增加編譯的 flags

image.png

CFLAGS 的基礎(chǔ)上,增加 -fno-rtti-fno-exceptions 。表示不使用 rtti異常功能。因?yàn)檫@兩者會(huì)增加編譯后的體積。

5.3 增加object

image.png

C 為模板,把原來(lái)的 .c 改成 .cpp,C_SOURCES 改成 CPP_SOURCES。

同時(shí)一定要注意,此處 OBJECTS 后接的是 +=。且,不能放在 # list of ASM program objects 之后。

5.4 增加 C++ 文件的編譯規(guī)則

image.png

5.5 改寫(xiě) clean 指令(只限 win 系統(tǒng))

image.png

此命令為刪除 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 ***

六、遇過(guò)的錯(cuò)誤

遇過(guò)的錯(cuò)誤大都和 Makefile 有關(guān)。

6.1 路徑書(shū)寫(xiě)錯(cuò)誤

make: *** No rule to make target 'build/start.o', needed by 'build/study-5.elf'.  Stop.

這個(gè)錯(cuò)誤里的 build/start.o ,指的是 Core/Src/start.cpp 這個(gè)文件沒(méi)找到,因此編譯不出 build/start.o 這個(gè)規(guī)則來(lái)。

這個(gè)規(guī)則是在

image.png

這里產(chǎn)生的。

形成這個(gè)錯(cuò)誤的主要原因在于,你的 CPP_SOURCES 的路徑書(shū)寫(xiě)出錯(cuò) 。

# 錯(cuò)誤形式
# C++ sources
CPP_SOURCES = \
Core\Src\start.cpp

# 正確的應(yīng)為
CPP_SOURCES = \
Core/Src/start.cpp

而我錯(cuò)的原因是使用 vscode 右鍵文件時(shí)的選項(xiàng) Copy Relative Path,因?yàn)槠鋸?fù)制的是 win 的路徑風(fēng)格。

6.2 編譯器選擇錯(cuò)誤

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

一開(kāi)始,我對(duì)其中的 -mcpu= 百思不得其解,因?yàn)槠渌说?makefile 也是這樣用的,為何我的就不行。

后來(lái)我細(xì)看才發(fā)現(xiàn) .cpp 的編譯器使用了 g++,而不是 arm-none-eabi-g++

image.png

另外,還會(huì)出現(xiàn)一種情況

image.png

CPP 的編譯規(guī)則里,忘記改成 CPP 了。

6.3 鏈接錯(cuò)誤

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

這個(gè)是什么原因,我也不太清楚。反正我重裝了 arm-gcc 就解決了。

6.4 只編譯 C++ 的文件,不會(huì)編譯 C 的文件

這種情況,一般是 OBJECTS 改錯(cuò)了。

image.png

這里的 OBJECTS 后要跟 += 。不然,會(huì)覆蓋了原來(lái) C 的內(nèi)容。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容