last formatting and adding code snippets

This commit is contained in:
Mirabellensaft 2023-03-14 19:39:50 +01:00
parent bc24f4d115
commit 3fd05d1f0f
3 changed files with 233 additions and 74 deletions

View file

@ -19,54 +19,48 @@ Note for trainer: Introduction to the exercise is a guided tour through the temp
* modify the `init()` function that brings up the board's peripherals
* how to configure pins
* how to write a function that checks the state of a pin
* write methods for a `struct`
* UARTE implementation
* implement functionality on a type
* implement a Trait
* to document and generate docs for your own library!
## Prerequisites
[todo!]
* `impl` keyword
* methods and associated functions
* `pub` keyword
* usage of structs to represent registers
* Trait
## Tasks
* Write a button implementation. This entails the following steps:
✅ Add `struct Buttons` with 4 fields, that represents each of the four buttons.
Add `struct Button` that is a wrapper for the pin that a single button is connected to.
Write a method `is_pushed` that checks if a single button is pushed.
Initialize the pins in `fn init()`.
Add the `struct Button` to the definition and instantiation of `struct Board`.
Run `apps/buttons.rs` to test.
Run `cargo doc` out of the apps folder to find all your doc comments!
* Write a UARTE implementation. This entails the following steps:
Check the `uarte` module of the `nrf-hal` for requirements of the instantiating method.
Add `struct Uarte` that serves as wrapper for the `UARTE1` instance.
Initialize the UARTE1 peripheral in `fn init()` using the following settings:
### Write a button implementation. This entails the following steps:
* Add `struct Buttons` with 4 fields, that represents each of the four buttons.
* Add `struct Button` that is a wrapper for the pin that a single button is connected to.
* Write a method `is_pushed` that checks if a single button is pushed.
* Initialize the pins in `fn init()`.
* Add the `struct Button` to the definition and instantiation of `struct Board`.
* Run `apps/buttons.rs` to test.
* Run `cargo doc` out of the apps folder to find all your doc comments!
### Write a UARTE implementation. This entails the following steps:
* Check the `uarte` module of the `nrf-hal` for requirements of the instantiating method.
* Add `struct Uarte` that serves as wrapper for the `UARTE1` instance.
* Initialize the UARTE1 peripheral in `fn init()` using the following settings:
* parity: included
* baudrate: 115200 baud
Add `struct Uarte` to the definition and instantiation of `struct Board`.
Implement the `fmt::Write` trait for `struct Uarte`.
Connect your computer to the virtual UART port with `screen`
Run `apps/uarte_print.rs` to test.
* Add `struct Uarte` to the definition and instantiation of `struct Board`.
* Implement the `fmt::Write` trait for `struct Uarte`.
* Connect your computer to the virtual UART port with `screen`.
* Run `apps/uarte_print.rs` to test.
## Knowledge
## Representation of Peripherals
The boards peripherals are represented as nested structs. The `struct Board` contains fields that represent single peripherals or groups of peripherals as structs, which in turn either contain a field of the single peripheral or ...
You have to add structs to represent the buttons and the UARTE peripheral to the board struct.
[todo!]
## Comments
### Comments
The `lib.rs` has an attribute #![deny(missing_docs)]. This means, that missing doc comments for structs are returned as compiler errors, to remind you to document your work properly.
```rust
/// This is a doc comment
// This is a normal comment
```
### Structs represent Registers
## impl blocks
[todo!]
## Visibility of structs, fields and functions: the pub keyword
[todo!]
[todo!] insert refresher from rust fundamentals
## Hardware documentation for pin configuration
Go to [Nordic Infocenter](https://infocenter.nordicsemi.com/topic/ug_nrf52840_dk/UG/dk/intro.html) to download the User Guide. You can find all the information that is relevant to this exercise in there.

View file

@ -2,52 +2,138 @@
## Step-by-Step Solution
### Step 1: Read the docs!
Go to [Nordic Infocenter](https://infocenter.nordicsemi.com/topic/ug_nrf52840_dk/UG/dk/intro.html) to download the User Guide. Read docs, section 8.7 for info about pins and pin configuration related to the buttons. Note down the pins that the buttons are connected to.
✅ Go to [Nordic Infocenter](https://infocenter.nordicsemi.com/topic/ug_nrf52840_dk/UG/dk/intro.html) to download the **User Guide**.
✅ Read docs, section 8.7 for info about pins and pin configuration related to the buttons. Note down the pins that the buttons are connected to.
The pins need to be configured as input pins with an internal pull-up. The pins as well as the configurations are defined as types in the `nrf-hal` in the `gpio` peripheral. Add the following imports: `Input` and `PullUp`.
### Step 2: Add the structs that represent the buttons as a group and a generic single button.
Add the struct that represents the single button. It has only one field, `inner`. The type of this button is the pin configuration: `Pin<Input<PullUp>>`
Add the struct that represents the single button. It has only one field, `inner`. The type of this button is the pin configuration: `Pin<Input<PullUp>>`
Add the `struct` that represents the group of buttons has four fields, one for each button. The field name contains the number that corresponds to the button numeration on the board. The of type of each field is the struct that represents the generic single button.
Add the `struct` that represents the group of buttons has four fields, one for each button. The field name contains the number that corresponds to the button numeration on the board. The of type of each field is the struct that represents the generic single button.
Add doc comments for every struct and field!
Add doc comments for every struct and field!
Building this code should return a warning: field `inner` is never read.
<!-- Solution Code Snippet -->
<details>
<summary>Solution</summary>
```rust
/// All buttons on the board
pub struct Buttons {
/// BUTTON1: pin P0.11, green LED
pub b_1: Button,
/// BUTTON2: pin P0.12, green LED
pub b_2: Button,
/// BUTTON3: pin P0.24, green LED
pub b_3: Button,
/// BUTTON4: pin P0.25, green LED
pub b_4: Button,
}
/// A single button
pub struct Button {
inner: Pin<Input<PullUp>>,
}
```
</details>
### Step 3: Implement the button function.
Add an `impl` block for the `struct Button`. Add a method `is_pushed` that takes in the struct as `&self` and returns a bool, if the button is pushed.
Add an `impl` block for the `struct Button`. Add a method `is_pushed` that takes in the struct as `&self` and returns a bool, if the button is pushed.
Now remember, the pins the buttons are connected to are configured as active low. For buttons this means, that the pin is pulled low, when the button is pushed.
Now remember, the pins the buttons are connected to are configured as active low. For buttons this means, that the pin is pulled low, when the button is pushed.
In the `nrf-hal` you can find a method to check if a single pin is low. To use it, you have to add the following line to your `nrf52840_hal` imports: `prelude::InputPin`.
In the `nrf-hal` you can find a method to check if a single pin is low. To use it, you have to add the following line to your `nrf52840_hal` imports: `prelude::InputPin`.
<!-- Solution Code Snippet -->
<details>
<summary>Solution</summary>
```rust
impl Button {
/// returns true if button is pushed
pub fn is_pushed(&self) -> bool {
self.inner.is_low() == Ok(true)
}
}
```
</details>
### Step 4: Bring up the pins!
Go to `pub fn init()`, the function that initializes the board's peripherals. Get your notes for the pin numbers that are reserver for the buttons. Configure each pin as degraded, pull-up input pin and bind it to a variable that makes it clear what button number it is connected to.
✅ Go to `pub fn init()`, the function that initializes the board's peripherals.
✅ Configure each pin as degraded, pull-up input pin and bind it to a variable that makes it clear what button number it is connected to.
Building this code brings up warnings about unused variables.
<!-- Solution Code Snippet -->
<details>
<summary>Solution</summary>
```rust
// Buttons
let b_1 = pins.p0_11.degrade().into_pullup_input();
let b_2 = pins.p0_12.degrade().into_pullup_input();
let b_3 = pins.p0_24.degrade().into_pullup_input();
let b_4 = pins.p0_25.degrade().into_pullup_input();
```
</details>
### Step 5: Add everything to the board struct.
In the definition of the `struct Board` add a field for the `struct Buttons`.
In the pub `fn init()` function, where `Board` is instantiated, add the button field, assigning the pins you defined earlier to the respective buttons.
✅ In the definition of the `struct Board` add a field for the `struct Buttons`.
<!-- Solution Code Snippet -->
✅ In the pub `fn init()` function, where `Board` is instantiated, add the button field, assigning the pins you defined earlier to the respective buttons.
<details>
<summary>Solution</summary>
```rust
/// Components on the board
pub struct Board {
/// LEDs
pub leds: Leds,
/// Buttons
pub buttons: Buttons,
/// Timer
pub timer: Timer,
}
// ...
pub fn init() -> Result<Board, ()> {
// ...
Ok(Board {
leds: Leds {
// ...
},
buttons: Buttons {
b_1: Button { inner: b_1},
b_2: Button { inner: b_2},
b_3: Button { inner: b_3},
b_4: Button { inner: b_4},
},
timer: Timer { inner: timer },
})
} else {
Err(())
}
}
```
</details>
### Step 6: Run the example!
Go to `/down-the-stack/apps`
Go to `/down-the-stack/apps`.
Run the following command:
Run the following command:
```shell
cargo run --bin button
@ -55,7 +141,7 @@ cargo run --bin button
### Step 7: Generate the docs!
Out of the apps folder run the following command to build the docs for this crate and to view your written documentation!
Out of the apps folder run the following command to build the docs for this crate and to view your written documentation!
```shell
cargo doc

View file

@ -9,7 +9,7 @@ The UART protocol requires four pins, they are usually labelled:
* 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.
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.
@ -23,68 +23,148 @@ 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:
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` that serves as a wrapper for the `UARTE1` instance.
### 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 Code Snippet -->
<details>
<summary>Solution</summary>
```rust
pub struct Uarte {
inner: hal::Uarte<hal::pac::UARTE1>,
}
```
</details>
### 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.
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`.
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:
✅ 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`.
<!-- Solution Code Snippet -->
<details>
<summary>Solution</summary>
```rust
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);
```
</details>
### Step 5: Board struct
Add a field for the `Uarte` struct in the `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 Code Snippet -->
<details>
<summary>Solution</summary>
```rust
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(())
}
```
</details>
### 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.
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 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.
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:
✅ To copy all data into an on-stack buffer, iterate over every chunk of the string to copy it into the buffer:
<details>
<summary>Solution</summary>
```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)?;
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(())
}
}
```
return `Ok(())`
</details>
### Step 7: Connect your computer to the virtual UART
[directions for mac present, linux and windows are missing.]
[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.
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.
Run the following command to run `screen` with the nRF52840-DK with 115200 baud.
```
screen <address of mc> 115200
@ -92,9 +172,8 @@ screen <address of mc> 115200
### Step 7: Run the example.
In another terminal window go into the folder `down-the-stack/apps`.
In another terminal window go into the folder `down-the-stack/apps` and use the following command.
Use the following command.
```
cargo run --bin uarte_print
```