秋月のLPC11U35ボードでMbedを使う

LPC11U35でMbed OSを動かす。開発環境はWindows + Mbed Studio。

問題点

秋月で売られているLPC11U35マイコンボードはQuick Start Board互換でMbed対応なのに850円と安く、使い勝手がよさそうなので買ってみた。

しかしいざソフトを作ろうとして気づいたのだが、このボード(というか元になっているQuick Start Board)が対応しているのはMbed OS 2のみで、Mbed OS 5はサポートされていなかった。Mbed StudioはMbed OS 5限定なのでこれではコンパイルできない。仕方ないのでMbed OS 5に対応させることにした。

ただし、Mbed OS 5をLPC11U35で動かすには別の問題がある。Mbed OS 5は2に比べてRTOSなど高度な機能が追加されているので、その分必要なRAM容量が増えている。どうやら16KB以下のRAMでは厳しいらしい(参考)。LPC11U35は12KBしか積んでいないのでダメそうだし、実際最初はRTOSも含めて移植しようとしてみたもののビルド時に「RAMが足りない」と怒られた。

一方、Mbed OS 5にはbaremetalプロファイルというものがある。これは機能的にはMbed OS 2とほとんど同じで、基本的なタイマーやIOなど最低限のAPIだけに絞られている。Mbed OS 2しか動かないようなMCUをMbed OS 5でもサポートする形になっていて、当然RTOSは使えないが、これならLPC11U35でも動きそうだということでbaremetalプロファイルを移植する。

まとめると

  1. 秋月LPC11U35はMbed OS 2しか対応していない
  2. Mbed OS 5のフル機能を移植するにはRAMが足りない
  3. 最低限の機能だけのbaremetalプロファイルを使おう

移植

移植といってもほとんどのコードはMbed OS 2用に書かれたものをそのまま使えるので大した作業量ではないが、手順を残しておく。

空のプロジェクトを用意する

Mbed StudioではプロジェクトごとにGithubにあるMbed OSのソースコードを丸々コピーしてきて利用するので、OSに対する修正もプロジェクトで行うことになる。Mbed Studioで「Empty Mbed OS program」か「mbed-os-example-blinky」あたりを選択してプロジェクトを作る。

baremetalプロファイルに切り替える

プロジェクトのフォルダができたらトップ階層(main.cppと同じ場所)に「mbed_app.json」というファイルを作り、以下のように編集。

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

カスタムターゲットを作る

同じくトップ階層に「custum_targets.json」というファイルを作り、以下のように編集。ほとんどはmbed-os/targets/targets.jsonのLPC11U35_401の部分をコピーしたものだけど、一部変更している。ターゲット名の「AE_LPC11U35」は秋月のボードの名前を拝借した。

{
    "AE_LPC11U35": {
        "inherits": ["LPCTarget"],
        "core": "Cortex-M0",
        "default_toolchain": "ARM",
        "extra_labels": ["NXP", "LPC11UXX", "LPC11U35_401"],
        "macros": [
            "CMSIS_VECTAB_VIRTUAL",
            "CMSIS_VECTAB_VIRTUAL_HEADER_FILE=\"cmsis_nvic.h\""
        ],
        "supported_toolchains": ["ARM", "uARM", "GCC_ARM", "IAR"],
        "device_has": [
            "ANALOGIN",
            "I2C",
            "I2CSLAVE",
            "INTERRUPTIN",
            "PORTIN",
            "PORTINOUT",
            "PORTOUT",
            "PWMOUT",
            "SERIAL",
            "SLEEP",
            "SPI",
            "SPISLAVE",
            "USTICKER"
        ],
        "release_versions": ["5"],
        "OUTPUT_EXT": "bin"
    }
}

コンフィグ情報を変更する

先ほどのmbed_app.jsonを再度編集。二度手間で申し訳ない。

{
    "requires": ["bare-metal"],
    "target_overrides": {
        "AE_LPC11U35": {
            "target.tickless-from-us-ticker": true
        }
    }
}

区切りのコンマをお忘れなく。

コードを修正して差し替える

ここまででコンパイル自体は通るのだが、実際にmbed-os-example-blinkyを走らせてみるとthread_sleep_for()の部分でハードフォルトになり止まってしまう。調べた結果割り込みベクターテーブルに問題があることがわかり、ソースコードを修正するとちゃんと動くようになった。ここではmbed-osフォルダ以下にあるソースコードを直接変更するのではなく、別のソースファイルに差し替えることで修正を行う。

まずは修正対象のソースファイルを無効にする。トップ階層に「.mbedignore」というファイルを作成し、以下の1行を記述。これでmbed-os/targets/TARGET_NXP/TARGET_LPC11UXX/device/cmsis_nvic.cはコンパイル対象から除外される。

mbed-os/targets/TARGET_NXP/TARGET_LPC11UXX/device/cmsis_nvic.c

次にトップ階層に「TARGET_LPC11UXX」というフォルダを作り、その中にmbed-os/targets/TARGET_NXP/TARGET_LPC11UXX/device/cmsis_nvic.cをコピー。

そして、コピーしてきたcmsis_nvic.cの63行目

      uint32_t *old_vectors = (uint32_t *)0;         // FLASH vectors are at 0x0

を次のように修正(volatileを追加)。

      volatile uint32_t *old_vectors = (uint32_t *)0;         // FLASH vectors are at 0x0

これでmbed-os/targets/TARGET_NXP/TARGET_LPC11UXX/device/cmsis_nvic.cの代わりにTARGET_LPC11UXX/cmsis_nvic.cが使われるようになり、修正が反映される。

ここまでで、ファイル構成は下のようになる。

ファイル構成
1つのフォルダと4つのファイルを追加

コンパイルする

Mbed StudioのTarget欄で「AE_LPC11U35」を選択する。検索ボックスで「ae」と入力したら候補の下の方の「MCUs and custom targets」のところに出てくるはず。

Mbed StudioのTarget選択画面
Mbed StudioのTargetに「AE_LPC11U35」が追加される

あとは普通にビルド。

バイナリの書き込みは自動でやってくれたりしないので手動で。BUILD/AE_LPC11U35/ARMC6にある.binファイルを秋月の説明書の手順通りに書き込む。

ほかのプロジェクトで使う

新しくプロジェクトを作ったら、今回作成したmbed_app.json、custom_targets.json、.mbedignore、TARGET_LPC11UXXフォルダをプロジェクトのトップ階層にコピーしてくればOK。同じようにMbed StudioでTargetに選択してビルドできるようになる。

感想

当初は移植というので大変な作業になることも想像したが、思ったよりあっさりできて、Mbed OS 2とMbed OS 5の互換性の高さに助けられた。また、今回使ったようなMbedのソース管理やコンフィグ関係の仕組みはよくできていると思う。RTOSも使ってみたかったけど、安いボードだし諦めることにする。今回の作業でビルド環境が整って実際に動かせるようになったので、これからこのボードには頑張ってもらいたい。