WCH の RISC-V MCU CH32V203 を Rust でLチカする

tl;dr

  • WCH 社の安価な RISC-V MCU CH32V203 を手に入れたよ
  • RISC-V の firmware は rust で実装できるよ
  • ch32-rs というライブラリを使えば firmware も実装しやすいよ

経緯

最近、 RISC-V か ARM に様々なエコシステムが集まっているように感じています。 そして、rust では RISC-V と ARM については、少しrust の追加セットアップは必要になりますが、rustだけでビルドができるようになっているます。 rust だけで完結するというのがとても開発環境構築にも楽でよさそうです。

最近 RISC-V である ESP32-C3 のFirmwareを rust で実装を試みていて、その他にも Rust で遊べる MCU をためそうと思い、そんな中同人誌『PICやAVRの代わりにCH55xはどうでしょう』で扱われていたWCH社の、STM32的な立ち位置のRISC-VのMCU CH32Vを見つけました。

techbookfest.org

本当は CH32V203 ではなく 50セントマイコンである CH32V003 を目当てに購入したのですが、USB 付属の CH32V203 も付いた開発ボードとMCU単体のセットが送料込みで2,500円であったので購入しました。

CH32V003F4P6 QingKe RISC-V2A 1-wire SDI System Main Frequency 48MHz,1Pcs/Kit

https://www.aliexpress.com/item/1005004895791296.html

embedded-hal

Rust には、Arduino的な位置づけの embedded-hal という crate があります。

embedded-hal

https://crates.io/crates/embedded-hal

これは、GPIOにアクセスするレジスタの操作を抽象化し、様々なMCUに対して統一した抽象レイヤを提供してくれます。 ESP32-C3 で使える esp-idf-hal も embedded-hal 経由で提供されています。

esp-idf-hal

https://crates.io/crates/esp-idf-hal

ラズピコにもあります。

rp2040-hal

https://crates.io/crates/rp2040-hal

Rust では、このようなエコシステムができつつあるのはすごく魅力的です。 特に他のマイコン用のサブセットを移植した言語とは異なり、標準ライブラリは使えない成約があるにしても、言語ランタイムそのものが動くことも非常に筋が良く感じます。

CH32V での Rust 開発

先の WCH 社の RISC-V MCU の CH32V 用の crate はすでに作られていました。

ch32v2

https://crates.io/crates/ch32v2

これは STM32 で使われるレジスタの情報ファイル svd から(2023/2/20修正) embedded-hal を出力する レジスタにアクセスするラッパーを出力する svd2rust を使って作成されています。 このcrateでは以下のように、riscv32imac-unknown-none-elf をターゲットにビルドするように指定されています。

[package.metadata.docs.rs]
default-target = "riscv32imac-unknown-none-elf"

依存にこのcrateを追加するだけで、cargo build すると riscv32imac-unknown-none-elf でビルドされます。

これを使うことで Rust で開発できそうです。

サンプルリポジトリのビルドで環境構築をする

CH32V203 を使ったサンプルリポジトリが公開されています。

github.com

これをビルドするには、リポジトリをチェックアウトします。 このリポジトリでは、ローカルにch32v2 の crate リポジトリがあることが前提になっているので、Cargo.toml のその部分を書き換えます。

# Cargo.toml
[dependencies]
ch32v2 = { version = "0.1.5", features = [ "ch32v20x", "critical-section" ] }

Rust 側の準備は1コマンドだけ、以下のコマンドで riscv32imac-unknown-none-elf を入れておきます。

rustup target add riscv32imac-unknown-none-elf

そして rust の一般的なビルド方法をするだけです。

cargo build --release

ただし、これではファイルサイズが大きくなるため、最適化するために以下を Cargo.toml に追加しました。 このオプションはラズピコのビルド手順を紹介しているところから拝借しました。

# Cargo.toml
[profile.release]
codegen-units = 1
debug = false
debug-assertions = false
overflow-checks = false
panic = 'abort'
lto = true
opt-level = "z"
incremental = false
strip = true

