僕が考えた最強のSTM32開発環境構築 (Windows)


目次

はじめに

普段はRaspberry Pi PicoやXIAO ESP32C3などをPlatformIOで開発している私ですが,platformIOで対応してないSTM32の石の開発にはCubeIDEを使っていました.

しかし,VSCodeのモダンな環境に比べると組み込み系のIDEはまだまだ使いづらいと感じています.

そこで,STM32の開発環境をVSCodeを中心に構築してみました.

大雑把に以下の流れで開発します.

  1. ペリフェラルの初期化コードはSTM32CubeMXで生成する
  2. VSCodeで中身をC++で書く
  3. CMake+Ninjaでビルドする
  4. probe-rsでデバッグする

環境・バージョンなど

  • Windows 11 Home 24H2
  • Dell Inspiron 5425
  • STM32CubeMX 6.14.1
  • Arm GNU Toolchain 14.2.Rel1 (Build arm-14.52)
  • CMake 4.1.0
  • Ninja 1.13.1
  • (cargo 1.89.0)

必要なものを入れる

Arm GNU Toolchainのインストール

ここから自分の環境にあったものをダウンロードします.私の環境はWindows 64bitなのでarm-gnu-toolchain-XX.X.rel1-mingw-w64-x86_64-arm-none-eabi.zipをダウンロードしました.

次に,展開したzipファイルを適当な場所(例えばC:\tools)に展開します.

そして,展開した場所/arm-gnu-none-eabi/binにPathを通します.

openocdのインストール

ここからダウンロードしたものを使いました.

Arm GNU Toolchainと同様に,展開してPathを通します.

CMake,Ninjaのインストール

ビルドのために使うCMakeとNinjaを入れます.Windowsの場合はwingetで簡単に入ります.

Terminal window
winget install cmake ninja-build.ninja

VSCodeに必要なプラグインを入れる

最低限以下のプラグインを入れておけば大丈夫です.

  • C/C++
  • Cortex Debug

加えて,CMake用のプラグインを入れておくと便利です.

  • CMake Tools

STM32CubeMXでコードを生成する

今回はSTM32G441KBTxをターゲットにします.

プロジェクトの作成

まずは,ピン割り当てやクロックなどの設定を変更します.今回は,クロックとFreeRTOSの設定を変更しました.

ピン割り当ての設定など

次に,CubeMXのProject Managerから,以下の項目を設定します.

  • Project
    • Application Structure
      • Advanced
    • Toolchain/IDE
      • CMake
  • Code Generator
    • STMCube MCU packages and embedded software packs
      • Copy only the necessary library files
    • Generated files
      • Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral
      • Keep User Code when re-generating
      • Deletepreviously generated files when not re-generated

Project Managerの設定

ここまで来たら,GENERATE CODEをクリックし,コードを生成します.

VSCodeでプロジェクトを取り込む

以下のようなディレクトリ構造になっているはずです.

├─cmake
│ └─stm32cubemx
├─Core
│ ├─Inc
│ ├─Src
│ └─Startup
├─Drivers
│ ├─CMSIS
│ └─STM32G4xx_HAL_Driver
└─Middlewares
└─Third_Party
└─FreeRTOS
.cproject
.mxproject
.project
CMakeLists.txt
CMakePresets.json
startup_stm32g441xx.s
STM32G441XX_FLASH.ld

CMakeの設定

.vscode/settings.jsonを以下のように編集します.

.vscode/settings.json
{
"cmake.cmakePath": "cmake",
"cmake.preferredGenerators": [
"Ninja"
]
}

保存したら,左下にある⚙Buildをクリックします.すると,以下のような画面になるので,Debugを選択します.

CMakeのpresetの選択

CubeMXが吐いたCMakeのままだと,cppファイルなどを追加しても反映されないです. それだと不便なので,cmake/stm32cubemx/CMakeLists.txtを書き換えます.

