概要
この記事ではSTM32CubeMXを用いて生成したプロジェクトで、C++を使用する方法を紹介します。
CubeMXで生成したプロジェクトは全てC言語で書かれていますが、少し編集すればC++も使えるようになります。
準備
この記事のようにCubeMXを使ってプロジェクトを生成してください。
僕はVSCodeを使っていますが、CubeIDEでも大丈夫だと思います。
CMakeListsの編集
まず。CMakeLists.txtをC++が使えるように修正します。
C++のバージョンは17を指定してますが、11とか14とかでも良いと思います。
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)
+ 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 StandardProjectTest)
# 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)
+ enable_language(C CXX ASM)
# Core project settings
- project(${CMAKE_PROJECT_NAME})
+ project(${CMAKE_PROJECT_NAME} C CXX ASM)
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
)
C言語からC++コードの呼び出し
CubeMXで生成された全てはC言語で記述されているので、そこからC++コードを呼び出せるようにする必要があります。
この方法はいろいろあると思いますが、その中の一例を示します。今回の方法はSingltonクラスをC言語から呼び出せるようにラッパ関数を用意しました。新しいフォルダApp
、App/Inc
、App/Src
を作成し、その下にApp/Inc/AppMain.hpp
、App/Inc/AppWrapper.hpp
、App/Src/AppMain.cpp
、App/Src/AppMainWrapper.cpp
のファイルを4つ作成します。
main.h
/*
* 略
*/
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
#define B1_Pin GPIO_PIN_13
#define B1_GPIO_Port GPIOC
#define USART_TX_Pin GPIO_PIN_2
#define USART_TX_GPIO_Port GPIOA
#define USART_RX_Pin GPIO_PIN_3
#define USART_RX_GPIO_Port GPIOA
#define LD2_Pin GPIO_PIN_5
#define LD2_GPIO_Port GPIOA
#define TMS_Pin GPIO_PIN_13
#define TMS_GPIO_Port GPIOA
#define TCK_Pin GPIO_PIN_14
#define TCK_GPIO_Port GPIOA
#define SWO_Pin GPIO_PIN_3
#define SWO_GPIO_Port GPIOB
/* USER CODE BEGIN Private defines */
+ extern UART_HandleTypeDef huart2; // huart2をAppMain.cpp内で使いたい場合に宣言
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
+ #include "AppMainWrapper.hpp"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
+ AppMainWrapper_Init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
+ AppMainWrapper_Update();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* 略
*/
CmakeLists.txt
#
# 略
#
# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add user sources here
+ App/Src/AppMain.cpp
+ App/Src/AppMainWrapper.cpp
)
# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined include paths
+ App/Inc
)
#
# 略
#
App/Inc/AppMain.hpp (新しくファイル追加)
#pragma once
#include <stdint.h>
#include <stm32f4xx_hal.h>
class AppMain
{
//===DEFINES===//
//===CONSTANTS===//
private:
//===VARIABLES===//
private:
//===FUNCTIONS===//
public:
static AppMain &GetInstance();
void Init();
void Update();
private:
AppMain();
~AppMain();
AppMain(const AppMain &other);
AppMain &operator=(const AppMain &other);
};
App/Inc/AppMainWrapper.hpp (新しくファイル追加)
#pragma once
#include <stm32f4xx_hal.h>
#ifdef __cplusplus
extern "C"
{
#endif
void AppMainWrapper_Init(void);
void AppMainWrapper_Update(void);
#ifdef __cplusplus
}
#endif
App/Src/AppMain.cpp (新しくファイル追加)
#include "AppMain.hpp"
#include "main.h"
#include "stm32f4xx_hal.h"
AppMain &AppMain::GetInstance()
{
static AppMain instance;
return instance;
}
AppMain::AppMain()
{
}
AppMain::~AppMain()
{
}
void AppMain::Init()
{
// 初期化処理
}
void AppMain::Update()
{
// メインループ
}
App/Src/AppMainWrapper.cpp (新しくファイル追加)
#include "AppMainWrapper.hpp"
#include "AppMain.hpp"
void AppMainWrapper_Init(void)
{
AppMain::GetInstance().Init();
}
void AppMainWrapper_Update(void)
{
AppMain::GetInstance().Update();
}
C++コードの実装
上記の設定ができていれば、C++のコードはAppMainクラス内に記述することができます。
AppMainクラス以外に別のクラスを作成し、AppMainクラスから呼び出すことも可能です。このとき、新しいソースファイル(.cpp)を追加した場合は、CMakeLists.txtにソースファイルを追加するのを忘れないようにしてください。
AppMainクラス内では、以下のようにHALライブラリを使用することも、もちろん可能です。
void AppMain::Update()
{
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
HAL_Delay(1000);
}
※("NUCLEO-F446RE"のデフォルトピン設定)
まとめ
今回は自身の備忘録として、STM32CubeMXを用いて生成したプロジェクトで、C++を使用する方法を記事にしました。
同じような作業をする人の参考になれば幸いです。