Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

1 バイトを送信する

最初のタスクは、シリアル接続を介してマイクロコントローラからコンピュータへ 1 バイトを送信することです。

そのために、次のスニペットを使用します(これはすでに 07-uart/src/main.rs にあります):

#![no_main]
#![no_std]

use cortex_m_rt::entry;
use panic_rtt_target as _;
use rtt_target::rtt_init_print;

#[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 = "v1")]
use embedded_io::Write;

#[cfg(feature = "v2")]
use embedded_hal_nb::serial::Write;

#[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 = {
        // Set up UART for microbit v1
        let serial = uart::Uart::new(
            board.UART0,
            board.uart.into(),
            Parity::EXCLUDED,
            Baudrate::BAUD115200,
        );
        serial
    };

    #[cfg(feature = "v2")]
    let mut serial = {
        // Set up UARTE for microbit v2 using UartePort wrapper
        let serial = uarte::Uarte::new(
            board.UARTE0,
            board.uart.into(),
            Parity::EXCLUDED,
            Baudrate::BAUD115200,
        );
        UartePort::new(serial)
    };

    // Write a byte and flush
    #[cfg(feature = "v1")]
    serial.write(&[b'X']).unwrap(); // Adjusted for UART on v1, no need for nb::block!

    #[cfg(feature = "v2")]
    {
        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 をうまくラップして、embedded_hal::serial トレイトを通じて UART とまったく同じように使えるようにすることです。必要であればこのモジュールが具体的に何をしているか確認してもかまいませんが、この章を理解するうえでは必須ではありません。

それらの違いを除けば、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 ペリフェラルやピンを勝手に触ることができなくなります。その後、2 つの設定オプションをコンストラクタに渡します。1 つは baudrate(これはもうおなじみでしょう)、もう 1 つは「parity」と呼ばれるオプションです。Parity は、シリアル通信線が受信したデータが伝送中に破損していないか確認できるようにする仕組みです。ここではそれを使いたくないので、単に除外します。次に、それを 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 章と同じようにプログラムを書き込めます:

# micro:bit v2 の場合
$ cargo embed --features v2 --target thumbv7em-none-eabihf
  (...)

# micro:bit v1 の場合
$ cargo embed --features v1 --target thumbv6m-none-eabi

書き込みが完了したら、minicom/PuTTY のターミナルに文字 X が表示されるはずです。おめでとうございます!