Rustにはembedded-halというハードウェアを抽象化するクレートがあります。
embedded-halを導入することで煩雑なレジスタ操作から解放され、コードの流用性も高まります。
Nucleo-F303K8(デバイス:STM32F303K8)を例にしてembedded-halを導入する方法を説明します。
環境・デバイス
やりたいこと: STM32でembedded-halを導入してRust開発を行う
ボード: Nucleo-F303K8
PC: Windows10
PCには既にRust開発環境が準備されているものとします。まだの方は下記の記事を参考にして環境構築をしてみてください。
embedded-halとは?
embedded-halとは、組み込みシステム向けに現在も開発が進められているクレートになります。
マイコンの各種機能の抽象化を行うことが目的で、Digital I/O, ADC, Serial ような機能に対してのトレイトを定義しています。
embedded-halを導入することで、煩雑なレジスタ操作から解放され、コードの流用性も高まります。
ここでコードを見比べてみましょう。まずはembedded-halを使わずに記述したLED制御コードです。
let peripherals = stm32f303::Peripherals::take().unwrap();
peripherals.RCC.ahbenr.modify(|_, w| w.iopben().enabled());
let gpiob = &peripherals.GPIOB;
gpiob.moder.modify(|_, w| w.moder3().output());
gpiob.otyper.modify(|_, w| w.ot3().push_pull());
gpiob.bsrr.write(|w| w.bs3().set());
次にembedded-halを使って記述したLED制御コードを見てみましょう。
let dp = pac::Peripherals::take().unwrap();
let mut gpiob = dp.GPIOB.split(&mut rcc.ahb);
let mut led = gpiob.pb3.into_push_pull_output(
&mut gpiob.moder,
&mut gpiob.otyper);
led.set_high().unwrap();
embedded-halを使わないコードでは、4~6行目などで、GPIOのレジスタを直接書き換えています。このようなコードは、デバイスのデータシートを開いてレジスタをチェックしなければならず、大変です。コードの流用性もありません。
それに対してembedded-halを使った場合、レジスタ操作はHAL関数に隠されています。embedded-halをつかう限りはマイコンが変わってもHAL関数は変わりませんので、移植も簡単に行えます。
embedded-halについてより詳しく確認したいかたは、公式ドキュメントやコードを確認してみてください。下記のリンクから確認が出来ます。
embedded-hal – crates.io: Rust Package Registry
stm32f3xx_halの導入
stm32f3xx_halとは?
実は、embedded-halそれ自体には各機能のトレイトが記述されているだけです。このクレートだけで組み込み開発が行える訳ではありません。
実際に組み込み開発を行う際には、使用するマイコン向けにembedded-halのトレイトを実装したHALを使用することになります。
STマイクロのSTM32F3シリーズ用には、stm32f3xx-halというクレートがあります。これを使う事で、STM32F3シリーズの各種機能を簡単に使う事ができるようになります。
導入の方法
それでは、プロジェクトにstm32f3xx_halを導入していきましょう。導入するのは、下記の記事で作成したプロジェクトに対して行っていきます。
編集する必要のあるファイルは、”Cargo.toml”になります。こちらで、クレートの依存関係を編集してやれば良いのです。具体的には、ファイル内の [dependencies]について下記のように書き換えてしまいます。
[dependencies]
cortex-m = "0.7.2"
cortex-m-rt = { version = "0.6.13", features = ["device"] }
panic-halt = "0.2.0"
stm32f3xx-hal = { version = "0.7.0", features = ["ld", "rt", "stm32f303x8"] }
基本的にはコピペで構いませんが、最後のstm32f3xx-halとの依存関係の定義にポイントがあります。下の赤線部分にて、STM32F3シリーズ内のどのデバイスを使用するのかを定義できます。
features = [“ld”, “rt”, “stm32f303x8“]
もし他のSTM32F3シリーズを使いたい場合は、ここを書き換える必要があります。書き換える事が出来る(対応しているデバイス)については、リンク先のドキュメントを確認してください。stm32f3xx-hal
”Cargo.toml”を上記のように書き換えることで、stm32f3xx_halをコード内で使用することが出来るようになります。
サンプルコードの実行
サンプルコードとして、/src/main.rsのコードを下記のようにしました。
#![no_std]
#![no_main]
use cortex_m::asm;
use cortex_m_rt::entry;
use panic_halt as _;
use stm32f3xx_hal::{pac, prelude::*};
#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();
let mut gpiob = dp.GPIOB.split(&mut rcc.ahb);
let mut led = gpiob
.pb3
.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);
loop {
led.toggle().unwrap();
asm::delay(2_000_000);
}
}
PB3ポートに接続されているLEDをチラつかせるサンプルコードです。
PB3ポートをpush_pull出力に設定した後、メインループ内のtoggle()関数で出力状態を反転させています。
サンプルコードが出来たら、Nucleo-F303K8ボードをPCに接続して、実行をしてみましょう。ボード上の黄緑LED(LD3)が点滅すれば成功です。
まとめ
embedded-halはハードウェアを抽象化するクレートです。
embedded-halのAPIを使用することで、煩雑なレジスタ操作から解放されコードの流用性も高まります。
実際に組み込み開発で使用するのは、”stm32f3xx-hal”のような各マイコン向けに導出されたものとなります。
今回の記事で作成したstm32f3xx-halを使用するプロジェクトをGithubに上げておきましたので、参考にしてください。
tteio/stm32f303k8: Embedded Rust stm32f303 quick start crates. (github.com)
コメント