micro:bitで本格的な組込みソフトウェア開発環境を構築し、サンプルプログラムをVSCodeからデバッグできるようにする

# micro:bitで本格的な組込みソフトウェア開発

micro:bitは、様々な方法で、組込みソフトウェア開発が可能です。
本記事では、リアルタイムオペレーティングシステムの実装である[Apache Mynewt](http://mynewt.apache.org/) を使って、本格的な組込みソフトウェア開発環境を構築し、VSCodeでサンプルプログラムをデバッグ実行します。

# Windows 10 (64bit版) 上での開発環境構築
次のドキュメントを参考にして、Windows 10 (64bit版) 上に開発環境を構築します。

**Installing Newt on Windows**
https://mynewt.apache.org/latest/newt/install/newt_windows.html
**Installing the Cross Tools for ARM**
https://mynewt.apache.org/latest/get_started/native_install/cross_tools.html

本記事では、具体的なダウンロード元やインストールフォルダを示していますので、将来、バージョンアップなどにより、動作しなくなる可能性があります（2020/07/04現在）。

## MSYS2/MinGW のインストール
https://www.msys2.org/ からMSYS2のインストーラーをダウンロードし、インストールします。
→ [msys2-x86_64-20200629.exe](https://github.com/msys2/msys2-installer/releases/download/2020-06-29/msys2-x86_64-20200629.exe)

MSYS2を最新化する為、スタートメニューから`MSYS2 MSYS`を起動し、次のコマンドを実行します。

```
pacman -Syuu
```

さらに、次のコマンドを実行し、`tar`ツールをインストールします。

```
pacman -Su tar
```

Windows 10 のシステム環境変数で、変数名`MSYS2_PATH_TYPE`の変数値`inherit`を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/22e4e5c1-bb57-f1db-8b01-312e4fc78ba7.png)

さらに、Windows 10 のシステム環境変数の変数名`Path`の先頭に変数値`C:\msys64\usr\bin`を追加します。
※ `C:\msys64\`はMSYS2がデフォルトでインストールされるフォルダーです。

Mynewtのドキュメントやチュートリアルで指定されたコマンドを実行するには、MinGWターミナルを起動する必要がありますが、その際、スタートメニューから`MSYS2 Mingw 64-bit`を起動します。

## Git のインストール
https://gitforwindows.org/ からgitのインストーラーをダウンロードし、インストールします。
→ [Git-2.27.0-64-bit.exe](https://github.com/git-for-windows/git/releases/download/v2.27.0.windows.1/Git-2.27.0-64-bit.exe)

## newt のインストール（バイナリ版 1.8.0)
バイナリ版のnewtをインストールします。
まず、MinGWターミナルを起動し、次のコマンドで、バイナリ版のnewtをダウンロードし、展開後、`/usr/bin`へ移動します。

```
wget -P /tmp https://ftp.riken.jp/net/apache/mynewt/apache-mynewt-1.8.0/apache-mynewt-newt-bin-windows-1.8.0.tgz
tar -xzf /tmp/apache-mynewt-newt-bin-windows-1.8.0.tgz -C /tmp
mv /tmp/apache-mynewt-newt-bin-windows-1.8.0/newt.exe /usr/bin
```

次のコマンドで、newtのバージョンを確認できます。

```
newt version
```

## ARM Toolchain のインストール

[gcc-arm-none-eabi-7-2018-q2-update-win32.exe](https://developer.arm.com/-/media/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-win32-sha2.exe?revision=bc72768f-9927-454e-9141-918927fb74ff?product=GNU%20Arm%20Embedded%20Toolchain,32-bit,,Windows,7-2018-q2-update)をダウンロードし、インストールします。
インストールの最後で、`Add path to environment variable`をチェックしてから、「完了」ボタンを押下してください。

MinGWターミナルを起動し、次のコマンドで、それぞれのツールのインストール先を確認することができます。

```
which arm-none-eabi-gcc
which arm-none-eabi-gdb
```

**※ インストールするバージョンに関する注意事項**
[gcc-arm-none-eabi-7-2018-q2-update-win32.exe](https://developer.arm.com/-/media/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-win32-sha2.exe?revision=bc72768f-9927-454e-9141-918927fb74ff?product=GNU%20Arm%20Embedded%20Toolchain,32-bit,,Windows,7-2018-q2-update)をダウンロードし、インストールしてください。

`GNU Arm Embedded Toolchain: 8-2018-q4-major December 20, 2018` (gcc-arm-none-eabi-8-2018-q4-major-win32.exe) では開発中に次のエラーでビルドに失敗します。

```
When extracting code from .elf, objcopy reports "64-bit address 0x4b4fa300000000 out of range for Intel Hex file".
```

不具合情報：https://bugs.launchpad.net/gcc-arm-embedded/+bug/1810274



## openocd のインストール

[openocd-0.10.0.zip](https://github.com/runtimeco/openocd-binaries/raw/master/openocd-0.10.0.zip)をダウンロードします。
`C:\openocd-0.10.0\`フォルダへ展開します。
`C:\openocd-0.10.0\bin` をシステム環境変数の`Path`の先頭に追加します。

## SEGGER J-Link デバッガ のインストール
[「J-Link Software and Documentation pack for Windows」](https://www.segger.com/downloads/jlink/#J-LinkSoftwareAndDocumentationPack)をダウンロードし、インストールします。
→ [JLink_Windows_V680d.exe](https://www.segger.com/downloads/jlink/JLink_Windows.exe)


## micro:bitのファームウェア最新化（DAPLink）
次の手順で、micro:bitのフォームウェアを最新化します。
https://microbit.org/get-started/user-guide/firmware/

1. [0253_kl26z_microbit_0x8000.hex](https://cdn.sanity.io/files/ajwvhvgo/production/5ecfa4d407a9d02e0f2e7fe192e5fa6a246f8621.hex?dl=0253_kl26z_microbit_0x8000.hex)ファイルをダウンロードします。
1. micro:bit本体のリセットスイッチを押しながら、USB通信ケーブルで、パソコンに接続します。
1. micro:bitのストレージを`MAINTENANCE`ドライブとしてパソコンが認識していることを確認します。
1. ダウンロードしたHEXファイルを`MAINTENANCE`ドライブへコピーします。

# サンプルプログラム

開発環境が構築できたかどうかを確認する為、Lチカのサンプルプログラムを作成し、micro:bit本体で実行してみます。
https://mynewt.apache.org/latest/tutorials/blinky/nRF52.html

## プロジェクトの作成とリポジトリの取得
MinGWターミナルを起動し、次の一連のコマンドを実行し、プロジェクトを作成(`newt new myproj`)とリポジトリの取得(`newt upgrade`)を行います。

```
mkdir ~/dev
cd ~/dev

newt new myproj

cd myproj
newt upgrade
```

## ターゲットの作成
次のコマンドで、ブートプログラム（`ubit_boot`）とLチカ本体(`ubit_blinky`)のターゲットを作成し、設定します。

```
newt target create ubit_boot
newt target set ubit_boot app=@mcuboot/boot/mynewt
newt target set ubit_boot bsp=@apache-mynewt-core/hw/bsp/bbc_microbit
newt target set ubit_boot build_profile=optimized

newt target create ubit_blinky
newt target set ubit_blinky app=apps/blinky
newt target set ubit_blinky bsp=@apache-mynewt-core/hw/bsp/bbc_microbit
newt target set ubit_blinky build_profile=debug

newt target show
```

## サンプルソースコードの差し替え(micro:bit LED)
Lチカ本体のソースコードは、`~/dev/myproj/apps/blinky/src/main.c`です。
Windows10からは、`C:\msys64\home\<アカウント名>\dev\myproj\apps\blinky\src\main.c`としてアクセスできます（`<アカウント名>`はログオンユーザーアカウント）。
micro:bitのLEDマトリックスは、少し特殊な回路となっていますので、LEDマトリックスを点滅させるため、メモ帳などのテキストディタで`main.c`を開き、次のサンプルソースコードで上書き保存します。

``` main.c
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <assert.h>
#include <string.h>

#include "sysinit/sysinit.h"
#include "os/os.h"
#include "bsp/bsp.h"
#include "hal/hal_gpio.h"

// LED pin Column
#define PIN_LED_COL1  4
#define PIN_LED_COL2  5
#define PIN_LED_COL3  6
#define PIN_LED_COL4  7
#define PIN_LED_COL5  8
#define PIN_LED_COL6  9
#define PIN_LED_COL7  10
#define PIN_LED_COL8  11
#define PIN_LED_COL9  12
// LED pin Row for LED toggling
#define PIN_LED_ROW_0  13
#define PIN_LED_ROW_1  14
#define PIN_LED_ROW_2  15

static volatile int g_task1_loops;

/**
 * main
 *
 * The main task for the project. This function initializes packages,
 * and then blinks the BSP LED in a loop.
 *
 * @return int NOTE: this function should never return!
 */
int
main(int argc, char **argv)
{
    int rc;

    sysinit();

    // because the LEDs are multiplexed, we must ground the opposite side of the LED
    hal_gpio_init_out(PIN_LED_COL1, 0);
    hal_gpio_init_out(PIN_LED_COL2, 0);
    hal_gpio_init_out(PIN_LED_COL3, 0);
    hal_gpio_init_out(PIN_LED_COL4, 0);
    hal_gpio_init_out(PIN_LED_COL5, 0);
    hal_gpio_init_out(PIN_LED_COL6, 0);
    hal_gpio_init_out(PIN_LED_COL7, 0);
    hal_gpio_init_out(PIN_LED_COL8, 0);
    hal_gpio_init_out(PIN_LED_COL9, 0);

    // set LED pin to output mode
    hal_gpio_init_out(PIN_LED_ROW_0, 1);
    hal_gpio_init_out(PIN_LED_ROW_1, 1);
    hal_gpio_init_out(PIN_LED_ROW_2, 1);

    while (1) {
        ++g_task1_loops;

        /* Wait one second */
        os_time_delay(OS_TICKS_PER_SEC);

        /* Toggle the LED */
        hal_gpio_toggle(PIN_LED_ROW_0);
        hal_gpio_toggle(PIN_LED_ROW_1);
        hal_gpio_toggle(PIN_LED_ROW_2);
    }
    assert(0);

    return rc;
}
```

ターゲットのビルドとアプリケーションイメージの生成
次のコマンドで、作成した2つのターゲットをビルドし、アプリケーションイメージを生成します。

```
newt build ubit_boot
newt build ubit_blinky
newt create-image ubit_blinky 1.0.0
```

## micro:bit本体へのロード
次のコマンドで、micro:bit本体へ、実行に必要なイメージをロードします。

```
newt load ubit_boot
newt load ubit_blinky
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/b304f28f-331f-d581-644d-c4a15b8d5445.png)

