🔅

RA4M1マイコンをRustでLチカしてみる

に公開

組込みマイコンをRustでプログラムしたい

つまりARM の「M」Cortex-MをRustでベアメタル(=OSなし)でプログラムしたいということなんだけど、STM32を使えばLチカまでキーボードポチポチで簡単にできる。

そんなのわざわざ記事にする必要もないので、「Arduino UNO R4」に搭載されたルネサス製RA4M1マイコンをRust言語を使ってLチカまでやってみる。

開発用ボード

純正の「Arduino UNO R4 Minima」は使いません。なぜかというと、水晶発振子が無いから。

水晶発振子を搭載した開発用ボードを探して Waveshare RA4M1-Zero を手に入れた。
USB-C / 水晶発振子 / WS2812 RGB LED / BOOT ボタン / RESET ボタンがあるのがお気に入りのポイント。

開発環境

Rust言語の環境整備

Rust Web サイトから 「Rustをインストール」のページに従ってインストールする。

組込みプログラミング環境整備

The Embedded Rust Bookのツールのインストールの章を参照してインストールする。
ターゲットのRA4M1マイコンは Arm Cortex-M4 (FPU付) なのでthumbv7em-none-eabihfがインストールされていればいい。

マイコンのマニュアルを入手する

RA4M1マイコンページから「ドキュメント」->「マニュアル、ガイド」-> 「Renesas RA4M1グループ ユーザーズマニュアル ハードウェア編」をダウンロードする。

「Renesas RA4M1グループ ユーザーズマニュアル ハードウェア編」の「アドレス空間」からフラッシュとメモリアドレスを確認しておく。

本MCU は、プログラムとデータの両方を格納できる4GB のリニアアドレス空間(0000 0000h ~ FFFF
FFFFh)をサポートしています。

32ビットマイコンなのでNearポインタだのFarポインタだの気にしなくていいですね。ってこと。あとメモリマップドI/Oだから周辺I/Oレジスタがいますね。

  • FLASH領域が 0000 0000h(16進数)から始まって256KB(RA4M1は256KB)
  • RAM領域が 2000 0000h(16進数)から始まって32KB(RA4M1は32KB)
  • 周辺I/Oレジスタが 4000 0000h(16進数)から始まる。

cortex-m-quickstart

RA4M1 は STM32 と同じ ARM Cortex-M なので、
cortex-m-quickstartを使うところなんだけど、今見たらarchiveになっていた。

STM32向けに書かれた組込みRustの情報を使えるので、細かいところはチャッピーに聞いてもらうか、検索するかでなんとかして。

memory.xファイルにはRA4M1のメモリアドレスを書いておく。

MEMORY {
    FLASH : ORIGIN = 0x00000000, LENGTH = 256K
    RAM   : ORIGIN = 0x20000000, LENGTH = 32K
}

Waveshare RA4M1-Zeroボードにブートローダーを書き込む

ミスしてマイコンを壊したか?と焦った時のために、一度練習で Renesas Flash Programmer でブートローダーを以下の記事通りに書き込んでみる。

https://zenn.dev/ichirowo/articles/6aa1614e102bce

Waveshare RA4M1-ZeroボードはBOOTボタンとRESETボタンを同時押しすると書き込みモードになる。
変なプログラムを書き込んでおかしくなっても、ブートローダーを書き込むと、Arduino UNO R4 minimaと同じ状態に戻る。

ボードの回路図を入手する

Waveshare wikiから Schematic Diagram をダウンロードする。

水晶発振子は16MHzの物が搭載されている。

通常ArduinoのLEDピンは D13(P111)なんだけど、このボードは D6(P106)にWS2812B RGB LEDが接続されている。

ということでオンボードのRGBLEDを使ってLチカをする。

Lチカ

動作するLチカコードをGitHubにおいておきます。

https://github.com/ak1211/hello-ra4m1/blob/ef9e229bed617c96346f8738882457f465770845/pac/src/main.rs

  • ベアメタルだからno_std環境です。
  • go言語のdefer関数みたいなことをしたかったからscopeguardを使う。
  • Peripheral Access Crate にra4m1_fsp_pacを使う。
    ほんとうはハードウエア抽象化レイヤ(HAL)を使いたかったです。

クロック

クロックの設定にちょっと癖があって。
搭載されている水晶発振子が16MHzなのでPLLが入力規定値を超えているんですよね。

まあ動けばいいか。

参考

http://igarage.cocolog-nifty.com/blog/2025/05/post-9275f8.html

ビルド&実行

このように

PS C:\hello-ra4m1\pac> cargo objcopy --release -- -O ihex app.hex
   Compiling hello-ra4m1 v0.1.0 (C:\hello-ra4m1\pac)
    Finished `release` profile [optimized] target(s) in 1.76s
PS C:\hello-ra4m1\pac> 

出来た app.hex ファイルを Renesas Flash Programmer で書き込んでリセットすることで実行する。

あとがき

最初は高速オンチップオシレータ(HOCO)を48MHz駆動するつもりだったけど、HOCOCR2レジスタがra4m1_fsp_pacにないので48MHz指定できなかった。

アドレスSYSTEM.HOCOCR2 4001 E000hとわかっているので、core::ptr::write_volatile関数に直接アドレス指定すれば出来るだろうけど、水晶発振16MHzを逓倍したPLL48MHz駆動でLチカできたのでこれでいいです。。。

何とかした

https://github.com/ak1211/hello-ra4m1/blob/1405a0a231ec97131e2a7f5a7d58b92d3dcdbae1/pac/src/main.rs

