5.5 KiB
Write the Uarte implementation
Step-by-Step Solution
Step 1: Check Documentation.
The UART protocol requires four pins, they are usually labelled:
- RXD
- TXD
- CTS
- RTS
✅ Check the User Guide in section 7.2 to find to find out which pins are reserved for these and what their configuration needs to be.
Step 2: Explore the nrf-hal
to find out what needs to be done.
The nrf52840-hal
is a crate that exports all the 52840
flagged features from the nrf-hal-common
. Let's take a look at the nRF-Hal Uarte module.
In line 16 we see, that the nRF52840 uses the hal::pac::UARTE1
peripheral.
In line 44 you find the struct Uarte<T>(T)
, the interface to a UARTE instance T
. Besides the instance T
, the instantiating method takes variables of the following types as arguments: Pins
, Parity
and Baudrate
.
A quick search of the document reveals where to find all of them:
Pins
: Line 463Parity
andBaudrate
: Re-export on line 34
✅ Add the following lines as import:
use hal::pac::uarte0::{
baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
use hal::uarte;
Step 3: Add struct Uarte
✅ Add struct Uarte
that serves as a wrapper for the UARTE1
instance.
The struct has one field labelled inner
, it contains the UARTE1
instance: hal::Uarte<hal::pac::UARTE1>
.
Solution
pub struct Uarte {
inner: hal::Uarte<hal::pac::UARTE1>,
}
Step 4: Bring up the peripheral in the fn init()
✅ Take a closer look at the definition of the uarte::Pins
struct in the nrf-hal
. Compare the pin type configurations with the ones you have already imported in lib.rs
. Add the ones you're missing.
✅ Create an instance of this struct in fn init()
with the appropriate pins and configurations. Set the output pin's level to Level::High
.
Note, that the third and fourth pin are each wrapped in an Option
.
✅ Create an interface to the UARTE1 instance with uarte::Uarte::new(...)
that you bind to a variable. This instantiating method takes four arguments:
- The
UARTE1
instance can be found in theperiph
variable. - Your instance of the
uarte::Pins
struct. - Set parity to
Parity::INCLUDED
- set the baud rate to
Baudrate::BAUD115200
.
Solution
let pins = hal::uarte::Pins {
rxd: pins.p0_08.degrade().into_floating_input(),
txd: pins.p0_06.degrade().into_push_pull_output(Level::High),
cts: Some(pins.p0_07.degrade().into_floating_input()),
rts: Some(pins.p0_05.degrade().into_push_pull_output(Level::High)),
};
let uarte = hal::uarte::Uarte::new(periph.UARTE1, pins, Parity::INCLUDED, Baudrate::BAUD115200);
Step 5: Board struct
✅ Add a field for the Uarte
struct in the Board
struct.
add the field to the instance of the Board
struct in fn init()
.
Solution
pub struct Board {
/// LEDs
pub leds: Leds,
/// Buttons
pub buttons: Buttons,
/// Timer
pub timer: Timer,
/// uarte interface
pub uarte: Uarte,
}
// ...
pub fn init() -> Result<Board, ()> {
// ...
Ok(Board {
leds: Leds {
// ...
},
buttons: Buttons {
// ...
},
// 🔼 --- Exercise Button --- 🔼
timer: Timer { inner: timer },
uarte: Uarte { inner: uarte },
})
} else {
Err(())
}
Step 6: Implementing the fmt::Write
trait
We can't just write to the Uarte
instance. A simple write would write from flash memory. This does not work because of EasyDMA. We have to write a function that implements the fmt::Write
trait. This trait guarantees that the buffer is fully and successfully written on a stack allocated buffer, before it returns.
✅ Add use::core::fmt;
to your imports.
✅ Create a public method write_str
. It takes a mutable reference to self and a &str
as argument. It returns an fmt::Result
✅ Create a buffer. The type is an array
of 16 u8, set to all 0.
✅ To copy all data into an on-stack buffer, iterate over every chunk of the string to copy it into the buffer.
Solution
impl fmt::Write for Uarte {
fn write_str(&mut self, s: &str) -> fmt::Result {
// Copy all data into an on-stack buffer so we never try to EasyDMA from
// flash.
let mut buf: [u8; 16] = [0; 16];
for block in s.as_bytes().chunks(16) {
buf[..block.len()].copy_from_slice(block);
self.inner.write(&buf[..block.len()]).map_err(|_| fmt::Error)?;
}
Ok(())
}
}
Step 7: Connect your computer to the virtual UART
[todo!] [directions for mac present, linux and windows are missing.]
✅ Use the following command to find the address of the nRF52840-DK on your computer.
ls /dev/tty.usbmodem*
✅ Run the following command to run screen
with the nRF52840-DK with 115200 baud.
screen <address of mc> 115200
Step 8: Run the example.
✅ In another terminal window go into the folder down-the-stack/apps
and use the following command.
cargo run --bin uarte_print
On your terminal window where screen
runs, "Hello, World" should appear.
You need to terminate screen
manually.