Rust + nRF52 で自作キーボードファームウェアを作ろうとしている履歴
買ったもの
ISP1807搭載Microボード(Pro Microピン互換)
- https://www.switch-science.com/catalog/6939/
- ISP1807 (nRF52840 パッケージ) が動作する Pro Micro とピン配置に互換性のあるボード
- ブートローダは UF2 が入ってる
- 最新が0.6.3なのに対し、やたらとバージョンが低い。またSoftDeviceは7系じゃなく6系。
UF2 Bootloader 0.3.2-114-gb5fab63-dirty lib/nrfx (v2.0.0) lib/tinyusb (0.6.0-272-g4e6aa0d8) lib/uf2 (remotes/origin/configupdate-9-gadbb8c7)
Model: ISP1807 Micro Board
Board-ID: ISP1807 Micro Board Rev.A
SoftDevice: S140 version 6.1.1
Date: Feb 15 2021
nRF52840 MDK USB Dongle
- https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/
- https://github.com/makerdiary/nrf52840-mdk-usb-dongle
- nRF52840 の載ったドングル。技適なし。
- 国内でBLE機能使ったらアウト。
- ブートローダはnordic connectが使えるやつじゃなくて最初からUF2だった。
- どうせアウトなので詳細はなし
澤田と和解せよボード
- 下記でSWDに苦しんでたら見かねたフォロワーさんが設計したものを安く譲っていただけた
- AE-LPC11U35-MB (DAPLink役) と MS88SF2(nRF52840パッケージ)がSWDで繋がるよう実装されている。
- 電源は3.3Vではなく5V(VDDH)を使うようになっている。
- ブートローダは UF2 の
BOARD=pca10056
互換との事なので自分で S140 v7.2.0 とあわせてビルドして使用。
ブートローダーってなに
- マイコンが起動したら真っ先に呼び出されるやつ
- メインプログラムを起動したり
- メインプログラムを書き込めるようにしたり
- ボードだとこれだけは書き込まれて出荷されることが多い
- まっさらな状態からブートローダーを書き込むのは大変
- ブートローダーの提供する書き込み機能でブートローダー自体も更新可能
- ブートローダーが破損するとまっさらな状態と変わらなくなるので修理は大変
- SWD(後述)などを使ってブートローダを復旧することは可能
- nRF52の場合、ブートローダーは主に2種類
Open Bootloader (nordic公式のもの)
- nRF connect をはじめとしたアプリとの連携機能
- USB-CDC
- ホストと通信するデバイスとして認識される
UF2 Bootloader (adafruitのもの)
- https://github.com/adafruit/Adafruit_nRF52_Bootloader
- Arduino 連携
- nRF SDK も使える
- USB-MSD
- ストレージデバイスとして認識される。特定ファイルとしてバイナリを書き込む
(ブートローダを使用しない)
- 実はブートローダは必須ではない
- USBからの書き込みをやめ、SWDで書くことにしてしまえばブートローダの機能はいらない
- 起動したら直接メインプログラムを起動するだけ
SoftDevice ってなに
- BLEのプロトコルスタックなど、プロプラエタリで変更不能にしたい機能のバイナリ
- nRF 固有のもの。チップ毎に種類がある
- nRF52840 の場合は S140 が該当する
- FLASHの予約された領域に配置してAPI経由でアクセスする
- バージョンがあり、予約サイズが異なる
-
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fsds_s132%2FSDS%2Fs1xx%2Fsd_mgr%2Fmem_isolation_runtime_protection.html
- APP_CODE_BASE から先にアプリケーションを書く必要がある
- 7系の APP_CODE_BASEは 0x00027000
- 6系の APP_CODE_BASEは 0x00026000
- APP_CODE_BASE から先にアプリケーションを書く必要がある
- embassy-rs/nrf-softdevice を使う場合は 7.x 系が必要
-
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fsds_s132%2FSDS%2Fs1xx%2Fsd_mgr%2Fmem_isolation_runtime_protection.html
Rust から BLE を使う
C から nRF で BLE を扱う、つまり SoftDevice を使用するには一般的に Nordic SDK を使用するが、Rust にはその用意がない。
Rust からのアクセスは SoftDevice へのバインディングである nrf-softdevice を使うか、あるいはいくつかの方法がある。
nrf-softdevice
- SoftDevice のバインディング
- Embassy (Embedded Rust 向けの非同期ライブラリ) の一部として提供される
- 現在開発中。crate.io からでなく github から拾ってくる必要がある
- SoftDevice 7.x 系が必要
rubble
- いちからRustで実装されたBLEスタック
- SoftDevice を使用しない
- 認証を通していないので 技適NG
CのプロジェクトからRustを呼ぶ
- Nordic SDK を使用する C プロジェクトを記載し、グルーコードを実装した上で Rust アプリを呼ぶ
- 一部で C を必要とする
- 一番現実的だが一番つまんない
ということで、今回は nrf-softdevice を使用することにした。
nrf-softdevice を使うための準備 (SWD環境作成、ブートローダ更新)
まずは examples の ble_bas_peripheral を走らせることを目指す。
ブートローダの更新および SoftDevice S140 の v7.x 化
まずは Adafruit のブートローダをビルドする。澤田と和解しろボードを使うので、BOARD=pca10056
とした。他のボードを使用する場合は適宜変更する必要がある。また存在しない場合はボード定義を書く必要がある。ここらへんはまだよくわかっていない。
$ git clone https://github.com/adafruit/Adafruit_nRF52_Bootloader
$ cd Adafruit_nRF52_Bootloader
$ git submodule update --init
$ make BOARD=pca10056 SD_VERSION=7.2.0
ここで作られた _build/build-pca10056/pca10056_bootloader-0.6.3-48-gcc8ea2b_s140_7.2.0.hex を焼く必要がある。
SWDを使ってブートローダを焼く
ブートローダを焼くには SWD での書き込みが必要となる。J-Linkの書き込み器とか持ってればそれに越したことはないんだろうけど、そんな金はないので安く済む書き込み器を求めて秋月の AE-LPC11U35-MB に行きついた。
以下記事などを参考にDAPLinkファーム焼いて結線してみる。
具体的には
- DAPLinkのreleases から v0254 ( 0254_release_package_f499eb6e.zip ) をダウンロード
- 展開して、 0254_lpc11u35_mtb_laird_bl654_0x0000.bin を AE-LPC11U35-MB に書き込み
- 以下の通り配線
ピン | AE-LPC11U35-MB | ISP1807 micro |
---|---|---|
GND | 1: GND | SWD-4: GND |
3V3 | 29: +3.3V | SWD-1: 3.3V |
SWCLK | 5:0_9 | SWD-2: SWDI |
SWDIO | 4:0_8 | SWD-3: SWDC |
- AE-LPC11U35-MB 側のUSBポートをPCと接続
- nRF52側のUSBポートではない
Segger Embedded Studio から書き込む
最初に知ったのがこの方法。
Segger Embedded Studio for ARM をインストールし、 Target → Connect J-Link から接続、Erase All で全消去して Download file → Download Intel Hex File からブートローダの *.hex ファイルを焼く。
うまくいったら Disconnect し、nRF52 側の USBポートをPCと接続すると USB メモリのように外部ボリュームとしてマウントできるようになる。
マウントしたあと INFO_UF2.txt
を開くとブートローダの構成情報が確認できる。
probe-rs-cli を使う
Segger でもいいんだけど、GUI を毎回使うのはめんどくさい。ということで CLI から使えるものを探して probe-rs-cli をみつけた。cargo で probe-rs-cli をインストールして使用する。た
$ probe-rs-cli erase --chip nrf52840
$ probe-rs-cli download --chip nrf52840 --format hex /path/to/***.hex
probe-run を使う
$ sudo apt install -y libusb-1.0-0-dev libudev-dev
$ cargo install --version 0.2.0 -f probe-run
- ターゲットボードと SWD で接続した probe をにUSB 接続して操作する
- 実際の通信部分は probe-rs
probe-rs を使う
SWDが必要。
秋月のLPC11U35マイコンボードキット が格安ながら DAPLINK という SWD での書き込み器になるとの噂を聞いたのでそれを試してみる。
SWDと戦う
ここらへんでTwitterではSWDを澤田と呼びはじめ「澤田と和解せよ」とかいうネタが流行りはじめていた。
それはさておき以下記事などを参考にファーム焼いて結線などしてみた。
具体的には
- DAPLinkのreleases から v0254 ( 0254_release_package_f499eb6e.zip ) をダウンロード
- 展開して、 0254_lpc11u35_mtb_laird_bl654_0x0000.bin を AE-LPC11U35-MB に書き込み
- 以下の通り配線
ピン | AE-LPC11U35-MB | ISP1807 micro |
---|---|---|
GND | 1: GND | SWD-4: GND |
3V3 | 29: +3.3V | SWD-1: 3.3V |
SWCLK | 5:0_9 | SWD-2: SWDI |
SWDIO | 4:0_8 | SWD-3: SWDC |
- nrf-softdevice の example を cargo run
$ vi memory.x
FLASH : ORIGIN = 0x00026000 と書き換える。(SoftDevice6.1.1を使用するため。7系を使うなら修正不要)
$ cargo run --bin ble_bas_peripheral
:
Error: An error with the usage of the probe occured
Caused by:
Didn't receive any answer during batch processing: [Read(DebugPort, 0)]
あれ・・・
$ cargo run --bin ble_bas_peripheral
:
Error: no probe was found
結線の接点不良?LPC11U35ハズレボード掴んだ?いろいろ努力してみたがちょっとよくわかんなかったので一旦あきらめ。
uf2conv を使う
cargo run
するには probe-run が必要だが、cargo build
したバイナリを uf2 ファイルに変換してしまえば Adafruit ブートローダの MSD 機能によってUSBつないでマウントしてコピペするだけでバイナリ自体は焼ける。
ということで変換ツールを含むリポジトリを clone しておく。
$ cd nrf-softdevice/example
$ vi memory.x
FLASH : ORIGIN = 0x00026000 と書き換える。(SoftDevice6.1.1を使用するため。7系を使うなら修正不要)
$ cargo build --bin ble_bas_peripheral
$ arm-none-eabi-objcopy -Oihex ../target/thumbv7em-none-eabihf/debug/ble_bas_peripheral ble_bas_peripheral.hex
$ python3 path/to/uf2conv.py ble_bas_peripheral.hex -c -b 0x26000 -f 0xADA52840
$ cp flash.uf2 /media/illness072/NRF52BOOT/
適当なBluetooth スキャンアプリなどで見てみると HelloRust というデバイスが見つかる。
memo: あとで整理する
env
rustup update
rustup install nightly
rustup component add llvm-tools-preview
rustup target add thumbv7em-none-eabihf --toolchain nightly
cargo install cargo-binutils
sudo apt install -y pkg-config libusb-1.0-0-dev libudev-dev
cargo install probe-run
DEFMT_LOG=info cargo run --bin ble_bas_peripheral
Linux
$ cat 50-daplink.rules
# 0d28:0204 DAPLink
SUBSYSTEM=="usb", ATTR{idVendor}=="0d28", ATTR{idProduct}=="0204", MODE:="666"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"
$ sudo mv 50-daplink.rules /etc/udev/rules.d/
$ sudo udevadm control --reload
$ sudo udevadm trigger