HOCOCR2 レジスタのアドレスは 4001 E000hではなかったようです。
HOCOCR2 レジスタの誤記訂正

これで48MHzで駆動の高速オンチップオシレータ(HOCO)をメインクロックに設定できた。

GPTタイマー割り込みをつかってみた

1秒delayを1秒周期GPTタイマー割り込みに変更してみた。結果は変わらない。

cortex_m::interrupt::freeは必要なのかよくわかっていない。

https://github.com/ak1211/hello-ra4m1/blob/07dc5e183bb2158742ec428251beca77977c9985/gpt_timer/src/main.rs

Arduino UNO R4 MINIMAでLチカ

BOOTとRESETを同時押ししてRenesas Flash Programmerで書き込むのが面倒になったので、
Arduino としては使わないが、DAPLINKのためにSWDコネクタが必要なので、Arduino UNO R4 Minima を買ってきた。

Amazonで手に入れたDAPLINKとprobe-rsでcargo runする。

用意するもの

  • DAPLINKの5Vと1ピン(VCC)
  • DAPLINKのSWDと2ピン(SWDIO)
  • DAPLINKのGNDと3,5,9
  • DAPLINKのSCKと4ピン(CLK)
  • DAPLINKのRSTと10ピン(~RESET)

を接続してArduino UNO R4 Minima のSWDコネクタにリボンケーブルを接続する。

USBケーブルをDAPLINKに接続する。

probe-rsのインストール&実行

Installationのページ通りインストール。

Arduino UNO R4 MINIMAのLEDが点滅する。

probe-rsのページに書いてある通り、cargo runする。

SWDコネクタ経由でデバッグプリントできた。

[INFO ] Hello. I'm "R7FA4M1AB3CFM   " (hello_ra4m1 probe_rs/src/main.rs:109)

シリアル通信を動かしてみた

https://github.com/ak1211/hello-ra4m1/blob/05e6f48811e1f3378e48cae7b464fd19191cb7f3/uart/src/main.rs

このままDAPLINKにあるシリアル通信とArduino UNO R4 MINIMAのSWDコネクタにあるSCI1_RXD, SCI1_TXDを接続して、通信してみる。

  • DAPLINKのRXDと8ピン(SCI1_TXD)
  • DAPLINKのTXDと7ピン(SCI1_RXD)

を接続する。

cargo runする。

hello-ra4m1\uart> cargo run
   Compiling hello-ra4m1 v0.1.0 (C:\Users\aki\development\hello-ra4m1\uart)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.78s
     Running `probe-rs run --chip R7FA4M1AB target\thumbv7em-none-eabihf\debug\hello-ra4m1`
      Erasing ✔ 100% [####################]  86.00 KiB @  17.72 KiB/s (took 5s)
  Programming ✔ 100% [####################]  86.00 KiB @   5.75 KiB/s (took 15s)                            
     Finished in 19.93s
[INFO ] Hello. I'm "R7FA4M1AB3CFM   " (hello_ra4m1 uart/src/main.rs:633)
[DEBUG] P110pfs is 0x40040868 (hello_ra4m1 uart/src/main.rs:278)
[DEBUG] P111pfs is 0x4004086c (hello_ra4m1 uart/src/main.rs:280)
[DEBUG] P112pfs is 0x4004086c (hello_ra4m1 uart/src/main.rs:282)
[DEBUG] P113pfs is 0x4004086c (hello_ra4m1 uart/src/main.rs:284)
[DEBUG] P114pfs is 0x4004086c (hello_ra4m1 uart/src/main.rs:286)
[DEBUG] P115pfs is 0x4004086c (hello_ra4m1 uart/src/main.rs:288)
[DEBUG] P010pfs is 0x40040828 (hello_ra4m1 uart/src/main.rs:290)
[DEBUG] P011pfs is 0x40040828 (hello_ra4m1 uart/src/main.rs:292)
[DEBUG] P012pfs is 0x40040828 (hello_ra4m1 uart/src/main.rs:294)
[DEBUG] P013pfs is 0x40040828 (hello_ra4m1 uart/src/main.rs:296)
[DEBUG] P014pfs is 0x40040828 (hello_ra4m1 uart/src/main.rs:298)
[DEBUG] P015pfs is 0x40040828 (hello_ra4m1 uart/src/main.rs:300)
[DEBUG] P500pfs is 0x40040940 (hello_ra4m1 uart/src/main.rs:302)
[DEBUG] P501pfs is 0x40040940 (hello_ra4m1 uart/src/main.rs:304)
[DEBUG] P502pfs is 0x40040940 (hello_ra4m1 uart/src/main.rs:306)
[DEBUG] P503pfs is 0x40040940 (hello_ra4m1 uart/src/main.rs:308)
[DEBUG] P504pfs is 0x40040940 (hello_ra4m1 uart/src/main.rs:310)
[DEBUG] P505pfs is 0x40040940 (hello_ra4m1 uart/src/main.rs:312)
[INFO ] RXD: aaa (hello_ra4m1 uart/src/main.rs:694)

このデバッグプリントはレジスタアドレスが変だったから。

DAPLINKのCOMポートをシリアルモニターで開くと、MCU内蔵温度センサーの値を1秒ごとに垂れ流す。

 36.2432 C
 36.0760 C
 36.0760 C
 36.4104 C
 36.2432 C
 36.8285 C
 35.9924 C
 36.5776 C
 36.2432 C

Discussion