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を開く
01 02 03 04 05 06 07 08 09 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 | 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を開く
01 02 03 04 05 06 07 08 09 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 | 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++のバージョン情報を指定する
1 2 3 | set (CMAKE_CXX_STANDARD 17) # C++17を使用 set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_EXTENSIONS OFF) |
対応言語にCXXを追加する
1 2 | # Enable CMake support for ASM and C languages enable_language(C CXX ASM) |
target_sourcesに.cppファイルを追加
.cppはプロジェクトに追加しただけではC++コンパイラでコンパイルされないので、ここだけは手動対応が必要。
.cファイルは自動で対応される。
1 2 3 4 | # ソースファイルを追加(.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
:スレッドセーフな静的変数の初期化を無効化。
1 2 | # 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++コンパイラのみがコンパイルできる場所に記述する。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #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」の値を変更したい場合などは以下のようにする必要がある。
1 2 3 4 5 6 7 8 9 | #include "sample.hpp" #include <new> void func_c(int val) { sampleClass *s = new sampleClass();//インスタンス生成 s->cpp_var = 1;//メンバ変数の値を変更 delete s;//メモリ解放 } |
コメント