BluePillのmbed VSCode開発環境を構築してみた


はじめに

趣味で宇宙開発を行う団体「リーマンサット・プロジェクト」がお送りする新春アドベントカレンダーです。
この記事は8日目です。

リーマンサット・プロジェクトは「普通の人が集まって宇宙開発しよう」を合言葉に活動をしている民間団体です。
他では経験できない「宇宙開発プロジェクト」に誰もが携わることができます。
興味を持たれた方は https://www.rymansat.com/join からお気軽にどうぞ。

この記事は

この記事ではWEB開発や組み込み開発などのシステム開発をしたことがある方(シェルやプログラムが使えること)を前提にしています。

僕はリーマンサットで超小型人工衛星である「RSP-00」と「RSP-01」の無線通信のソフトウェア開発を担当しています。
RSP-01の無線通信制御には STM32(arm)マイコン とオープンソースな組み込み開発環境である mbed を採用しました。 STM32(arm)マイコン はマイコンの中でも比較的スペックが高く、 mbed のAPIがハードウェアを抽象化してくれるためソフトウェア開発が進めやすいといった利点がありました。また、使用したマイコンは宇宙環境を想定した恒温槽試験や放射線試験で問題がなかったことも選定に至った理由です。

リーマンサット・プロジェクトは文理問わず様々な職業の方が参加しており、事前知識やスキルはメンバー毎に大きく異なります。普段開発に関わることの無いメンバーでも組み込み開発を体験もらうために、メンバーの某御方が BluePill という格安の開発ボードを大量入手して教材として配布していました。そこで今回はこの BluePill 向けに mbed の開発環境を構築してみました。

BluePillって