すると約9KBになります。

$ls -alh target/riscv32imac-unknown-none-elf/release/

-rwxrwxr-x  2 nnyn nnyn 9.0K Dec 21 07:22 ch32v203-demo

最初私はここで作成される target/riscv32imac-unknown-none-elf/release/ch32v203-demo が ELF ファイルであるため、そのままFirmware として使えるバイナリかと思いこんでいました。 しかし、ビルドが動作できずに困っていて、ch32v203-demo のリポジトリで質問させてもらい、教えていただきました。

これを使うのではなく、以下のコマンドでバイナリファイルにする必要があります。

riscv64-unknown-elf-objcopy -O binary target/riscv32imac-unknown-none-elf/release/ch32v203-demo firmware.bin

riscv64-unknown-elf-objcopy は Ubuntu であれば以下のコマンドでインストールできます。

sudo apt install binutils-riscv64-unknown-elf

MacOS であれば以下の記事が参考になりそうです。

blog-dry.com

これでできた firmware.bin のサイズは 632 B でした。

$ls -alh firmware.bin
-rwxrwxr-x 1 nnyn nnyn 632 Dec 22 20:31 firmware.bin

CH32V203開発ボードに焼く

CH32V203 には、WCH-LINKを使って焼く方法と、CH32V203 の USB を使って焼く方法があります。

CH32V203 のUSBを使う

CH32V203 は USB Device 機能があり、CH552 と同様に USB 経由で書き換えることができます。

この USB で書き込める状態にするには、CH32V203 の各ピンを以下のように接続して、リセットします。

  • BOOT0: 3V3
  • BOOT1: GND

開発ボード CH32V203C8T6-EVT-R0 には最初からBOOT0とBOOT1 をGND に接続するジャンパが付いており、このジャンパを外すことでプルアップされるものだと思っていましたが、そうではないようです。 BOOT0 と 3V3 とつないでリセットすることでこのモードに入ることができました。 このモードに入った後はBOOT0と3V3 は外します。

また、このボードには USB が3口付いていますが、このモードに入ることができる USB は MCU の USB1 に接続されている以下の方になります。

下のUSB-AとUSB-Cはどちらも USB2 に接続されています。

USB1 が write できる状態になると、以下のように認識します。

$lsusb

Bus 003 Device 028: ID 4348:55e0 WinChipHead

これを使って焼くには wchisp を使うと楽です。wchisp は Rust 製のプログラマです。 これをインストールするには以下のように実行します。

cargo install wchisp

先程作った firmware.bin を焼くには以下のように実行します。

wchisp flash firmware.bin

以下のように接続します。

WCH-LINK CH32V203
3V3 3V3
GND GND
WSDIO PA13
WSCLK PA14

開発ボード CH32V203C8T6-EVT-R0 ではこの4ポートが並んで配置されているので簡単に接続できます。

CH32V に対応した OpenOCD が、MountRiver Studio という IDE で提供されているツールの中に含まれています。 それは以下からダウンロードできます。MacOSLinuxでもあるので助かります。

www.mounriver.com

この OpenOCDを使って焼くには以下のように実行します。

sudo ./openocd -f wch-riscv.cfg  -c init -c halt  -c "program firmware.bin"  -c exit

ch32v203-demo の動作

この firmware では PB8 をLチカします。

この開発ボードにはLED1、LED2 のポートがありますが、これはどこにも繋がっておらず、自分で接続することで動作します。なお、LOWになると光る仕様です。

以上

なにか作ったわけではないですが、Rustで RISC-V MCU CH32V を扱う一歩を踏み出すことができました!

CH32V203 は(2023/2/20修正) ATTiny のように MCU だけでは動作せず、クリスタルも2個必要で、扱いやすいとは言えませんが、クリスタルなしでも動作し、とても安価に手に入るので、これから Rust で触ってみようと思います。

2023/02/20追記

CH32V203 開発ボードを作りました!

74th.hateblo.jp

参照