STM32 + makefileでC++に対応してみる(全てg++でコンパイルをしてしまう方法)

2021年8月2日

STM32マイコンのペリフェラル関連記事を一覧にまとめました。

こんにちは。そらです。

STM32でROSSerialを対応させたときに、makefileでC++に対応させることをしました。そのときに、少しはまったこともあったので忘備録として書いていきます。

この対応方法でHAL,LLAPIを問わずにC++に対応が可能です。




対応させていく

変更点について

基本的には、gccでコンパイルしているところをg++に変更してC++のコンパイルをできるようにしていけばOKです。STM32CUBEMXで出力されるソースコードはC++でコンパイルすることを考えられているようで、マクロでg++でコンパイルするときにc言語としてコンパイルするようにされています。したがって、gccでcファイルをコンパイルしてg++でcppファイルをコンパイルしてg++でリンクするという方法でも、g++ですべてをコンパイルするという方法をとっても大丈夫そうだということがわかります。

makefileを改造する上では後者のほうが簡単なのでg++ですべてをコンパイルするということをしていきたいと思います。

makefileを書き換える

binariesのところを以下のように変更します

#######################################
# 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)g++
AS = $(GCC_PATH)/$(PREFIX)g++ -x assembler-with-cpp
CP = $(GCC_PATH)/$(PREFIX)objcopy
SZ = $(GCC_PATH)/$(PREFIX)size
else
CC = $(PREFIX)g++
AS = $(PREFIX)g++ -x assembler-with-cpp
CP = $(PREFIX)objcopy
SZ = $(PREFIX)size
endif
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S

変更点についてのところで書いた通り、gccをg++にそのまま変更しました。

続いて、sourceのところにcppのソースを追加していきます

# CPP sources
CPP_SOURCES = 
$(wildcard Src/*.cpp)

c++のときのコンパイルのflag設定などをしていきます。compile gcc flagsにc++用のフラグを立てたり、includeのパス設定を行います。

CXX_INCLUDES = 

# compile gcc flags
ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections -std=c++11

CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections -std=c++11

CXXFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(CXX_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections -std=c++11

次に、build the applicationでcpp objectの追加を行いましょう。

# list of CPP program 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)))
# list of C program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))

$(BUILD_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR) 
	$(CC) -c $(CXXFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.cpp=.lst)) $< -o $@

$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 
	$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@

$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
	$(AS) -c $(CFLAGS) $< -o $@

これで、コンパイルが通ると思いきや、newlib周りのシステムコールがないと怒られてしまいコンパイルができませんでした。

対策として、libnosysというnewlibに同梱されているリンカオプションを設定することで問題がでないようにします。このリンク設定をするにあたって、LDFLAGSとLIBに-lnosysと–specs=nosys.specsを追加しましょう。

私の環境でのLDFLAGS周りを以下に示しておきます。

# link script
LDSCRIPT = STM32F405RGTx_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 -u _printf_float --specs=nosys.specs

ここで、–specs=nosys.specsは、syscalls.cで定義されている関数と同じ関数を中でライブラリとして持っているため、syscalls.cがある場合はmultiple definitionのエラーがでる可能性があります。その場合は、syscalls.cを消してあげればOKです。

さいごに

STM32のmakefileの環境でC++にで対応してみました。ROSSerialの対応はstm32_rosserialのGitHubのREADMEを読みながら、ところどころmakefileに変更して上手くやればできると思います。よきマイコン生活を!