前言 为什么要引入C++ 在单片机开发领域,C语言无疑是占大头的,由于C语言执行迅速,编译体积小巧且逻辑清晰,相比其他语言有了十足的优势
但是…没错但是,当一个工程稍微复杂起来的时候,你是否经历过因为名称被占用而不得不绞尽脑汁想一个合法名称的窘境?或是在一堆相似的函数里迷失自我?亦或是苦恼于自己不想被他人使用的工具函数暴露在外?对于C来说,想要解决这些问题就很麻烦了,于是乎,我引入C++的理由也就来了
对于一段有效的C程序,它同时可以几乎不加修改便能在C++下运行,且C++引入了名称空间,可以很好的解决命名冲突、重复的问题,且最重要的,C++有类的特性,这在大型项目开发中可以节约大量的时间,且结构清晰明了,也许写一个类很烦躁,但使用一个类却非常容易,而且,基于类的编写方式让程序在各个平台上的移植变得非常容易,此外,我还能列举出引入C++许多别的理由 (主要是比纯C逼格更高)
也许你担心引入C++后会占用更多原本就宝贵的单片机资源,但经过我的测试,这个”多”占用的部分实际上很少很少,在编译器的加持下,与C源码编译后的结果相差无几
好了,吹了半天C++,现在让我们来进入正题
准备工作 在开始之前你需要的是什么?当然是一个STM32的开发环境,没有的话赶紧去用CubeMX生成一个,这里我准备了一个F103C8T6的HAL库工程
对于MDK工程方式 对于MDK开发方式来说引入C++是很简单的,只需要在工程设置里关闭使用MicroLIB
,然后再将你的源文件从*.c
重命名为*.cpp
就行了,别忘了对原有的库包含使用extern "C"
关键字 所需要做的一切就是这些,非常的简单,如果你的主要开发方式是MDK,那么,你可以退出了
对于MakeFile工程方式 厌倦了MDK古早的界面和奇奇怪怪的附属包?我就喜欢轻量级的MakeFile工程,配合armgcc
工具链,在VsCode编辑,使用openOCD等工具下载,优雅~
但是,将它更改为C++编译方式就比较麻烦了,不过完成后用得很爽也是真的….
总的来说,有下面四个步骤
处理工程目录
处理源文件
处理MakeFile
让我们一步一步来
1.处理工程目录 原本工程目录下的文件都是.c
文件,我们需要将Core
目录下的C源文件与我们之后的C++源文件分开放置便于管理,在这里我选择新建了一个文件夹C
,并将原本的Inc
与Src
文件夹放到新建的C
文件夹内
与此同时,在C
同级目录下新建CPP
文件夹,里面与C
文件夹内相同,需要新建一个Inc
与Src
文件夹
再然后,于CPP/Src
内添加real_main.cpp
,于CPP/Inc
内添加real_main.hpp
,为什么要新建这两个文件?既然都打算使用C++编写了,当然要将main函数写在C++源文件下嘛,至于为什么起这个名…..你也可以改,那是你的自由
处理完后成后的工程目录长这样:
2.处理源文件 首先是对Core/C/Inc/main.h
的处理,它是main.c
的重要依赖文件,也是与HAL库连接的入口,我们需要把原本放置在extern"C"
内的内容释放出来,为了简洁,我删掉了CubeMX生成的注释,真正有效的内容只有这么一点点 然后,我们在Core/CPP/Inc/real_main.hpp
内添原本位于main.c
内的需要用到的函数原型并包含main.h
,当然,不要忘记extern "C"
处理好的real_main.hpp
长这样:
顺便,一定要去确认下位于main.c
下的MX_GPIO_Init()
函数定义是否包含了准确的GPIO区,在生成空GPIO定义的工程者某些特殊情况下,CubeMX可能会漏掉开启某些GPIO区域的操作,需要自己手动补上
最后,当然是把main函数整个从main.c
里删掉,搬到real_main.cpp
啦~~ 看起来非常简洁,比起原来的main.c
一堆注释舒服多了
处理好的real_main.cpp
长这样:
3.处理MakeFile 这里是需要处理改动最多的地方,有整整7处需要修改,不过基本也就照猫画虎复制来复制去就行了,我在这里贴一份我改好的MakeFile, 借助搜索功能搜索1./2./3./~~~~7.
来快速预览对应的修改处吧
[点击展开MakeFile]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 TARGET = target DEBUG = 1 OPT = -Og BUILD_DIR = build C_SOURCES = \ Core/C/Src/main.c \ Core/C/Src/stm32f1xx_it.c \ Core/C/Src/stm32f1xx_hal_msp.c \ Core/C/Src/system_stm32f1xx.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 CXX_SOURCES = \ Core/CPP/Src/real_main.cpp ASM_SOURCES = \ startup_stm32f103xb.s BINPATH = PREFIX = arm-none-eabi- CXX = $(BINPATH) $(PREFIX) g++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) sizeelse CC = $(PREFIX) gcc AS = $(PREFIX) gcc -x assembler-with-cpp CP = $(PREFIX) objcopy SZ = $(PREFIX) sizeendif HEX = $(CP) -O ihex BIN = $(CP) -O binary -S CPU = -mcpu=cortex-m3 MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI) AS_DEFS = C_DEFS = \ -DUSE_HAL_DRIVER \ -DSTM32F103xB AS_INCLUDES = C_INCLUDES = \ -ICore/C/Inc \ -IDrivers/STM32F1xx_HAL_Driver/Inc \ -IDrivers/STM32F1xx_HAL_Driver/Inc/Legacy \ -IDrivers/CMSIS/Device/ST/STM32F1xx/Include \ -IDrivers/CMSIS/Include CXX_INCLUDES = \ -ICore/CPP/Inc ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections CXXFLAGS = -lstdc++ $(CFLAGS) $(CXX_DEFS) $(CXX_INCLUDES) -g -ggdb3 -fno-rtti -fno-exceptions \ -fverbose-asm -fdata-sections -ffunction-sections -fpermissive -Wa,-ahlms=$(BUILD_DIR) /$(notdir $(<:.cpp=.lst) )ifeq ($(DEBUG) , 1) CFLAGS += -g -gdwarf-2endif CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" LDSCRIPT = STM32F103C8Tx_FLASH.ld LIBS = -lc -lm -lnosys LIBDIR = LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR) /$(TARGET) .map,--cref -Wl,--gc-sectionsall: $(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 $(CXX_SOURCES:.cpp=.o) ))vpath %.cpp $(sort $(dir $(CXX_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) -Wa,-a,-ad,-alms=$(BUILD_DIR) /$(notdir $(<:.c=.lst) ) $< -o $@ $(BUILD_DIR) /%.o: %.cpp Makefile | $(BUILD_DIR) $(CXX) -c $(CXXFLAGS) $< -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: -rm -fR $(BUILD_DIR) -include $(wildcard $(BUILD_DIR) /*.d)
然后,只需要简单的make指令即可完成整个工程的构建 如果需要添加新的源文件,记得向MakeFile添加对应的文件索引,配合bat脚本食用更佳哦
至此,一个优雅的C++与C混合编译的STM32工程就构建完成了,enjoy!!!
相关参考资料 MakeFile修改参考 工程修改参考