# VSCodeを用いた開発環境の構築
次のドキュメントを参考にして、Visual Studio Code (VSCode)を用いたに開発環境を構築します。
https://mynewt.apache.org/latest/misc/ide.html

## VSCodeのインストール
https://code.visualstudio.com/Download のページから、Windows版のVSCodeをダウンロードし、インストールします。

## VSCode拡張機能のインストール
VSCodeを起動し、次の２つの拡張機能をインストールします。

1. C/C++ 拡張機能（Microsoft）
1. Native Debug 拡張機能（webfreak）

### C/C++ 拡張機能 のインストール

1. `Ctrl-P`を押下し、VSCodeの検索ボックスを開きます。
1. 検索ボックスに`ext install cpptools`と入力し、Enterキーを押下します。 
1. リストのトップに拡張機能の`C/C++`が表示されますので、`インストール`ボタンでインストールします。

### Native Debugger 拡張機能 のインストール

1. `Ctrl-P`を押下し、VSCodeの検索ボックスを開きます。
1. 検索ボックスに`ext install webfreak.debug`と入力し、Enterキーを押下します。 
1. リストのトップに拡張機能の`Native Debug`が表示されますので、`インストール`ボタンでインストールします。

# VSCode上でのワークスペースとタスクの関連付け
VSCodeには、次のような概念があります。

