From 3fd05d1f0fb753f440f68ab105d574e21caf641e Mon Sep 17 00:00:00 2001 From: Mirabellensaft Date: Tue, 14 Mar 2023 19:39:50 +0100 Subject: [PATCH] last formatting and adding code snippets --- embedded-workshop-book/src/bsc-exercise.md | 56 ++++---- .../src/button-implementation.md | 122 ++++++++++++++--- .../src/uarte-implementation.md | 129 ++++++++++++++---- 3 files changed, 233 insertions(+), 74 deletions(-) diff --git a/embedded-workshop-book/src/bsc-exercise.md b/embedded-workshop-book/src/bsc-exercise.md index 6441ff1..7182165 100644 --- a/embedded-workshop-book/src/bsc-exercise.md +++ b/embedded-workshop-book/src/bsc-exercise.md @@ -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. diff --git a/embedded-workshop-book/src/button-implementation.md b/embedded-workshop-book/src/button-implementation.md index 9fddb63..13810b7 100644 --- a/embedded-workshop-book/src/button-implementation.md +++ b/embedded-workshop-book/src/button-implementation.md @@ -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>` +✅ Add the struct that represents the single button. It has only one field, `inner`. The type of this button is the pin configuration: `Pin>` -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 + +```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>, +} +``` +
### 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 + +```rust +impl Button { + /// returns true if button is pushed + pub fn is_pushed(&self) -> bool { + self.inner.is_low() == Ok(true) + } +} +``` +
### 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 + +```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(); +``` +
### 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`. - +✅ In the pub `fn init()` function, where `Board` is instantiated, add the button field, assigning the pins you defined earlier to the respective buttons. + +
+ Solution + +```rust +/// Components on the board +pub struct Board { + /// LEDs + pub leds: Leds, + /// Buttons + pub buttons: Buttons, + /// Timer + pub timer: Timer, +} + +// ... + +pub fn init() -> Result { + // ... + 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(()) + } + +} + +``` +
### 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 diff --git a/embedded-workshop-book/src/uarte-implementation.md b/embedded-workshop-book/src/uarte-implementation.md index 8874d5c..80a8b87 100644 --- a/embedded-workshop-book/src/uarte-implementation.md +++ b/embedded-workshop-book/src/uarte-implementation.md @@ -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`. - + +
+ Solution + +```rust +pub struct Uarte { + inner: hal::Uarte, +} +``` +
+ ### 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 + +```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); +``` +
+ ### 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 + +```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 { + + // ... + + 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. +✅ 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: + +
+ Solution ```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(())` +
### 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
115200 @@ -92,9 +172,8 @@ screen
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 ```