BluePillSTM32F103C8T6 を搭載した開発ボードです。この開発ボードはとても安くで200円ほどで買えてしまい、なぜかIC単体で買うよりも安いといった不思議なボードです。
また、 STM32F103C8T6 はデータシート上64KByteの Flash Memory を搭載しているとありますが、なぜか `BluePill では128KByte搭載されているとの噂です。不思議ですねー。記事の下部で検証してみましたが、手元のものも128KByte扱うことができました。

BluePill の開発方法ですが Arduino IDE を使うことが多いようです。 Arduino は情報量が多いので、使い方を調べやすいといった利点があり、 BluePillArduino IDE 開発環境の構築も調べるといくつか出てきます。
一方で mbed を使用した方法はあまり情報がありませんが、 mbed の内部では HAL というベンダーが提供しているライブラリが使われているため、 BluePill の能力を最大限まで引き出すことができると考えています。
また、 OpenOCD PyOCD といったデバッグツールも用意されているため、一度環境を整えてしまえば最近流行りの Visual Studio Code(VSCode) を操作するだけで BluePill のソフト開発・ファームウェアの書き込み・ステップ実行までをおこなう事ができて非常に便利だったため記事に残してみました。

開発環境

ハードウェア

  • Macbook Pro(Retina, 13-inch, Late 2013)
  • BluePill

  • STLink v2

ソフトウェア

  • macOS Catalina v10.15.1
  • Visual Studio Code v1.41.1
  • Mbed CLI v1.10.2
  • gcc-arm-none-eabi 9-2019-q4-major
  • OpenOCD v0.10.0

今回はmacOS上で構築しましたが、 VSCode Mbed Cli gcc-arm-none-eabi OpenOCD はWindowsやLinux上でも動作しますので、インストールすれば同様に構築可能と考えています。

ブロック図

OpenOCD arm-none-eabi-gdb はVSCodeから起動されます。

各ツールのインストール

Visual Studio Code

こちらからインストーラー経由でインストールします。
また、 VSCode上の拡張機能の追加から C/C++ のプラグインをインストールしておきます。
C/C++ プラグインがあると、GDB を使ったデバッグ実行や、ソースコード上の 定義 宣言 参照 に素早くアクセスしたり、構文エラーにいち早く気付けます。

Mbed CLI

Windowsの場合はインストーラーでインストールするのが簡単でした。
macOSの場合はインストーラーでインストールすると専用のシェルが立ち上がり、普段の環境を汚さないようになっています。サクッと試すのであればインストーラーの方が簡単ではありますが、使い慣れたシェル上で扱えない上、この後のVSCodeからの呼び出しでパスが通っていなかったりするため、個人的にはマニュアルインストールの方がおすすめです。

$ brew install python@2
$ pip install mbed-cli

gcc-arm-none-eabi

インストーラー経由の場合、 gcc-arm-none-eabi は自動でインストールされますが、マニュアルインストールの場合は別途インストールする必要があります。

$ brew tap ArmMbed/homebrew-formulae
$ brew install gcc-arm-none-eabi

OpenOCD

$ brew install OpenOCD

Windowsのインストール方法はまだ調べれてないです。。

プロジェクトのセットアップ

下記のコマンドでプロジェクトをセットアップします。

$ mbed new bluepill_first_project
$ cd bluepill_first_project
$ mbed toolchain GCC_ARM
$ mbed target BLUEPILL_F103C8
  • mbed new : プロジェクトの作成
  • mbed toolchain : 使用するコンパイラ、今回はGCC_ARMを指定する
  • mbed target : 開発ボードの指定

そして main.cpp ファイルを下記内容で作成します。組み込み開発の Hello World! 、Lチカのソースコードです。

#include "mbed.h" // mbed OSの読み込み

DigitalOut led1(LED1); // mbed target BLUEPILL_F103C8でボード上のLEDがLED1と定義される

int main() {
    while (true) {
        led1 = !led1; // 出力のHIGH/LOWをトグル
        wait_us(1 * 1000 * 1000); // 1秒
    }
}

あとは下記コマンドでコンパイルすると、開発ボードに書き込むファームウェア(バイナリ)が生成されます。

$ mbed compile

正常終了時は以下のように出力されます。

Link: bluepill_first_project
Elf2Bin: bluepill_first_project
| Module           |        .text |      .data |        .bss |
|------------------|--------------|------------|-------------|
| [fill]           |      46(+22) |      8(+0) |      21(+9) |
| [lib]/c.a        |    17428(+0) |   2472(+0) |      56(+0) |
| [lib]/gcc.a      |     3104(+0) |      0(+0) |       0(+0) |
| [lib]/misc       |      180(+0) |      4(+0) |      28(+0) |
| main.o           |       64(+0) |      0(+0) |      28(+0) |
| mbed-os/drivers  |       74(+0) |      0(+0) |       0(+0) |
| mbed-os/hal      |     1356(+0) |      4(+0) |      66(+0) |
| mbed-os/platform |   2646(+316) |    260(+0) |    220(+10) |
| mbed-os/rtos     |  6380(+6380) |  168(+168) | 5973(+5973) |
| mbed-os/targets  |   6046(+690) |      4(+0) |     368(+0) |
| Subtotals        | 37324(+7408) | 2920(+168) | 6760(+5992) |
Total Static RAM memory (data + bss): 9680(+6160) bytes
Total Flash memory (text + data): 40244(+7576) bytes

Image: ./BUILD/BLUEPILL_F103C8/GCC_ARM/bluepill_first_project.bin

一般的なmbedの開発ボードであれば開発ボード上に mbed interface と呼ばれるICが乗っており、開発ボードをUSB接続するとドライブがマウントされるので、先程生成された ./BUILD/BLUEPILL_F103C8/GCC_ARM/bluepill_first_project.bin をドラッグ・アンド・ドロップ(コピー)すればファームウェアの書き込みができます。
ですが、 BluePill には mbed interface が搭載されておらず、その機能がありません。

VSCode(OpenOCD)を使った書き込みとデバッグ

BluePill では mbed interface が搭載されていないため、代わりに STLink v2 という書き込み機器を使用してファームウェアを書き込みます。
BluePillSTLink v23.3V GND SWDIO SWCLK をそれぞれ接続します。

下記コマンドで VSCode用の設定ファイルを出力します。

$ mbed export -i vscode_gcc_arm

生成された .vscode/launch.json に下記修正を加えます。

@@ -11,11 +11,11 @@
             "cwd": "${workspaceRoot}",
             "environment": [],
             "externalConsole": false,
-            "debugServerArgs": "",
+            "debugServerArgs": "-f /usr/local/Cellar/open-ocd/0.10.0/share/openocd/scripts/interface/stlink-v2.cfg -f /usr/local/Cellar/open-ocd/0.10.0/share/openocd/scripts/target/stm32f1x.cfg",
             "serverLaunchTimeout": 20000,
             "filterStderr": true,
             "filterStdout": false,
-            "serverStarted": "GDB\\ server\\ started",
+            "serverStarted": "openocd",
             "preLaunchTask": "make",
             "setupCommands": [
                 { "text": "-target-select remote localhost:3333", "description": "connect to target", "ignoreFailures": false },
@@ -41,7 +41,7 @@
             "osx": {
                 "MIMode": "gdb",
                 "MIDebuggerPath": "arm-none-eabi-gdb",
-                "debugServerPath": "pyocd-gdbserver"
+                "debugServerPath": "openocd"
             },
             "windows": {
                 "preLaunchTask": "make.exe",

以上で準備は完了です。
VSCodeの「デバッグと実行」からコンパイル・書き込み・デバッグを開始してみます。

無事 BluePill へ書き込みができ、ステップ実行やローカル変数への参照ができました!

おまけ

BluePillのFlashサイズ

STM32F103C8T6 は公式で64KByteの Flash Memory を搭載しているとありますが、128KByte搭載されているとの噂があるため、実際に検証してみました。

やり方は簡単で、 printf とかで10000文字くらいの文字列を適当に追加していきます。
Lチカのソースコードをコンパイルすると約44KByteでした。10000文字で約10KByteなので8行ほど追記してみます。(同じ文字列だとコンパイラに最適されるため少しだけ文字を変えてます。)

        printf("1aaaaaaaaaa...");
        printf("2aaaaaaaaaa...");
        ...

これでコンパイルして書き込めば・・・と思ったのですが、想定外にもコンパイルの時点でエラーが発生しました。

Link: bluepill_first_project
/usr/local/Cellar/arm-none-eabi-gcc/9-2019-q4-major/gcc/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld: ./BUILD/BLUEPILL_F103C8/GCC_ARM/bluepill_first_project.elf section `.text' will not fit in region `FLASH'
/usr/local/Cellar/arm-none-eabi-gcc/9-2019-q4-major/gcc/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/bin/ld: region `FLASH' overflowed by 58112 bytes
collect2: error: ld returned 1 exit status

どうやら64KByteの制限がかかっているようです。
下記ファイルで64KByteの制限をかけているようなので、力技ですが直接編集してしまいます。(mbed-osディレクトリは基本的にgit管理外とするため注意)
./mbed-os/targets/TARGET_STM/TARGET_STM32F1/TARGET_BLUEPILL_F103C8/device/TOOLCHAIN_GCC_ARM/STM32F103XB.ld

@@ -9,7 +9,7 @@

 MEMORY
 {
-  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
+  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
   RAM (rwx) : ORIGIN = 0x200000F0, LENGTH = 20K - (0xEC+0x4)
 }

上記修正を加えた後、コンパイルと書き込みを試したところ無事書き込むことができました!この時のバイナリサイズは126KByteでしたので、噂は本当でした!

Mbed OS bare metalを試してみる

Mbed OS では、スペックの低いマシンでも一部の機能(主にRTOS)を無効にすることで、 Mbed OS を使うことができるbare metalと呼ばれる仕組みがあります。
BluePillには Mbed OS 5 の記載がないため Mbed 2bare metal での実行が必要だろうなと予想していましたが、上記で試したLチカは Mbed OS 5 で問題なく動いてしまいましたw
ただ、せっかくなので bare metal でもコンパイルしてみたいと思います。

mbed_app.json を下記のように編集して、後はいつも通りにコンパイルするだけでした。

{
    "requires": ["bare-metal"]
}

Mbed OS 5 時のバイナリサイズが44Kbyteに対し、 bare metal 時のバイナリサイズが35Kbyteと少しだけダイエットに成功です。恩恵はあまり多くありませんが、少しでもバイナリを小さくする必要があったり、RTOSを意図的に無効にしたい場合の選択肢になるのかなと思います。

今後の目標

シリアル通信

今回は VSCode上で BluePill のコンパイルと書き込み・デバッグまでできることが確認できました。
これだけでも開発をすすめるには十分ですが、printf といった標準入出力の対応も検討したいと考えてます。
mbed interface が搭載されている開発ボードであれば、USBからシリアル通信で標準入力・出力へのアクセスが可能ですがSTLink には同等の機能が搭載されていないため以下の三種類の方法が検討されます。

外部のUSBシリアル通信変換を用いれば他のmbed開発ボードと同様に標準入出力が利用できますが、ケーブル2本用いるためスマートとは言えません。
SWO を使えば追加のモジュールやケーブルは不要となるためスッキリとしますが、環境の用意が少し手間取りそうだったため今回は深追いできてません。。

環境構築

開発環境の構築は既存マシンの環境に依存する箇所があるため、上記手順でうまく構築できない場合もあると思います。
そういった所をDockerで置き換えていけたら環境構築のトラブルを減らせるのかななんて考えてます。(DockerではUSB周りがうまく動作しないと思うので、 OpenOCDGDBDocker Network で繋げるのがいいのかななんて妄想しています。)

参考記事

STM32F103C8 で遊ぶ
Arm Mbed CLIの環境構築 for Windows
mbed CLI (コマンドライン・インタフェース)を Mac OS X で使ってみる
Visual Studio Codeでmbed OSプログラムをデバッグする方法(STM32)
STM32F103C8T6 board, alias Blue Pill

おわりに

リーマンサット・プロジェクトは「普通の人が集まって宇宙開発しよう」を合言葉に活動をしている民間団体です。
他では経験できない「宇宙開発プロジェクト」に誰もが携わることができます。
興味を持たれた方は https://www.rymansat.com/join からお気軽にどうぞ。

次回は @ytakuro0926 さんの「C# & Unityで小型衛星姿勢制御ツールを作ってみた①」です。楽しみですね!