4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rustを使ったマイコン開発!STM32F401でLチカとRTT出力をやってみた

4
Posted at

1. なぜRustに興味を持ったか(動機)

きっかけは、2026年4月にリリース予定の次世代Linux Ubuntu 26.04 LTS "Resolute Raccoon" における「セキュリティ強化」の流れでした。
【Ubuntu 26.04 LTS】セキュリティ革命の全容:Rust化と脱X11で「世界一安全なLinux」へ

Ubuntu 26.04 では、OS の安全性を高めるため、長年 C 言語で書かれてきた OS の基幹コンポーネント(sudocoreutils など)を、Rust 製の sudo-rsuutils へと試験的に、あるいは標準的に置き換える動きが加速しています。

「OS の最深部が C から Rust へと置き換わる時代なら、自分も Rust を使えるようになった方がいいのでは?」と考えたのが学習の始まりです。

2. Rust ってどんな言語?(組み込み視点)

Ubuntu 26.04 での動向を背景に、現時点で期待されているメリットを検証していきたいと考えています。

  • C 言語と同等の実行速度(への期待)
    ガベージコレクション(GC)がないため実行時のオーバーヘッドが極小であり、リアルタイム性が求められる制御系でも C/C++ と同等のパフォーマンスを発揮できると言われています。
  • メモリ安全性の向上
    C/C++ で頻発し、脆弱性の原因となりやすいバッファオーバーフローなどを、コンパイル時に自動検出して防ぐ仕組みが備わっています。
    「実行して落ちる」のではなく「コンパイルが通ればメモリ周りは安全」という感覚を、今後の開発で体感していきたいポイントです。
  • 機能制限のなさ(自由度との両立)
    unsafe ブロックという仕組みを活用すれば、C 言語と同様にレジスタを直接叩くような低レイヤーな操作もすべて可能とされています。
    安全性をうたいつつ、組み込みに必要な「泥臭い操作」がどこまでストレスなく行えるのか、実際に使い込んで確かめていこうと思います。

3. 実行環境

  • ボード: Nucleo-F401RE / Black Pill (STM32F401)
  • OS: Windows 10
  • ツールチェーン: rustup, probe-rs

4. 環境構築手順

Windows 環境で STM32F401 を動作させるための手順です。

① Rust 本体のインストール

公式サイト (rustup.rs) から rustup-init.exe をダウンロードし、デフォルト設定でインストールします。

② マイコン用コンパイラ(ターゲット)の追加

STM32F401 は Cortex-M4 アーキテクチャを採用しているため、専用のコンパイルターゲットを追加します。ターミナル(PowerShell等)で以下を実行します。

rustup target add thumbv7em-none-eabihf
  • thumbv7em: アーキテクチャ (Cortex-M4)
  • none: OSなし
  • eabihf: 浮動小数点演算 (Hard Float) 対応

③ 書き込み・デバッグツール probe-rs の導入

Rust エコシステムで標準的な probe-rs をインストールします。これ 1 つで「書き込み・リセット・RTT ログ表示」が完結します。

powershell -ExecutionPolicy Bypass -c "irm https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-tools-installer.ps1 | iex"

④ VS Code の設定

拡張機能 「rust-analyzer」 をインストールします。コード補完が強力になります。
image.png

5. プロジェクトの作成と設定

プロジェクトの新規作成

任意の作業フォルダで以下を実行します。

cargo new my-stm32
cd my-stm32

設定完了後のファイル構成は以下のようになります。
このコマンドにより、フォルダ構成と共に Cargo.toml(設定ファイル)や src/main.rs(ソースコードの雛形)が自動的に生成されます

自動生成されたファイルを「組み込み用」に整える

自動生成された直後の main.rs は「Hello, world!」を表示するパソコン用(標準ライブラリ std 依存)のコードになっています。組み込みでは OS がないため、これらを手動で追加・書き換えして環境を整えます。

最終的なディレクトリツリー

my-stm32/
├── .cargo/          <-- 手動作成
│   └── config.toml  <-- 手動作成(書き込み設定)
├── src/
│   └── main.rs      <-- 自動生成されるが、中身を「全消し」して書き換える
├── Cargo.toml       <-- 自動生成されるが、依存関係を追記する
└── memory.x         <-- 手動作成(メモリ配置定義)

各ファイルの設定内容

Cargo.toml の編集(自動生成分に追記)

[dependencies] に、STM32F401 用の HAL(ハードウェア抽象化レイヤー)を追記します。

[dependencies]
stm32f4xx-hal = { version = "0.17.1", features = ["stm32f401", "rt"] }
cortex-m = "0.7.7"
cortex-m-rt = "0.7.3"
panic-halt = "0.2.0"
rtt-target = "0.6"

.cargo/config.toml の作成

プロジェクト直下に .cargo フォルダを作り、その中に config.toml を作成します。

[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# 書き込みツールの指定
runner = "probe-rs run --chip STM32F401VBTx"
# コンパイルオプション
rustflags = [
  "-C", "link-arg=-Tlink.x",
]
[build]
# デフォルトのターゲット設定
target = "thumbv7em-none-eabihf"

memory.x の作成

プロジェクト直下に作成し、メモリ配置を定義します。

MEMORY
{
  FLASH : ORIGIN = 0x08000000, LENGTH = 256K
  RAM   : ORIGIN = 0x20000000, LENGTH = 64K
}

src/main.rs のコードと、その詳細解説です。

#![no_std]
#![no_main]

use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{pac, prelude::*};
// RTTライブラリの使用
use rtt_target::{rprintln, rtt_init_print};

#[entry]
fn main() -> ! {
    // 1. RTTの初期化(パソコンへの文字送信準備)
    rtt_init_print!();
    rprintln!("--- STM32 PA5 LED & Console Start! ---");

    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.MHz()).freeze();

    // 2. GPIOAを有効化
    let gpioa = dp.GPIOA.split();
    
    // 3. PA5を出力モードに設定
    let mut led = gpioa.pa5.into_push_pull_output();

    let mut delay = cp.SYST.delay(&clocks);

    loop {
        // コンソールに現在の状態を表示
        rprintln!("LED ON (PA5)");
        
        // PA5を点灯(Nucleoなどの場合はHighで点灯、Black Pill等はLowで点灯が多いです)
        led.set_high(); 
        delay.delay_ms(1000u32);

        rprintln!("LED OFF (PA5)");
        led.set_low();
        delay.delay_ms(1000u32);
    }
}

コードのポイント解説

  • #![no_std] と #![no_main]: 組み込みRustの基本です。PC用の便利な機能(画面出力やファイル操作)を切り捨て、マイコンの生の状態からスタートすることを意味します。
  • fn main() -> !: 戻り値の ! は「この関数は絶対に終了しない」という意味です。マイコンは電源を切るまでループし続けるため、この型を使います。
  • take().unwrap(): Rustの安全性を示す部分です。ハードウェア資源の所有権を1つしか存在させないことで、同時に複数の場所から同じレジスタを操作して壊すのを防いでいます。
  • rtt-target (RTT): 従来のシリアル通信(UART)より高速で、デバッグ線(ST-Link等)をそのまま使ってログを送れるため、配線が少なくて済みます。

6. 実行

ボードを PC に接続し、以下を実行します。

cargo run

書き込み完了直後から、ターミナルに LED ON / OFF のログが流れ、実機の LED が点滅します。

感想

C 言語に慣れていると最初は戸惑いますが、一度通してしまえばコンパイラがバグを事前に指摘してくれる安心感があります。Ubuntu 26.04 のようなモダンなシステムが Rust へ移行する理由を、これから自分なりに検証していきたいです。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?