VSCode + ARM GNU ToolsでNordicの開発環境を構築する

# はじめに
nRF52840等のNordicのBluetoothモジュールをネイティブSDKで開発する場合、 SEGGER Embedded StudioやKeilなどのIDEを利用するケースが多いです。これらのIDEはモジュールのメモリレイアウトの変更やイメージのフラッシュなどがわかりやすく、便利な開発環境です。

しかし、世の中にはエディタ＋ターミナルで開発したいという欲望に抗えない人たちも少なくありません。この記事はそんな人々への私からのクリスマスプレゼントです。どこかで誰かの役に立ちますように。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167138/5f2bfe7e-5a30-fa1c-8a77-e499246ce868.png)

# 前提

以下の前提で解説します。また、説明が冗長になるのでVSCodeはインストール済みとして解説します。今回説明するOSはwindowsですが、Ubuntuやmacでも構築は可能です。

* OS：windows
* エディタ：VSCode
* ボード：[Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)、[SparkFun Pro nRF52840 Mini](https://www.sparkfun.com/products/15025?_ga=2.246745668.1160114571.1575712962-1448546874.1575594430)
* デバッグプローブ：J-Link
* ケーブル：[1.27mmピッチ・2×5のリボンケーブル](http://akizukidenshi.com/catalog/g/gC-06776/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167138/569335ea-6d6c-2a7c-9627-8294a6728ad0.png)


また、今回はSDKのサンプルのビルド、フラッシュまでの解説をターゲットにしています。nRF5 SDK for Meshに関しては少し構成が違うので、[リンク](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_nrf52840_dk%2FUG%2Fcommon%2Fnordic_tools.html&anchor=nordic_tools)を参照して下さい。本記事の構築ができれば読んで理解できると思います。

# インストール

## make

悲しいですがwindowsはmakeはデフォルトではインストールされていないので、インストールしましょう。

* [Make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm)

## Nordic tools

### [Software Development Kit](https://www.nordicsemi.com/Software-and-tools/Software/nRF5-SDK/Download#infotabs)  
いわゆるSDK。これがないと始まらない。開発ボードによっては使用できるSDKのバージョンに制限があります。（nRF52840はSDK15.0.0以降でないとサポートされていません）各SDKの説明を参照して下さい。（zip配布じゃなくてGithubとかで配布してほしいけど、makeファイルがsdkバージョンをディレクトリ名で使い分けているので厳しそう）

### [nRF Command Line Tools](https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Command-Line-Tools/Download#infotabs)  
nrfjprog、mergehex（後述）といった、バイナリのフラッシュやバイナリとSoftDeviceのマージに必要なNordic純正のコマンドラインツールです。IDEを使用しない場合、makeのターゲットを指定してこれらのコマンドを呼び出す形で開発を進めます。インストール後はコマンドにパスを通しておいて下さい。

## ARM Toolchain

IDEを使用する場合、Nordicが提供する各IDE用のMDKをインストールする必要がありますが、今回は変わりにGNU gccコンパイラを使用します。

* [ARM Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads)

### 注意
windowsは`8-2018-q4-major`バージョンではコンパイルでエラーが発生することが確認されています。ですので、windowsの方は`7-2018-q2-update`、または`8-2019-q3-update`以上のものを使用して下さい。ちなみに私がwindowsで動作確認したのは`7-2018-q2-update`と`8-2019-q3-update`のみです。

Toolchainは以下の三種の神器で構成されています。

* **GNU C/C++ Compiler**  
泣く子も黙るGNUコンパイラ。
* **Binary tools**  
コンパイルされたコードをリンクし、アセンブリコードをコンパイルし、さまざまなファイル変換操作を実行できるツール。
* **Debugging tools**  
GNUプロジェクトデバッガーであるGDBを使用して、コードをステップ実行し、メモリの内容をリアルタイムで表示できます。VSCodeには[Cortex-Debug](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug)というプラグインがあり、J-LinkがあればVSCode上でデバッグできてしまいます。すごい。（というかターミナルとGDBだけでデバッグするのは流石に辛い）

### ARM Toolchainの設定

SDKがToolchainのgccを使用するように設定する必要があります。`[SDK ROOT]/components/toolchain/gcc`にある以下のファイルを修正します。（Windowsの設定例です）

* Windows：Makefile.windows
* Mac and Linux：Makefile.posix

GNU_VERSIONは`arm-none-eabi-gcc --version`で確認できます。

```Makefile.windows
GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/8 2019-q3-update/bin/
GNU_VERSION := 8.3.1
GNU_PREFIX := arm-none-eabi
```
# ボード定義

Adafruit Feather nRF52840 Expressはピン配置がNordicの開発ボードと基本的には互換（利用可能なボタンやLED、GPIOはは少ない）のため、ここは省略して大丈夫です。

Nordicのボードはピンアサインをヘッダファイルで独自に定義できます。（[Using the SDK with other boards](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.3.0%2Fsdk_for_custom_boards.html&resultof=%22%63%75%73%74%6f%6d%22%20%22%62%6f%61%72%64%22%20)）これは逆に、SDKで提供されているピンアサインと異なる各メーカーのブレイクアウト基盤などは、独自のヘッダファイルを用意する必要があります。
SparkFun Electronicsが提供しているSparkFun Pro nRF52840 Miniなどは技適の通っているMDBT50Qを使用しているため、国内で使用できるnRFモジュールの1つです。しかし、ピンアサインが異なるため以下の作業が必要です。

1. ピンアサインヘッダファイルを追加
2. ボード定義ヘッダファイルに1.で作成したピンアサインを含めるようマクロを追記

幸いSparkFun Pro nRF52840 MiniはSparkFun Electronicsがピンアサインヘッダファイルを用意しているため、それを`[SDK ROOT]/components/boards`に追加します。

* [sparkfun_nrf52840_mini.h](https://github.com/sparkfun/nRF52840_Breakout_MDBT50Q/blob/master/Firmware/nRF5_SDK/components/boards/sparkfun_nrf52840_mini.h)

その後、ボード定義にピンアサインのファイルを追加できるよう`[SDK ROOT]components/boards/boards.h`のマクロの`#elif defined (BOARD_…`と記載してある箇所に以下を追記します。（マクロ名とそれに紐づくピンアサインヘッダファイルは揃えておいた方がわかりやすいです）

```boards.h
#elif defined (BOARD_SPARKFUN_NRF52840_MINI)
  #include "sparkfun_nrf52840_mini.h"
#elif defined(BOARD_CUSTOM)
  #include "custom_board.h"
#else
#error "Board is not defined"
```

# VSCodeでIntelliSenseを有効にする

VSCodeを使用する一番の利点はやはりIntelliSenseです。当然有効にします。今回はSDKの中のexampleの各ディレクトリをルートディレクトリとする形式で有効にします。今回はスマートフォンからLEDを点灯、消灯させるサンプルの`[SDK ROOT]/example/ble_peripheral/ble_app_blinky`をルートディレクトリにします。
VSCodeで上記ディレクトリを開いたら、`ctl+sft+p`、`c/cpp: edit configurations`で`c_cpp_properties.json`を作成したら、以下のようにSDKのソースを参照するよう編集します。

`[SDK ROOT]`、`[ARM toolchain ROOT]`にはそれぞれSDKのルートディレクトリへの絶対パス、ARM ToolChainのルートディレクトリへの絶対パスを記載して下さい。

```c_cpp_properties.json
{
    "env": {
        "nrfSDK": "[SDK ROOT]"
    },

    "configurations": [
        {
            "name": "ARMGCC",
            "includePath": [
                "${workspaceFolder}/**",
                "${nrfSDK}/components/**",
                "${nrfSDK}/components",
                "${nrfSDK}/components/libraries/experimental_memobj",
                "${nrfSDK}/components/libraries/experimental_section_vars",
                "${nrfSDK}/modules/nrfx/mdk",
                "${nrfSDK}/modules/nrfx/hal",
                "${nrfSDK}/components/libraries/balloc",
                "${nrfSDK}/components/libraries/experimental_log",
                "${nrfSDK}/components/libraries/experimental_log/src",
                "${nrfSDK}/components/libraries/delay",
                "${nrfSDK}/integration/nrfx",
                "${nrfSDK}/components/libraries/bsp",
                "${nrfSDK}/components/drivers_nrf/nrf_soc_nosd",
                "${nrfSDK}/components/libraries/strerror",
                "${nrfSDK}/components/boards",
                "${nrfSDK}/components/toolchain/cmsis/include",
                "${nrfSDK}/modules/nrfx",
                "${nrfSDK}/components/libraries/util",
                "${nrfSDK}/components/libraries/fifo",
                "${nrfSDK}/components/libraries/uart",
                "${nrfSDK}/integration/nrfx/legacy",
                "${nrfSDK}/components/libraries/delay",
                "${nrfSDK}/modules/nrfx/drivers/include",
                "${workspaceFolder}/sparkfun/blank/config",
                "${workspaceFolder}/sparkfun/blank"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE",
                "BSP_DEFINES_ONLY",
                "CONFIG_GPIO_AS_PINRESET",
                "FLOAT_ABI_HARD",
                "NRF52840_XXAA",
                "DCONFIG_NFCT_PINS_AS_GPIOS"
            ],
            "compilerPath": "[ARM toolchain ROOT]\\7 2018-q2-update\\bin\\arm-none-eabi-gcc.exe",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}
```

この状態でF12などでコードジャンプできるようになっていれば正しく設定できています。

# ボード毎のビルド設定

## ボード毎のディレクトリを作成

NordicのSoCのバージョン毎に使用するSoftDeviceやメモリ容量が異なるため、`pca10056`のように各ボード毎にビルド用ディレクトリがあります。ここで、SparkFun Pro nRF52840 Miniのような公式の開発ボードとピンアサインに互換性の無いカスタムボードを使用する場合、使用されているNordicのSoCに対応したディレクトリをコピーし、ボード定義のマクロを修正します。

SparkFun Pro nRF52840 MiniはSoCにnRF52840を使用しているため、`pca10056`をコピーし、`sparkfun_nrf52840_mini`という名前にします。

## Makefileの修正

`sparkfun_nrf52840_mini/s140/armgcc`のMakefileを開き、`CFLAGS`と`ASMFLAGS`で使用されている`-DBOARD_PCA10056`というオプションを以下のように修正します

```Makefile
CFLAGS += -DBOARD_SPARKFUN_NRF52840_MINI
```
```Makefile
ASMFLAGS += -DBOARD_SPARKFUN_NRF52840_MINI
```

コンパイラへの引数オプションを変えているだけなので、ファイル自体は修正せずmakeコマンドの引数に渡すことでも可能です。

# ビルドとフラッシュ

ここまできたらターミナルで`make`を実行すればビルドできます。ビルドが成功したらあとはフラッシュするだけです。ボードとJ-Linkを接続し、`make flash`を実行しましょう。
それぞれのボードのSWDは以下の箇所です。SparkFun Pro nRF52840 Miniは裏面にあるので注意して下さい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167138/6794628d-bcca-7a58-beda-321733304733.png)

# 最後に

もはやVSCodeはIDE…？**一理ある。**

余力があればデバッグについても書きます。もちろんデバッグもVSCodeでできます。