初心者が組み込みRustやってみる - その3

初心者が組み込みRustやってみる - その2 - 雨垂れ石を穿つ
のつづき。

6. Hello, world!

  • iprintln!panic! マクロは、マイクロコントローラのITMにメッセージを出力する。
  • ITM は Instrumentation Trace Macrocell の略で、SWD(Serial Wire Debug)の上で通信するプロトコル
  • マイクロコントローラからデバッグしているホストPCにメッセージを送ることができる。
  • OpenOCD はマイコンとホストPCを接続するためのもので、その上でITMプロトコルで受信したデータを解釈するためのツールが itmdump ?

冒頭に

注意 ユーザーマニュアル(page 21)ではんだ付けしなければならないと書いてあるにも関わらず、 STM32F3DISCOVERY上のSB10「はんだブリッジ」(ボードの裏を見て下さい)がはんだ付けされていない、と複数の読者が報告しています。 これは、後ほど出てくるITMとiprint!マクロを使うために必要です。
TL;DR 2つの選択肢があります。SB10はんだブリッジをはんだ付けするか、下記写真の通りSW0とPB3の間をワイヤで接続するか、です。

のように注意書きがあり、これをしないと itmdump を実行しているターミナルに何も出力されなかった。
(はんだ付けされているように見えたが、よくわからず。)
指示の通りジャンパワイヤで接続することで、itmdumpを実行したコマンドプロンプト上ににログが出力された。

7. レジスタ

  • レジスタに値を書き込むことで、ペリフェラル(今回はGPIO)を制御することができる。
    • LEDと接続されているGPIOピンにhighを書き込むことで、LEDが点灯する。
  • レジスタの詳細を知るには、リファレンスマニュアルを読む。

7.1. RTRM(Read The Reference Manual)

  • マニュアルを読むことで、メモリのどのアドレスがどのレジスタに対応するかがわかる

7.2. (誤った)最適化

コンパイラによる最適化によって、意図しない動きをしてしまうことがある。
元のプログラム

        // A magic address!
        const GPIOE_BSRR: u32 = 0x48001018;

        // Turn on the "North" LED (red)
        *(GPIOE_BSRR as *mut u32) = 1 << 9;

        // Turn on the "East" LED (green)
        *(GPIOE_BSRR as *mut u32) = 1 << 11;

        // Turn off the "North" LED
        *(GPIOE_BSRR as *mut u32) = 1 << (9 + 16);

        // Turn off the "East" LED
        *(GPIOE_BSRR as *mut u32) = 1 << (11 + 16);

コンパイラによる最適化
同じアドレスへの書き込みは無駄なので、最後の書き込みだけが有効となる。

        // A magic address!
        const GPIOE_BSRR: u32 = 0x48001018;

        // Turn off the "East" LED
        *(GPIOE_BSRR as *mut u32) = 1 << (11 + 16);

誤った最適化を避けるには、通常の読み書きの代わりにvolatile操作 core::ptr::write_volatile を使う。

7.3. 0xBAAAAAAD番地

  • 不正な番地(そのアドレスにレジスタがない)にアクセスした場合、例外が発生する。
  • gdbのコマンドを使うことで、例外が発生した際のプログラムカウンタやレジスタの値を確認することができる。

7.5. 型安全な操作

16進数のアドレスを直接扱ってメモリにアクセスすることは間違いのもと。
安全な方法でレジスタにアクセスするためのAPIを使用する。

最初のプログラム

#![no_main]
#![no_std]

#[allow(unused_imports)]
use aux7::{entry, iprint, iprintln};

#[entry]
fn main() -> ! {
    aux7::init();

    unsafe {
        // 魔法のアドレス!
        const GPIOE_BSRR: u32 = 0x48001018;

        // 「北」のLED(赤)を点灯します
        *(GPIOE_BSRR as *mut u32) = 1 << 9;

        // 「東」のLED(緑)を点灯します
        *(GPIOE_BSRR as *mut u32) = 1 << 11;

        // 「北」のLEDを消灯します
        *(GPIOE_BSRR as *mut u32) = 1 << (9 + 16);

        // 「東」のLEDを消灯します
        *(GPIOE_BSRR as *mut u32) = 1 << (11 + 16);
    }

    loop {}
}


安全な方法(API)を使用したプログラム

#![no_main]
#![no_std]

#[allow(unused_imports)]
use aux7::{entry, iprint, iprintln};

#[entry]
fn main() -> ! {
    let gpioe = aux7::init().1;

    // 北のLEDを点灯
    gpioe.bsrr.write(|w| w.bs9().set_bit());

    // 東のLEDを点灯
    gpioe.bsrr.write(|w| w.bs11().set_bit());

    // 北のLEDのを消灯
    gpioe.bsrr.write(|w| w.br9().set_bit());

    // 東のLEDを消灯
    gpioe.bsrr.write(|w| w.br11().set_bit());

    loop {}
}

このAPIは、 https://crates.io/crates/svd2rust のツールを使用して、System View Description (SVD) ファイルから自動生成されている。
SVDファイルはマイクロコントローラのベンダが提供するXMLファイルで、レジスタブロックのレイアウトやベースアドレス、レジスタのRead/Writeのパーミッションなどの情報が含まれる。