Hello, world!

注意 ユーザーマニュアル(page 21)ではんだ付けしなければならないと書いてあるにも関わらず、 STM32F3DISCOVERY上のSB10「はんだブリッジ」(ボードの裏を見て下さい)がはんだ付けされていない、と複数の読者が報告しています。 これは、後ほど出てくるITMとiprint!マクロを使うために必要です。

TL;DR 2つの選択肢があります。SB10はんだブリッジをはんだ付けするか、下記写真の通りSW0とPB3の間をワイヤで接続するか、です。


低レベルのことを始める前に、もう少しだけ役立つ魔法を学んで下さい。

LEDを点滅させることは、組込みの世界の「Hello, world」です。

しかし、このセクションでは、ラップトップのコンソールに出力するちゃんとした「Hello, world」プログラムを実行します。

06-hello-worldディレクトリに移動して下さい。その中にスターターコードがあります。

#![deny(unsafe_code)]
#![no_main]
#![no_std]

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

#[entry]
fn main() -> ! {
    let mut itm = aux6::init();

    iprintln!(&mut itm.stim[0], "Hello, world!");

    loop {}
}

iprintlnマクロは、メッセージを整え、マイクロコントローラのITMに出力します。ITMは、Instrumentation Trace Macrocellの略であり、 SWD(Serial Wire Debug)の上で通信するプロトコルです。これは、マイクロコントローラからデバッグしているホストにメッセージを送るために使います。 この通信は、一方向だけです。デバッグしているホストは、マイクロコントローラにデータを送ることができません。

OpenOCDは、デバッグセッションを管理し、ITMチャネルを通して送信されたデータを受信し、ファイルにリダイレクトします。

ITMプロトコルは、フレーム(イーサネットフレートのようなものだと考えて下さい)で動作します。各フレームは、ヘッダと可変長のペイロードを持ちます。 OpenOCDは、フレームを受信し、フレームを解析せずに、直接ファイルに書き込みます。 マイクロコントローラが、iprintlnマクロを使用して「Hello, world!」という文字列を送信した場合、 OpenOCDの出力ファイルは、その文字列をそのまま含んでいるわけではありません。

元の文字列を復元するために、OpenOCDの出力ファイルを解析しなければなりません。 届いた新しいデータの解析を行うために、itmdumpプログラムを使用します。

既にitmdumpプログラムをインストールの章でインストールしているはずです。

*nix OSを使っている場合、新しい端末の/tmpディレクトリ下で、Windowsを使っている場合、%TEMP%ディレクトリ下で、 次のコマンドを実行して下さい。これはOpenOCDを実行しているのと、同じディレクトリである必要があります。

注記 itmdumpopenocdとの両方が、同じディレクトリで実行していることが、非常に重要です。

$ # itmdumpする端末

$ # *nix
$ cd /tmp && touch itm.txt

$ # Windows
$ cd %TEMP% && type nul >> itm.txt

$ # 両方
$ itmdump -F -f itm.txt

このコマンドは、itmdumpitm.txtを監視している間、ブロックします。この端末は開いたままにしておきます。

では、スターターコードをビルドして、マイクロコントローラのFlashに書き込みましょう。

--target thumbv7em-none-eabihfフラグをCargo呼び出しごとに渡さなくて済むように、.cargo/configにデフォルトターゲットを設定できます。

 [target.thumbv7em-none-eabihf]
 runner = "arm-none-eabi-gdb -q -x openocd.gdb"
 rustflags = [
   "-C", "link-arg=-Tlink.x",
 ]

+[build]
+target = "thumbv7em-none-eabihf"

これで、--targetが指定されない場合、Cargoは、ターゲットがthumbv7em-none-eabihfだと想定しましす。

$ cargo run
Reading symbols from target/thumbv7em-none-eabihf/debug/hello-world...done.
(..)
Loading section .vector_table, size 0x400 lma 0x8000000
Loading section .text, size 0x27c4 lma 0x8000400
Loading section .rodata, size 0x744 lma 0x8002be0
Start address 0x8002980, load size 13064
Transfer rate: 18 KB/sec, 4354 bytes/write.
Breakpoint 1 at 0x8000402: file src/06-hello-world/src/main.rs, line 10.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, main () at src/06-hello-world/src/main.rs:10
10          let mut itm = aux6::init();

Cargoプロジェクトのルートディレクトリに.gdbinitがあることに留意して下さい。 これは、前のセクションで使ったものと非常によく似ています。

iprintln!ステートメントを実行する前に、itmdumpが監視しているファイルと同じファイルに対して、OpenOCDがITM出力をリダイレクトするように指示しなければなりません。

(gdb) # ITMをグローバルに有効化し、itm.txtに全ての出力をリダイレクトします
(gdb) monitor tpiu config internal itm.txt uart off 8000000

(gdb) # ITMポート0を有効にします
(gdb) monitor itm port 0 on

全ての準備が整ったはずです!では、iprintlnステートメントを実行します。

(gdb) next
12          iprintln!(&mut itm.stim[0], "Hello, world!");

(gdb) next
14          loop {}

itmdump端末に、何らかの出力が見られるはずです。

$ itmdump -F -f itm.txt
(..)
Hello, world!

素晴らしい、そう思いませんか?以降のセクションでiprintlnをロギングツールとして、自由に活用して下さい。

次:これで全てではありません!ITMを使うのは、iprint!マクロはだけではありません。:-)