STM32マイコンの開発環境をSTM32CubeIDEからVSCodeへ移行した。小規模なコントローラーを作るときはC言語のみで作っていたが、F4シリーズを使ったり、別のプロジェクトから移植するときにクラスが使えず・・・。ToolchainにはC++のコンパイラが入っているのになぜC++がコンパイルできないんだ?ということで、なんとかしてみたい。
環境前提
ソフトウェア | バージョン |
---|---|
VSCode | 1.97.2 |
STM32 VS Code Extention | 2.1.1 |
STM32CubeMX | 6.13.0 |
CMake(VSCode Extention) | 0.0.17 |
プロジェクト構成
VSCodeでSTM32プロジェクトを構築する際は、以下の順序でツールをいじってテンプレートを作成する。
CMakeLists.txtを編集、C++に対応させる
CMakeがプロジェクトファイルを認識し、コンパイル指示を出している。それを設定するCMakeList.txtがプロジェクト内に出来上がっている。これを以下のように編集することでCコンパイラとC++コンパイラをファイル拡張子で切り替えつつ呼び出すことができる。
変更前:CMakeLists.txt
変更前のCMakeLists.txtを開く
cmake_minimum_required(VERSION 3.22)
#
# This file is generated only once,
# and is not re-generated if converter is called multiple times.
#
# User is free to modify the file as much as necessary
#
# Setup compiler settings
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
# Define the build type
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
endif()
# Set the project name
set(CMAKE_PROJECT_NAME VFD_NTP_Clock)
# Include toolchain file
include("cmake/gcc-arm-none-eabi.cmake")
# Enable compile command to ease indexing with e.g. clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
# Enable CMake support for ASM and C languages
enable_language(C ASM)
# Core project settings
project(${CMAKE_PROJECT_NAME})
message("Build type: " ${CMAKE_BUILD_TYPE})
# Create an executable object type
add_executable(${CMAKE_PROJECT_NAME})
# Add STM32CubeMX generated sources
add_subdirectory(cmake/stm32cubemx)
# Link directories setup
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined library search paths
)
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add user sources here
)
# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined include paths
)
# Add project symbols (macros)
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined symbols
)
# Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
stm32cubemx
# Add user defined libraries
)
変更後:CmakeLists.txt
変更後のCmakeLists.txtを開く
cmake_minimum_required(VERSION 3.22)
#
# This file is generated only once,
# and is not re-generated if converter is called multiple times.
#
# User is free to modify the file as much as necessary
#
# Setup compiler settings
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
set(CMAKE_CXX_STANDARD 17) # C++17を使用
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Define the build type
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
endif()
# Set the project name
set(CMAKE_PROJECT_NAME VFD_NTP_Clock)
# Include toolchain file
include("cmake/gcc-arm-none-eabi.cmake")
# Enable compile command to ease indexing with e.g. clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
# Enable CMake support for ASM and C languages
enable_language(C CXX ASM)
# Core project settings
project(${CMAKE_PROJECT_NAME})
message("Build type: " ${CMAKE_BUILD_TYPE})
# C++ コンパイルオプション(STM32向け最適化)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-exceptions -fno-rtti -fno-threadsafe-statics")
# Create an executable object type
add_executable(${CMAKE_PROJECT_NAME})
# Add STM32CubeMX generated sources
add_subdirectory(cmake/stm32cubemx)
# Link directories setup
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined library search paths
)
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add user sources here
Core/Src/test.cpp
)
# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined include paths
)
# Add project symbols (macros)
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined symbols
)
# Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
stm32cubemx
# Add user defined libraries
)
変更のポイント
C++のバージョン情報を指定する
set(CMAKE_CXX_STANDARD 17) # C++17を使用
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
対応言語にCXXを追加する
# Enable CMake support for ASM and C languages
enable_language(C CXX ASM)
target_sourcesに.cppファイルを追加
.cppはプロジェクトに追加しただけではC++コンパイラでコンパイルされないので、ここだけは手動対応が必要。
.cファイルは自動で対応される。
# ソースファイルを追加(.cppファイルをここに追加)
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
Core/Src/test.cpp # C++ ファイルを追加
)
CMAKE_CXX_FLAGSでコンパイルオプションを追加する
-fno-exceptions
:C++ の例外 (try-catch
) を無効化(STM32 では通常使わない)。-fno-rtti
:RTTI(Run-Time Type Identification)を無効化(通常は不要)。-fno-threadsafe-statics
:スレッドセーフな静的変数の初期化を無効化。
# C++ コンパイルオプション(STM32向け最適化)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-exceptions -fno-rtti -fno-threadsafe-statics")
これでC++ファイル(.cpp / .hpp)がコンパイルできるようになる。
ヘッダ・ソースファイルのC/C++対応
プロジェクトすべてをCやC++どちらかに統一できるのであれば、以下はあまり問題にならないが、STM32CubeMXの吐き出すコードはCだが、自作ライブラリはC++ということもあるだろう(今回のケースもこれ)。
そうしたとき、CからC++を呼び出すときにクラス定義などC++特有のコードを識別できずコンパイルエラーになってしまう。その解決方法を記す。
ヘッダファイル
C言語と同様にインクルードガードを設定し、多重読み込みを防止する。
その後、C言語で使用する変数・関数定義を「C言語の定義である」ことを明示する。
C++言語用の定義は、C++コンパイラのみがコンパイルできる場所に記述する。
#ifndef __SAMPLE_HPP //インクルードガード
#define __SAMPLE_HPP
#ifdef __cplusplus //GCCでコンパイルするときは__cplusplusが定義されている
extern "C" //この命令でこの内部定義はC言語のもとの分かる C言語コンパイラは認識できないので上のifdefで場合分けする
{
#endif
void func_c(int);//C言語用関数定義
evtern int c_var;//C言語用変数定義
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
class sampleClass
{
public:
void func_cpp();
int cpp_var;
private:
void func_cpp_pv();
int cpp_ver_pv;
};
#endif //__SAMPLE_HPP
ソースファイル
.cppのソースファイルはC++コンパイラしか読み込み・コンパイルを行わないので、ヘッダファイルのような読み分け処理は不要。
しかし、C言語はC++言語のクラスを利用できないのでC++用関数をロードできない。例えばCの関数「func_c」の引数でCPPのクラスメンバ変数「cpp_var」の値を変更したい場合などは以下のようにする必要がある。
#include "sample.hpp"
#include <new>
void func_c(int val)
{
sampleClass *s = new sampleClass();//インスタンス生成
s->cpp_var = 1;//メンバ変数の値を変更
delete s;//メモリ解放
}
コメント