cmake/stm32cubemx/CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
# Enable CMake support for ASM and C languages
enable_language(C ASM)
# STM32CubeMX generated symbols (macros)
set(MX_Defines_Syms
USE_HAL_DRIVER
STM32G441xx
$<$<CONFIG:Debug>:DEBUG>
)
# STM32CubeMX generated include paths
set(MX_Include_Dirs
${CMAKE_SOURCE_DIR}/Core/Inc
${CMAKE_SOURCE_DIR}/Drivers/STM32G4xx_HAL_Driver/Inc
${CMAKE_SOURCE_DIR}/Drivers/STM32G4xx_HAL_Driver/Inc/Legacy
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/include
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F
${CMAKE_SOURCE_DIR}/Drivers/CMSIS/Device/ST/STM32G4xx/Include
${CMAKE_SOURCE_DIR}/Drivers/CMSIS/Include
)
# STM32CubeMX generated application sources
file(GLOB MX_Application_Src
${CMAKE_SOURCE_DIR}/Core/Src/*.c
${CMAKE_SOURCE_DIR}/Core/Src/*.cpp
${CMAKE_SOURCE_DIR}/startup_stm32g441xx.s
)
list(REMOVE_ITEM MX_Application_Src
${CMAKE_SOURCE_DIR}/Core/Src/system_stm32g4xx.c
)
# STM32 HAL/LL Drivers
file(GLOB STM32_Drivers_Src
${CMAKE_SOURCE_DIR}/Core/Src/system_stm32g4xx.c
${CMAKE_SOURCE_DIR}/Drivers/STM32G4xx_HAL_Driver/Src/*.c
)
# Drivers Midllewares
file(GLOB FreeRTOS_Src
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/*.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c
${CMAKE_SOURCE_DIR}/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c
)
# Link directories setup
set(MX_LINK_DIRS
)
# Project static libraries
set(MX_LINK_LIBS
STM32_Drivers
FreeRTOS
)
# Interface library for includes and symbols
add_library(stm32cubemx INTERFACE)
target_include_directories(stm32cubemx INTERFACE ${MX_Include_Dirs})
target_compile_definitions(stm32cubemx INTERFACE ${MX_Defines_Syms})
# Create STM32_Drivers static library
add_library(STM32_Drivers OBJECT)
target_sources(STM32_Drivers PRIVATE ${STM32_Drivers_Src})
target_link_libraries(STM32_Drivers PUBLIC stm32cubemx)
# Create FreeRTOS static library
add_library(FreeRTOS OBJECT)
target_sources(FreeRTOS PRIVATE ${FreeRTOS_Src})
target_link_libraries(FreeRTOS PUBLIC stm32cubemx)
# Add STM32CubeMX generated application sources to the project
target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${MX_Application_Src})
# Link directories setup
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE ${MX_LINK_DIRS})
# Add libraries to the project
target_link_libraries(${CMAKE_PROJECT_NAME} ${MX_LINK_LIBS})
# Add the map file to the list of files to be removed with 'clean' target
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES ADDITIONAL_CLEAN_FILES ${CMAKE_PROJECT_NAME}.map)
# Validate that STM32CubeMX code is compatible with C standard
if((CMAKE_C_STANDARD EQUAL 90) OR (CMAKE_C_STANDARD EQUAL 99))
message(ERROR "Generated code requires C11 or higher")
endif()

編集箇所は,MX_Application_SrcとSTM32_Drivers_Src,FreeRTOS_Srcに値をセットするところです.wildcardで指定するようにしています.

Core/Src/system_stm32g4xx.cがMX_Application_Srcではなく,STM32_Drivers_Srcに含まれるので注意して下さい.

Intellisenseの設定をする

VSCode側にinclude pathを設定することで,補完をいい感じにします.

.vscode/c_cpp_properties.json
{
"configurations": [
{
"name": "STM32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/**/**"
],
"defines": [
"USE_HAL_DRIVER",
"STM32G441xx",
"__weak=__attribute__((weak))"
],
"compilerPath": "arm-none-eabi-gcc",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}

ビルドタスクの設定

次に,ビルド&書き込みを自動化するための設定をします.tasks.json

  • ninjaでビルドするタスク
  • openocdで書き込むタスク

を用意して,書き込みを行うタスクのdependOnにビルドのタスクを追加します.こうすることで,書き込むときに自動的にビルドも行うようにしてくれます.

なお,g441test.elfの部分はCubeMXのプロジェクト名によって変わります.(今回はプロジェクト名をg441testにした)

