組み込みRust開発【STM32をembedded-halで制御】

Hello_Rust_Worldソフト

Rustにはembedded-halというハードウェアを抽象化するクレートがあります。

embedded-halを導入することで煩雑なレジスタ操作から解放され、コードの流用性も高まります。

Nucleo-F303K8(デバイス:STM32F303K8)を例にしてembedded-halを導入する方法を説明します。

こんな方におすすめの記事
  • 組み込みRustの開発に興味のある方

環境・デバイス

やりたいこと:  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

リンク先のcrates.ioというサイトは、様々なクレートが登録されているRustのコミュニティーサイトになります。

何万というクレートが登録/公開されているので、Rustのライブラリを探す際にはこのサイトで検索するのが良さそうです。

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)

コメント