1バイト送信する
最初のタスクは、シリアル通信でマイクロコントローラからコンピュータに1バイト送ることです。
そのために、以下のコードを使いましょう。(07-uart/src/main.rs
中にあるものです。)
#![no_main] #![no_std] use cortex_m_rt::entry; use rtt_target::rtt_init_print; use panic_rtt_target as _; #[cfg(feature = "v1")] use microbit::{ hal::prelude::*, hal::uart, hal::uart::{Baudrate, Parity}, }; #[cfg(feature = "v2")] use microbit::{ hal::prelude::*, hal::uarte, hal::uarte::{Baudrate, Parity}, }; #[cfg(feature = "v2")] mod serial_setup; #[cfg(feature = "v2")] use serial_setup::UartePort; #[entry] fn main() -> ! { rtt_init_print!(); let board = microbit::Board::take().unwrap(); #[cfg(feature = "v1")] let mut serial = { uart::Uart::new( board.UART0, board.uart.into(), Parity::EXCLUDED, Baudrate::BAUD115200, ) }; #[cfg(feature = "v2")] let mut serial = { let serial = uarte::Uarte::new( board.UARTE0, board.uart.into(), Parity::EXCLUDED, Baudrate::BAUD115200, ); UartePort::new(serial) }; nb::block!(serial.write(b'X')).unwrap(); nb::block!(serial.flush()).unwrap(); loop {} }
まず目新しいものといえば、cfg
ディレクティブでしょう。これは条件によって特定のコードセクションをソースに含めたり除外したりするためのもので、ここではmicro:bit v1用にUART、micro:bit v2用にはUARTEを使用するよう指定しています。
また、ここで初めてライブラリ外のコードを取り込んでいることにもお気づきでしょう。serial_setup
モジュールのことです。UARTEのラッパであるこのモジュールを使うことで、UARTEもUARTとまったく同じようにembedded_hal::serial
トレイト経由で扱うことができます。この章の理解には必要ありませんが、もし興味があればモジュールの設計をのぞいてみてください。
これらの違いを除けば、UARTとUARTEの初期化手続きはよく似ています。ですからここではUARTEの初期化についてだけ解説します。UARTEは以下のコードで初期化します。
uarte::Uarte::new(
board.UARTE0,
board.uart.into(),
Parity::EXCLUDED,
Baudrate::BAUD115200,
);
この関数はRustで表現されたUARTEペリフェラル(board.UARTE0
)、ならびにTX/RXピン(board.uart.into()
)の所有権を取得します。こうすることで、私たちの使っているUARTEとピンを他で使えないようにできます。次に、ふたつの設定オプションをコンストラクタに渡します。ボーレート(Baudrate
)とパリティ(Parity
)です。パリティはシリアル通信ラインに受信したデータが破損していないか確認することを可能にするオプションです。ここでは使いませんので、除外(EXCLUDED
)しておきましょう。最後にUartePort
型で包んでやります。こうすることで、micro:bit v1の serial
と同じように扱うことが可能になります。
初期化できたら、今作ったばかりのUARTインスタンスでX
を送ります。ここに出てくるblock!
マクロは、nb::block!
マクロです。nb
は、「最小限かつ再利用可能なノンブロッキングI/O層」(公式ドキュメントからの引用)です。これによって、バックグラウンドでハードウェア操作を行うあいだに別タスクを処理(ノンブロッキング)することができます。ですが、このケースでは平行して別の処理はしないので、ここではただblock!
を呼び、I/O処理の成功・失敗を待ってからプログラムの実行を続けることにします。
最後に、シリアルポートをflush()
します。なぜかというと、embedded-hal::serial
トレイトの実装によっては、送信するデータが一定のバイト数になるまで、送信バッファに貯めておく実装になっていることがあるからです。(実際にUARTEはそのように実装されています。)flush()
を呼ぶことで、送信データをそれ以上待たずに、送信バッファの内容を強制的に出力させることができるのです。
テストしましょう
プログラムを書き込む前に、忘れずにminicom/PuTTYをスタートしてください。シリアル通信で受信するデータは、どこかに保存されるわけではなく、リアルタイムに観察する必要があるからです。シリアルモニタが立ち上がったら、5章でしたようにプログラムを書き込みましょう。
# For micro:bit v2
$ cargo embed --features v2 --target thumbv7em-none-eabihf
(...)
# For micro:bit v1
$ cargo embed --features v1 --target thumbv6m-none-eabi
書き込みが終わると、X
の文字がminicom/PuTTYのターミナルに出るはずです。おめでとうございます!