1. **ワークスペース** - 開いているフォルダがワークスペースです。
2. **タスク** - タスクを使用すると、プロジェクトのビルドやデバッグに使用される外部ツールとの操作をVisual Studio Codeに統合できます。

## ワークスペースの関連付け
Lチカのサンプルプログラム（プロジェクト）をVSCodeのワークスペースとして関連付けます。
エクスプローラーを開き、`~/dev/myproj/`である`C:\msys64\home\<アカウント名>\dev\myproj`フォルダを開きます。ファイル名やフォルダ名以外の空白の部分で、右クリックし、`Code で開く'で、VSCodeで開きます。

## ワークスペース内でのタスクの定義
`myproj`のワークスペースを開いた状態で、開発に必要なタスクを定義します。

### tasks.jsonの追加

次の手順で、`tasks.json`ファイルを追加します。
1. `Ctrl-Shift-P`キーを押下し、表示された検索ボックスで、`tasks`と入力すると候補が絞り込まれますので、`タスク: タスクの構成 / Tasks:Configure Task`を選択します。
1. `テンプレートからtasks.jsonを作成`をクリックし、リスト末尾の`Others`をクリックします。
1. `tasks.json`ファイルが追加され、エディタに表示されます。

### task.jsonの定義

`tasks.json`の内容を次の定義内容で上書き保存します。

```tasks.json
{
    "version": "0.1.0",
    "command": "newt",
    "echoCommand": true,
    "isShellCommand": true,

    "tasks":[
        {
            "taskName": "build_ubit_boot",
            "args": ["build", "ubit_boot"],
            "suppressTaskName": true
        },
        {
            "taskName": "build_ubit_blinky",
            "args": ["build", "ubit_blinky"],
            "isBuildCommand": true,
            "suppressTaskName": true
        },
        {
            "taskName": "create_ubit_blinky",
            "args": ["create-image", "ubit_blinky", "1.0.0"],
            "suppressTaskName":true
        },
        {
            "taskName": "load_ubit_boot",
            "args": ["load", "ubit_boot"],
            "suppressTaskName":true
        },
        {
            "taskName": "load_ubit_blinky",
            "args": ["load", "ubit_blinky"],
            "suppressTaskName":true
        },
        {
            "taskName": "debug_ubit_blinky",
            "args": ["debug", "ubit_blinky", "-n"],
            "suppressTaskName": true
        },
    ]
}
```

### タスクの実行方法
例えば、`build_ubit_blinky`タスクを実行するには、次の手順で操作します（他のタスクも同様）。

1. `Ctrl-Shift-P`キーを押下し、検索ボックスを開きます。
2. `tasks`と入力すると、候補が絞り込まれますので、`タスク: タスクの実行 / Tasks: Run Task`をクリックします。
3. `build_ubit_blinky`が実行するタスクの選択候補に表示されますので、それをクリックします。
4. 下段のペインで、`newt build ubit_blinky'が実行されます。

