VSCodeでSTM32の開発するためのメモ(Windows+CubeMX+Makefile+VSCodeでSTM32のデバッグ)

# やりたい事
CubeMXでmakefileプロジェクトとして生成
↓
VSCode上で編集
↓
VSCode上でコンパイル
↓
VSCode上でマイコンに書き込み
↓
VSCode上でデバッグ  

これらをWindows上で実現します。
今回は環境変数やレジストリを編集しないような方法で環境を構築します。

今回の記事ではデバッガにST-linkではなくJ-linkを使用しています。
[こちら](https://www.segger.com/products/debug-probes/j-link/models/other-j-links/st-link-on-board/)に書かれている方法でnucleoやDiscoveryなどの評価ボードに内蔵されているST-linkをJ-link化することができます。
J-linkの動作に必要なソフトは[こちら](https://www.segger.com/downloads/jlink/#J-LinkSoftwareAndDocumentationPack)からダウンロードしてインストールしておいてください。
ST-linkを使用する場合はOpenOCDなどのツールを使用します。

# 初回のみ必要な作業(使用するツール等のインストール)  
※VSCode・CubeMX・ST-linkのドライバ・J-linkのドライバ等のインストール方法については省略します。

インストールといっても今回は環境変数を変更しないため、以下のツールを適当なフォルダに展開するだけです。

## ツール
- [GNU ARM Embedded Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads)
- [OpenOCD](https://sourceforge.net/projects/openocd/files/openocd/0.10.0/) 　(ST-linkを使用する場合　J-linkを使用する場合は不要)
- [Make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm)
- [LibIntl for Windows](http://gnuwin32.sourceforge.net/packages/libintl.htm)
- [LibIconv for Windows](http://gnuwin32.sourceforge.net/packages/libiconv.htm)  

こいつらを`C:\tools`などの任意のフォルダ以下に展開にします。
下3つのツールはWindowsでmakeコマンドを使えるようにするためのものです。
(今回はmakeだけ使えればよいのでmake単体をインストールしました。GNU Toolsが既にインストールされている場合はそちらを使用することもできます。)
最新版のバージョンはこの記事を書いているときと変わるかもしれませんが、その場合は適宜パスを最新版に合わせて読み替えてください。

場所(例)

- `C:\tools\gcc-arm-none-eabi-9-2019-q4-major-win32\bin`
- `C:\tools\OpenOCD\0.10.0-12-20190422-2015\bin`
- `C:\tools\make-3.81-bin\bin`
- `C:\tools\make-3.81-bin\bin\libintl3.dll` 
- `C:\tools\make-3.81-bin\bin\libiconv2.dll`  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/663d617a-c6ee-3c2e-bb25-2ca85cac64e9.png)



## 拡張機能
VSCodeに以下の拡張機能をインストールします。

- C/C++ 
- Cortex-Debug  

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/a8e0399a-2194-1d09-2fd4-b9f284d42a29.png)


# プロジェクトごとに必要な設定  
次に新しいプロジェクトを作成するたびに設定する内容について説明します。

## CubeMXの設定
CubeMXのProject Managerのタブの中にある`Toolchain / IDE`の部分を`Makefile`に変更します。
それ以外は通常のSTM32のプロジェクトを作るときと同様に設定し、最後に`GENERATE CODE`を押します。
`USER CODE BEGIN`と`USER CODE END`の間にコードを書けば、自分が書いたコードに影響せずにCubeMX上で変更した内容がコードに反映されるという機能はVSCodeでも使用することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/8f263f1e-6541-2d07-75da-ebd26f55c99e.png)

## VSCodeの設定
CubeMXで生成したプロジェクトのあるフォルダをVSCodeで開きます。
初期状態では`Inc`や`Src`などのフォルダ、`makefile`や`***.ioc`などのファイルが生成されていると思います。

VSCodeでは開いたフォルダがプロジェクトとして認識され、開いたフォルダの中にある`.vscode`というフォルダにプロジェクトの設定が保存されます。

STM32の開発を行うための設定も`.vscode`内に記述していくので、`.vscode`というフォルダが無ければ作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401306/17fd8919-f6d9-ee16-5c67-bae0b74f4cda.png)

### .vscode\tasks.json (ビルドタスクの設定)  
`.vscode`内に`tasks.json`というファイルを作成し、以下の内容を記述して保存します。

ここではVSCodeのビルドタスクの設定をします。

`Build project`というタスクでプロジェクトのコンパイルができるように設定をしています。
`Clean project`というタスクではコンパイルによって生成されたアイテムを全て削除するように設定をしています。

※以下補足
`GCC_PATH`にはGNU ARM Embedded Toolchainのパスを指定します。
`options`の`env`ではこのタスク内でWindowsの環境変数に一時的に追加するパスの設定をしています。今回はmakeコマンドを使いたいので`Make for Windows`をインストールしたパスを指定します。(Windowsの設定で環境変数を編集しmakeにパスを通した場合はこの設定は不要です。)
CubeMXで生成されたmakefieは初期設定では`Build`というフォルダ内にコンパイルしたデータを吐き出すような設定になっています。

```json
{
    "version": "2.0.0",
    "type": "shell",
    "echoCommand": true,
    "tasks": [
        {
            "label": "Build project",
            "command": "make",
            "args": [
                "all",
                "GCC_PATH=C:\\tools\\gcc-arm-none-eabi-9-2019-q4-major-win32\\bin"
            ],
            "options": {
                "env": {
                    "PATH": "C:\\tools\\make-3.81-bin\\bin"
                }
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        },
        {
            "label": "Clean project",
            "command": "powershell -Command Remove-Item",
            "args": [
                "${workspaceFolder}\\build",
                "-Recurse"
            ],
            "problemMatcher": [
                "$gcc"
            ],
            "group": "build"
        }
    ]
}
```


### .vscode\launch.json (デバッグの設定)  
`.vscode`内に`launch.json`というファイルを作成し、以下の内容を記述して保存します。

ここではVSCodeのデバッグの設定をします。

以下はST-linkとOpenOCDを用いてNucleo F4ボードのデバッグをする場合の設定の一例です。

`preLaunchTask`には`tasks.json`で設定したプロジェクトのコンパイルを実行するタスクを指定します。
`armToolchainPath`にはGNU ARM Embedded Toolchainのパスを指定します。
`serverpath`にはOpenOCDのパスを指定します。

```json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Cortex Debug",
            "cwd": "${workspaceRoot}",
            "executable": "./build/${workspaceFolderBasename}.elf",
            "request": "launch",
            "preLaunchTask": "Build project",
            "type": "cortex-debug",
            "servertype": "openocd",
            "configFiles": [
                "board/st_nucleo_f4.cfg"
            ],
            "armToolchainPath": "C:\\tools\\gcc-arm-none-eabi-9-2019-q4-major-win32\\bin\\",
            "serverpath": "C:\\tools\\OpenOCD\\0.10.0-12-20190422-2015\\bin\\openocd.exe"
        }
    ]
}
```

#### STM32F4単体の場合
`configFiles`の部分を以下のように変更します。
STM32F0シリーズやF2シリーズなどを使用する場合もマイコンの型番に合わせてこの部分を変更します。

```json
            "configFiles": [
                "interface/stlink-v2-1.cfg",
                "target/stm32f4.cfg"
            ],
```

#### J-linkを使用する場合
J-linkを使用する場合は以下のような設定をします。
`device`で使用するマイコンの設定をします。  

```json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Cortex Debug",
            "cwd": "${workspaceRoot}",
            "executable": "${workspaceFolder}\\build\\${workspaceFolderBasename}.elf",
            "request": "launch",
            "type": "cortex-debug",
            "preLaunchTask": "Build project",
            "armToolchainPath": "C:\\tools\\gcc-arm-none-eabi-9-2019-q4-major-win32\\bin\\",
            "serverpath": "C:\\Program Files (x86)\\SEGGER\\JLink\\JLinkGDBServerCL.exe",
            "servertype": "jlink",
            "device": "STM32F446RE",
            "interface": "swd"
        }
    ]
}
```


### ソースファイルの追加 (Makefileの編集)  
CubeIDEのようなIDEと違い、VSCodeではソースファイルを追加しても自動でコンパイル対象に追加される仕組みは無いので、手動で設定をする必要があります。  

ソースファイルを追加したら`Makefile`の`source`と書かれた場所に以下の例を参考に追加したファイルの設定を追加します。
次の行に続く場合は`\`を最後につけ忘れないようにしましょう。
`$(wildcard Core/Src/*.c) \`のように特定のフォルダ以下のファイルを全て一気に追加することもできます。

```makefile
(前略 だいたい35行目くらいにsourceの項目があるはず)
######################################
# source
######################################
# C sources
C_SOURCES =  \
自分で追加したソースファイル1.c \
自分で追加したソースファイル2.c \
自分で追加したソースファイル3.c \
Core/Src/main.c \
Core/Src/stm32f4xx_it.c \
Core/Src/stm32f4xx_hal_msp.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c \
(以下省略)
```

# ビルドの実行
ビルドの実行は`Ctrl`+`Shift`+`B`を押してビルドタスクを選択して実行します。

# デバッグの実行
デバッグの実行は`F5`を押して実行します。
正しく設定ができていればマイコンへの書き込みが行われ、ブレークポイントなどの機能を使うこともできるはずです。

# 便利な機能

## 使用率の表示
RAMやROMの使用率を表示するには以下の設定をしておくと便利。
`Makefile`内の`LDFLAGS`の末尾に`,-print-memory-usage`を追加する。

```makefile
# libraries
LDFLAGS = $(MCU) -specs=nano.specs <中略> ,-print-memory-usage
```

## printfでfloatの表示
`printf`で`float`を使う場合は次の設定を行う。
`Makefile`内の`LDFLAGS`に `-u_printf_float`を追加する。

```makefile
# libraries
LDFLAGS = $(MCU) -specs=nano.specs -u_printf_float -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections,-print-memory-usage

```


## .vscode\c_cpp_properties.json  
必須ではないが設定しておくとintellisenseが機能するようになるので便利。 

```json
{
    "configurations": [
        {
            "name": "STM32",
            "includePath": [
                "${workspaceFolder}/**",
                "C:\\tools\\gcc-arm-none-eabi-9-2019-q4-major-win32\\arm-none-eabi\\include**",
                "C:\\tools\\gcc-arm-none-eabi-9-2019-q4-major-win32\\lib\\gcc\\arm-none-eabi\\9.2.1\\include"
            ],
            "defines": [
                "USE_HAL_DRIVER",
                "STM32F446xx",
                "__weak=__attribute__((weak))"
            ],
            "compilerPath": "C:\\tools\\gcc-arm-none-eabi-9-2019-q4-major-win32\\bin\\arm-none-eabi-gcc",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}
```