Send a single byte

Our first task will be to send a single byte from the microcontroller to the computer over the serial connection.

In order to do that we will use the following snippet (this one is already in 10-uart/examples/send-byte.rs):

#![no_main]
#![no_std]

use cortex_m::asm::wfi;
use cortex_m_rt::entry;
use panic_rtt_target as _;
use rtt_target::rtt_init_print;

use microbit::{
    hal::uarte,
    hal::uarte::{Baudrate, Parity},
};

use serial_setup::UartePort;

#[entry]
fn main() -> ! {
    rtt_init_print!();
    let board = microbit::Board::take().unwrap();

    let mut serial = {
        let serial = uarte::Uarte::new(
            board.UARTE0,
            board.uart.into(),
            Parity::EXCLUDED,
            Baudrate::BAUD115200,
        );
        UartePort::new(serial)
    };

    serial.write(b'X').unwrap();
    serial.flush().unwrap();

    loop {
        wfi();
    }
}

You might notice that one of the libraries used here, the serial_setup module, is not from crates.io, but was written for this project. The purpose of serial_setup is to provide a nice wrapper around the UARTE peripheral. If you want, you can check out what exactly the module does, but it is not required to understand this chapter in general.

We'll next discuss the initialization of UARTE. The UARTE is initialized with this piece of code:

uarte::Uarte::new(
    board.UARTE0,
    board.uart.into(),
    Parity::EXCLUDED,
    Baudrate::BAUD115200,
);

This function takes ownership of the UARTE peripheral representation in Rust (board.UARTE0) and the TX/RX pins on the board (board.uart.into()) so nobody else can mess with either the UARTE peripheral or our pins while we are using them. After that we pass two configuration options to the constructor: the baudrate (that one should be familiar) as well as an option called "parity". Parity is a way to allow serial communication lines to check whether the data they received was corrupted during transmission. We don't want to use that here so we simply exclude it. Then we wrap it up in the UartePort type so we can use it.

After the initialization, we send our X via the newly created uart instance. These serial functions are "blocking": they wait for the data to be sent before returning. This is not always what is wanted: the microcontroller can do a lot of work while waiting for the byte to go out on the wire. However, in our case it is convenient and we didn't have other work to do anyway.

Last but not least, we flush() the serial port. This is because the UARTE may decide to buffer output until it has received a certain number of bytes to send. Calling flush() forces it to write the bytes it currently has right now instead of waiting for more.

Testing it

Before flashing this you should make sure to start your minicom/PuTTY as the data we receive via our serial communication is not backed up or anything: we have to view it live. Once your serial monitor is up you can flash the program just like in chapter 5:

$ cargo embed --example send-byte
  (...)

And after the flashing is finished, you should see the character X show up on your minicom/PuTTY terminal, congrats!

If you missed it, you can hit the reset button on the back of the MB2. This will cause the program to start from the beginning and send an X again.