.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "ninja",
"args": [
"-C",
"./build/Debug",
],
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": false
}
},
{
"label": "flash ( OpenOCD + CMSIS-DAP )",
"type": "shell",
"command": "openocd",
"args": [
"-f",
"interface/cmsis-dap.cfg",
"-f",
"target/stm32g4x.cfg",
"-c",
"'program ./build/Debug/g441test.elf; reset; exit'"
],
"dependsOn":[
"build"
],
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

Debugの設定

デバッグの設定も行います.デバッグの設定はlaunch.jsonに書きます.

rttConfigを設定するのがポイントで,これによってSEGGER RTTの出力をターミナルから確認することができます.

tasks.jsonと同様にg441test.elfの部分はCubeMXのプロジェクト名によって変わります.

launch.json
{
"configurations": [
{
"cwd": "${workspaceRoot}",
"executable": "./build/Debug/g441test.elf",
"name": "openocd with cmsis-dap",
"request": "launch",
"type": "cortex-debug",
"servertype": "openocd",
"configFiles": [
"interface/cmsis-dap.cfg",
"target/stm32g4x.cfg"
],
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"label": "RTT Console",
"port": 0,
"type": "console"
}
]
},
"searchDir": [],
"runToEntryPoint": "main",
"showDevDebugOutput": "none"
}
]
}

Gitの設定をする

コードを再生成したときに上書きされてしまうと面倒なため,Gitのrepoもこの段階でつくってしまいます.

.gitignoreを編集します.

.gitignore
*.launch
Debug/*
Release/*
.settings/*
.mxproject
.cproject
.project
build

次に,左のGitのタブのInitialize RepositoryをクリックしRepoを作ります.

Initialize Repository

全部のファイルを追加しておきます.

Terminal window
git add .

SEGGER-RTTを使えるようにする

RTT(Real-Time Transfer)は、SEGGER社のJ-Linkデバッガが提供するリアルタイムでのデータ転送機能です。

UARTなどによるprintfと比較して

  • TAG/SWD経由でデータ転送するため書き込み回路以外要らない1
  • CPUの実行を止めずにログを出力可能2
  • 通信速度が圧倒的に速い3
  • デバッグ版と本番環境で同じコードを使える4

といった利点があります.

リアルタイム性が求められる場合は特にこのメリットが効いてくるため,私はよく使っています.

本家のファイルをコピーする

GitHubから以下の三つのファイルをコピーする

設定用ファイルをコピーする

更にSEGGER RTTの設定用ファイルCore/Inc/SEGGER_RTT_Conf.h配置します.

メモリの量に応じて95行目のBUFFER_SIZE_UPの大きさを変えるとよいです.

初期化処理を追加

Core/Src/main.cに以下を追加します.

Core/Src/main.c
...
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "SEGGER_RTT.h"
/* USER CODE END Includes */
...
int main(void)
{
/* USER CODE BEGIN 1 */
SEGGER_RTT_Init();
/* USER CODE END 1 */
...

使い方

SEGGER_RTT.hをincludeすればSEGGER_RTT_printf()を使えるようになります.例えば,printf("a=%d",a)に相当することをしたいなら,以下のように書けばOKです.

SEGGER_RTT_printf(0,"a=%d",a);

色を付けることもできます.

SEGGER_RTT_printf(0,"[%sinfo%s] System initialization started\n", RTT_CTRL_TEXT_BRIGHT_GREEN, RTT_CTRL_RESET);

実行結果↓

↑のコードの実行結果

使い方

ビルド

左下にある⚙Buildをクリックすると,ビルドできます.

Buildを実行している様子

ビルド & 書き込み

Ctrl+Shift+Bを押すと,ninjaでビルドした後openocdで書き込みします.

タスクを実行している様子

デバッグ

F5を押すとデバッグが開始されます.やはり,RTTがとても便利です.

デバッグの様子

最後に

以上の設定で,そこそこ快適にSTM32の開発できるようになったのではないかと思ってます.

CMakeを使っているのでTinyUSBなども簡単に追加できそうです.そこら辺は今後試してみたいと思います.

上記設定を適用例をGitHubにあげてあるので,それらのリンクを張っておきます.

Footnotes

  1. https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/#requirements

  2. https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/#internal-structures

  3. https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/#rtt-performance

  4. https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/#target-implementation