また、タスクに定義されていなくても`newt`コマンドを実行することができます。日本語キーボードの場合、`Ctrl-@`(バッククォート)キーを押下すると下段のペインにターミナルが表示・非表示されますので、そこで直接、`newt version`等のコマンドを入力し、実行することができます。
尚、`build_ubit_blinky`タスクに関しては、その定義で`isBuildCommand`が`true`になっていますので、`Ctrl-Shift-B`キーで、実行できます。

# アプリケーションのデバッグ
micro:bit本体とUSB通信ケーブルで接続して、VSCodeからアプリケーションをデバッグすることができます。まずは、デバッグ環境の構成を定義します。

## デバッグ環境構成の定義(GDB)
VScodeのメニューから[実行(Debug)]-[構成を開く(Open Configuration)]を選択し、環境の選択の中から`GDB`を選択します。`launch.json`ファイルが追加され、テキストエディタに表示されますので、次の内容で上書き保存します。

``` launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "gdb_ubit_blinky",
            "type": "gdb",
            "request": "attach",
            "executable": "${workspaceRoot}\\bin\\targets\\ubit_blinky\\app\\apps\\blinky\\blinky.elf",
            "target": ":3333",
            "cwd": "${workspaceRoot}",
            "gdbpath": "C:\\Program Files (x86)\\GNU Tools ARM Embedded\\7 2018-q2-update\\bin\\arm-none-eabi-gdb.exe",
            "remote": true

        }
    ]
}
```

## サンプルプログラムのデバッグ
サンプルプログラムのLチカをステップ実行でデバッグしてみます。

### ブレークポイントの追加
`main.c`を開き、行番号の左で、ブレークポイントを追加・削除できますので、例えば、図の行77に、ブレークポイントを追加します（赤い点）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/b59351b1-eefa-5660-14cf-e5298c31d596.png)

### GDBサーバーの開始
次の手順で、`GDB サーバー`を開始します。

1. `Ctrl-Shift-P`キーを押下し、検索ボックスを開きます。
2. `tasks`と入力すると、候補が絞り込まれますので、`タスク: タスクの実行 / Tasks: Run Task`をクリックします。
3. `debug_ubit_blinky`が実行するタスクの選択候補に表示されますので、それをクリックします。
4. 下段のペインで、`newt debug ubit_blinky -n'が実行され、micro:bit本体裏の通信LED(オレンジ色)が激しく点滅します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/e92c9d74-76aa-5222-a7fd-b611762cce5f.png)


### デバッグ実行の開始
次の手順で、デバッグ実行を開始します。

1. `Ctrl-Shift-D`キーを押下し、左縦のアクティビティバーから「実行(Debug)」アイコンを選択し、左側のペインを切り替えます。
2. 「実行(Debug)」ドロップダウンリストで、`gdb_ubit_blinky`を選択します。
3. 「実行(Debug)」横の緑色の「デバッグの開始」ボタンをクリックするとGDBセッションが開始されます。
4. すぐさま、デバッグが開始され、ブレークポイントで、一時停止しますので、変数`g_task1_loops`にカーソルを合わせると、現在値が表示されます。
5. 「続行(F5)」で、処理が続行され、次のブレークポイントで一時停止します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252891/d7f2c260-7416-237b-2f11-3594cae38ecd.png)

# まとめ
micro:bitで本格的な組込みソフトウェア開発ができることを確認しました。

* リアルタイムオペレーティングシステムの実装である[Apache Mynewt](http://mynewt.apache.org/) を使いました。
* C/C++による本格的な組込みソフトウェア開発環境を構築しました。
* サンプルプログラムのLチカをmicro:bit本体で動作させました。
* VSCodeでサンプルプログラムをデバッグ実行し、ステップ実行できることを確認しました。