embedded-trainings-2020/embedded-workshop-book/src/uarte-implementation.md

104 lines
3.9 KiB
Markdown
Raw Normal View History

2023-02-21 15:24:57 +00:00
# Write the Uarte implementation
## Step-by-Step Solution
2023-02-27 16:00:21 +00:00
2023-03-07 16:52:41 +00:00
### Step 1: Check Documentation.
2023-02-27 16:00:21 +00:00
The UART protocol requires four pins, they are usually labelled:
2023-02-22 18:11:41 +00:00
* RXD
* TXD
* CTS
* RTS
2023-03-13 12:29:44 +00:00
2023-03-07 16:52:41 +00:00
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.
2023-02-27 16:00:21 +00:00
2023-03-07 16:52:41 +00:00
### Step 2: Explore the `nrf-hal` to find out what needs to be done.
2023-02-22 18:11:41 +00:00
2023-03-07 16:52:41 +00:00
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](https://github.com/nrf-rs/nrf-hal/blob/v0.14.1/nrf-hal-common/src/uarte.rs).
2023-02-22 18:11:41 +00:00
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 463
* `Parity` and `Baudrate`: Re-export on line 34
Add the following lines as import:
2023-03-13 12:29:44 +00:00
```
2023-02-22 18:11:41 +00:00
use hal::pac::uarte0::{
baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
use hal::uarte;
```
2023-03-07 16:52:41 +00:00
### Step 3: Add `struct Uarte` that serves as a wrapper for the `UARTE1` instance.
2023-02-22 18:11:41 +00:00
2023-03-07 16:52:41 +00:00
The struct has one field labelled `inner`, it contains the `UARTE1` instance: `hal::Uarte<hal::pac::UARTE1>`.
<!-- Solution Code Snippet -->
### Step 4: Bring up the peripheral in the `fn init()`
2023-02-22 18:11:41 +00:00
2023-03-13 12:29:44 +00:00
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.
2023-03-07 16:52:41 +00:00
Create an instance of this struct in `fn init()` with the appropriate pins and configurations. Set the output pin's level to `Level::High`.
2023-03-13 12:29:44 +00:00
Note, that the third and fourth pin are each wrapped in an `Option`.
2023-02-22 18:11:41 +00:00
2023-03-07 16:52:41 +00:00
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 the `periph` variable.
* Your instance of the `uarte::Pins` struct.
* Set parity to `Parity::INCLUDED`
* set the baud rate to `Baudrate::BAUD115200`.
2023-02-22 18:11:41 +00:00
2023-03-07 16:52:41 +00:00
<!-- Solution Code Snippet -->
### Step 5: Board struct
2023-02-22 18:11:41 +00:00
2023-03-07 16:52:41 +00:00
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 Code Snippet -->
### Step 6: Implementing the `fmt::Write` trait
2023-02-22 18:11:41 +00:00
2023-03-13 12:29:44 +00:00
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.
2023-02-22 18:11:41 +00:00
2023-02-27 16:00:21 +00:00
Create a public method `write_str`. It takes a mutable reference to self and a `&str` as argument. It returns an `fmt::Result`
2023-03-07 16:52:41 +00:00
Create a buffer. The type is an `array` of 16 u8, set to all 0.
2023-02-27 16:00:21 +00:00
To copy all data into an on-stack buffer, iterate over every chunk of the string to copy it into the buffer:
```rust
for block in string.as_bytes().chunks(16) {
buf[..block.len()].copy_from_slice(block);
self.inner.write(&buf[..block.len()]).map_err(|_| fmt::Error)?;
}
```
return `Ok(())`
2023-03-07 16:52:41 +00:00
### Step 7: Connect your computer to the virtual UART
[directions for mac present, linux and windows are missing.]
2023-02-27 16:00:21 +00:00
Use the following command to find the address of the nRF52840-DK on your computer.
2023-02-22 18:11:41 +00:00
2023-02-27 16:00:21 +00:00
```
2023-03-13 12:29:44 +00:00
ls /dev/tty.usbmodem*
2023-02-27 16:00:21 +00:00
```
Run the following command to run `screen` with the nRF52840-DK with 115200 baud.
```
2023-03-07 16:52:41 +00:00
screen <address of mc> 115200
2023-02-27 16:00:21 +00:00
```
2023-03-07 16:52:41 +00:00
### Step 7: Run the example.
2023-02-27 16:00:21 +00:00
In another terminal window go into the folder `down-the-stack/apps`.
Use the following command.
```
cargo run --bin uarte_print
```
2023-02-21 15:24:57 +00:00
2023-03-13 12:29:44 +00:00
On your terminal window where `screen` runs, "Hello, World" should appear.
You need to terminate `screen` manually.