- remove outdated notes

- VS: you can open more than one folder in the same VS workspace
- current folder in VS code needs to be set to beginner/apps for "Run" to appear
- rename `lsusb` to `usb-list`
- recommend `usb-list` over `lsusb`
- make `serial-term` behave like `screen`
This commit is contained in:
Jorge Aparicio 2020-06-12 11:45:57 +02:00
parent 3983ee5310
commit 75f41a47c4
14 changed files with 404 additions and 465 deletions

View file

@ -13,8 +13,6 @@ Material for the beginner and advanced workshops of Oxidize Global (15.07.2020).
## Checking the hardware
> NOTE To be done *before* the workshop
### nRF52840 Dongle
Connect the Dongle to your PC/laptop. Its red LED should start oscillating in intensity. The device will also show up as:
@ -115,15 +113,13 @@ $ ls /dev/tty.usbmodem*
The board has several switches to configure its behavior. The out of the box configuration is the one we want. If the above instructions didn't work for you, check the position of the on-board switches:
NOTE: Directions assume you are holding the board "horizontally" with components (switches and button) facing up. In this horizontal position you'll find one USB connector (J2) on the left edge, another USB connector (J3) on the bottom edge and 4 buttons on the bottom right corner.
These directions assume you are holding the board "horizontally" with components (switches and button) facing up. In this horizontal position you'll find one USB connector (J2) on the left edge, another USB connector (J3) on the bottom edge and 4 buttons on the bottom right corner.
- Switch SW8, on the bottom edge left corner, is set to the ON position; this is the left position of the two possible positions
- Switch SW9, to the right the left edge USB connector (J2), is set to the VDD position; this is the center position of the three possible positions
## Installation instructions
> NOTE To be done *before* the workshop
### Base Rust installation
Go to https://rustup.rs and follow the instructions.
@ -275,8 +271,6 @@ In Zadig's graphical user interface,
### `nrf-recover`
> NOTE(japaric) this may not be necessary in the future -- the probe-rs library may do this on its own
Some nRF52840 devices, specially older revisions, may have parts of their Flash memory locked. To unlock the memory use the `nrf-recover` tool.
This is only relevant to the nRF52840 Development Kit. First connect the nRF52840 DK to your PC using micro-USB J2 (as done before) then run the following commands:
@ -298,46 +292,3 @@ $ cargo install cargo-flash cargo-embed cargo-binutils
(..)
Summary Successfully installed cargo-flash, cargo-embed, cargo-binutils!
```
### "power debug error"
> NOTE(japaric) we need to investigate what this error is about -- it seems to occur only once?
Install `probe-rs-cli`:
``` console
$ cargo install probe-rs-cli
```
Connect the nRF52840 DK to your PC / laptop; use USB connector J2 on the DK. Then run this command:
``` console
$ probe-rs-cli info
```
In case you get an error, run the above program above a second time. You should now be seeing output similar to:
```
$ probe-rs-cli info
Available Access Ports:
IDR {
REVISION: 0x2,
DESIGNER: 0x23b,
CLASS: MEMAP,
_RES0: 0x0,
VARIANT: 0x1,
TYPE: AMBA_AHB3,
}
Class1RomTable(
CSComponentId {
base_address: 0xe00ff000,
class: RomTable,
peripheral_id: PeripheralID {
REVAND: 0x000000,
CMOD: No,
REVISION: 0x000003,
JEP106: Some(
JEP106Code({ cc: 0x02, id: 0x44 } => Some("Nordic VLSI ASA")),
),
(...)
```

View file

@ -39,7 +39,7 @@ In addition to these two workspaces there's a third folder called "common". This
## Listing USB devices
Open the `advanced/host/lsusb` folder in VS Code and run the program.
In the `tools` folder you'll find `usb-list`: a minimal cross-platform version of the `lsusb` tool. Run it (`cargo run` from `tools/usb-list`) to list all USB devices.
``` console
$ cargo run
@ -49,7 +49,7 @@ Bus 001 Device 003: ID 0c45:6713
Bus 001 Device 001: ID 1d6b:0002
```
The goal is to get the nRF52840 SoC to show in this list. The embedded application will use the vendor ID (VID) and product ID (PID) defined in `advanced/common/consts`; the `lsusb` tool will also look for a USB device with that VID/PID and highlight it in the output.
The goal is to get the nRF52840 SoC to show in this list. The embedded application will use the vendor ID (VID) and product ID (PID) defined in `advanced/common/consts`; the `usb-list` tool will highlight the USB device that matches that VID PID pair.
``` console
$ # expected output
@ -58,16 +58,22 @@ Bus 002 Device 001: ID 1d6b:0003
Bus 001 Device 002: ID 0cf3:e300
Bus 001 Device 003: ID 0c45:6713
Bus 001 Device 001: ID 1d6b:0002
Bus 001 Device 059: ID 0000:0000 <- nRF52840 on the nRF52840 Development Kit
Bus 001 Device 059: ID 2020:0705 <- nRF52840 on the nRF52840 Development Kit
```
## Hello, world!
0. Open the `tools/dk-run` folder and run `cargo install --path . -f` to install the `dk-run` tool.
First, open the `tools/dk-run` folder and run `cargo install --path . -f` to install the `dk-run` tool.
1. Open the `advanced/firmware` folder in VS Code then open the `src/bin/hello.rs` file.
Next open the `advanced/firmware` folder in VS Code. If have already opened the root of the repository (`embedded-trainings-2020`) then please also open the `advanced/firmware` folder: right click the left side panel, select "Add folder to workspace" and add the `advanced/apps` folder.
2. In VS code, click the "Run" button that's displayed over the `main` function. If you are not using VS code run the `cargo run --bin hello` command from the `advanced/firmware` folder.
Now, on the left side panel, open the `src/bin/hello.rs` file from under the `advanced/apps` folder.
Give Rust Analyzer some time to analyze the file and its dependency graph. When it's done, a "Run" button will appear over the `main` function -- you may need to edit the file contents to make the "Run" button appear.
Click the "Run" button to run the application on the microcontroller.
If you are not using VS code run the `cargo run --bin hello` command from the `advanced/firmware` folder.
> NOTE if you run into an error along the lines of "Debug power request failed" retry the operation and the error should disappear
@ -81,7 +87,7 @@ Note that when the `dk-run` tool sees the device enter the halted state it will
RTIC, Real Time on Integrated Circuits, is a framework for building evented, time sensitive applications.
Open the `advanced/apps` folder in VS Code then open the `src/bin/rtic-hello.rs` file.
Open the `src/bin/rtic-hello.rs` file.
RTIC applications are written in RTIC's Domain Specific Language (DSL). The DSL extends Rust syntax with custom attributes like `#[init]` and `#[idle]`.
@ -102,7 +108,7 @@ fn main() -> ! {
## Dealing with registers
Open the `advanced/firmware` folder in VS Code; then open the `src/bin/rtic-events.rs` file.
Open the `src/bin/rtic-events.rs` file.
In this and the next section we'll look into the RTIC's event handling features. To explore these features we'll use the action of connecting a USB cable to the DK's port J2 as the event we'd like to handle.
@ -124,11 +130,9 @@ Below the `idle` function you'll see a `#[task]` handler (function). This *task*
Note that all tasks will be prioritized over the `idle` function so the execution of `idle` will be interrupted (paused) by the `on_power_event` task. When the `on_power_event` task finishes (returns) the execution of the `idle` will be resumed. This will become more obvious in the next section.
## Task state
Open the `advanced/firmware` folder in VS Code; then open the `src/bin/rtic-resources.rs` file.
Open the `src/bin/rtic-resources.rs` file.
> TODO
@ -174,7 +178,7 @@ Although the control pipe should be bidirectional, in practice to complete the e
## Dealing with USB events
Open the `advanced/firmware` folder in VS Code; then open the `src/bin/rtic-usb-1.rs` file.
Open the `src/bin/rtic-usb-1.rs` file.
The USB peripheral contains a series of registers, called EVENTS registers, that indicate the reason for entering the USBD event handler. These events must be handled by the application to complete the enumeration process.
@ -194,7 +198,7 @@ The SETUP stage consists of the host sending a 8 byte header to the device. This
The nRF52840 USBD peripheral will parse this header and store its contents in the following registers: BMREQUESTTYPE, BREQUEST, WVALUE{L,H}, WINDEX{L,H} and WLENGTH{L,H}. These registers match the logical division of the setup packet, in *fields*, as specified in the USB 2.0 specification. The fields that start with the letter 'b' are 1-byte large; the ones that start with the letter 'w' are 2-bytes large.
Open the `advanced/firmware` folder in VS Code; then open the `src/bin/rtic-usb-2.rs` file.
Open the `src/bin/rtic-usb-2.rs` file.
The task here is to write a SETUP packet parser in the `common/usb` crate and reach the GOAL comment when executing the program. The starter code has already read the relevant registers using helper functions.
@ -206,7 +210,7 @@ You should develop this part completely on the host. Switch the workspace to the
After receiving a GET_DESCRIPTOR request during the SETUP stage the device needs to respond with a descriptor during the DATA stage.
Open the `advanced/firmware` folder in VS Code; then open the `src/bin/rtic-usb-3.rs` file.
Open the `src/bin/rtic-usb-3.rs` file.
This starter code parses the GET_DESCRIPTOR request and sends back a zero-byte response to the host using the `Ep0In` abstraction, which we'll cover in the next section. The starter code is wrong because a zero-byte response is not a valid descriptor.
@ -245,7 +249,7 @@ Not relevant to the DMA operation but relevant to the USB specification, the `st
The USB specification defines a device-side procedure for "stalling a endpoint", which amounts to the device telling the host that it doesn't support some request. This procedure should be used to deal with invalid requests, requests whose SETUP stage doesn't match any USB 2.0 standard request, and requests not supported by the device, for instance the SET_DESCRIPTOR request is not mandatory.
Open the `advanced/firmware` folder in VS Code; then open the `src/bin/rtic-usb-4.rs` file.
Open the `src/bin/rtic-usb-4.rs` file.
In this starter code the code that handles the SETUP stage of endpoint 0 has been refactored into a separate function, `ep0setup`, that uses Rust's built-in error handling features. This function will return the `Err` variant when it encounters an invalid request or a request that is not supported.

View file

@ -1,4 +1,4 @@
#![no_std]
pub const VID: u16 = 0x0000;
pub const PID: u16 = 0x0000;
pub const VID: u16 = 0x2020;
pub const PID: u16 = 0x0715;

View file

@ -20,20 +20,22 @@ Some details about the nRF52840 microcontroller that are relevant to this worksh
## Parts of an embedded program
1. Open the `beginner/apps` folder in VS Code.
Open the `beginner/apps` folder in VS Code.
``` console
$ # or use "File > Open Folder" in VS Code
$ code beginner/apps
```
2. Open the `src/bin/hello.rs` file.
Then open the `src/bin/hello.rs` file.
If you find it more convenient to open the repository at the root then please also add the `beginner/apps` folder to the VS Code workspace: right click the left side panel, select "Add folder to workspace" and add the `beginner/apps` folder.
Note the differences between this embedded program and a desktop program:
The `#![no_std]` attribute indicates that the program will not make use of the standard library, `std` crate. Instead it will use the `core` library, a subset of the standard library that does not on a underlying operating system (OS).
The `#![no_main]` attribute indicates that the program will use a custom entry point instead of the default one: `fn main() { .. }`.
The `#![no_main]` attribute indicates that the program will use a custom entry point instead of the default `fn main() { .. }` one.
The `#[entry]` attribute declares the custom entry point of the program. The entry point must be a divergent function; note that the return type is the never type `!`. The function is not allowed to return; therefore the program is not allowed to terminate.
@ -104,6 +106,8 @@ The following command will flash the ELF file to the device.
$ cargo flash --chip nRF52840_xxAA --elf target/thumbv7em-none-eabi/debug/hello
```
> NOTE if you run into an error along the lines of "Debug power request failed" retry the operation and the error should disappear
Alternatively you can run this command, which builds the application before flashing it.
``` console
@ -138,13 +142,15 @@ Logging is implemented using the Real Time Transfer (RTT) protocol. Under this p
Both `cargo-embed` and `cargo-flash` are tools based on the `probe-rs` library. This library exposes an API to communicate with the J-Link and perform all the operations exposed by the JTAG protocol. For this workshop we have developed a small Cargo runner that uses the `probe-rs` library to streamline the process of running a program and printing logs, like `cargo-embed`, while also having better integration into VS code.
1. Browse to the `tools/dk-run` folder and run this command from there:
1. Run this command from the `tools/dk-run` folder:
``` console
$ cargo install --path . -f
```
2. Open the `beginner/apps` folder in VS Code; then open the `src/bin/hello.rs` file and click the "Run" button that's hovering over the `main` function.
2. Open the `src/bin/hello.rs` file and click the "Run" button that's hovering over the `main` function.
Note: you will get the "Run" button if the Rust analyzer's workspace is set to the `beginner/apps` folder. This will be the case if the current folder in VS code (left side panel) is set to `beginner/apps`.
If you are not using VS code, run the command `cargo run --bin hello` from within the `beginer/apps` folder. Rust Analyzer's "Run" button is a short-cut for that command.
@ -164,7 +170,7 @@ Unlike `cargo-embed`, `dk-run` will terminate when the program reaches a breakpo
## Panicking
Open the `beginner/apps` folder in VS Code then open the `src/bin/panic.rs` file and click the "Run" button.
Open the `src/bin/panic.rs` file and click the "Run" button.
This program attempts to index an array beyond its length and this results in a panic.
@ -200,7 +206,7 @@ Now run the program again. Try changing the format string of the `error!` macro.
In this section we'll start using the hardware features of the nRF52840 and the board.
Open the `beginner/apps` folder in VS Code then open the `src/bin/led.rs` file.
Open the `src/bin/led.rs` file.
The `dk` crate / library is a Hardware Abstraction Layer (HAL) over the nRF52840 Development Kit. The purpose of a HAL is to abstract away the device-specific details of the hardware, for example registers, and instead expose a higher level API more suitable for application development.
@ -212,6 +218,8 @@ $ cargo doc -p dk --open
Check the API docs of the `Led` abstraction then run the `led` program. Two of the green LEDs on the board should turn on; the other two should stay off.
> NOTE this program will not terminate itself. Within VS code you need to click "Kill terminal" (garbage bin icon) in the bottom panel to terminate it.
Now, uncomment the `log::set_max_level` line. This will make the logs more verbose; they will now include logs from the board initialization function (`dk::init`) and from the `Led` API.
Among the logs you'll find the line "I/O pins have been configured for digital output". At this point the electrical pins of the nRF52840 microcontroller has been configured to drive the 4 LEDs on the board.
@ -224,7 +232,7 @@ After the `dk::init` logs you'll find logs about the `Led` API. As the logs indi
Next we'll look into the time related APIs exposed by the `dk` HAL.
Open the `beginner/apps` folder in VS Code then open the `src/bin/blinky.rs` file.
Open the `src/bin/blinky.rs` file.
This program will blink (turn on and off) one of the LEDs on the board. The time interval between each toggle operation is one second. This wait time between consecutive operations is generated by the blocking `timer.wait` operation. This function call will block the program execution for the specified [`Duration`] argument.
@ -250,15 +258,9 @@ When put in bootloader mode the Dongle will run a bootloader program instead of
To put the Dongle in bootloader mode connect it to your laptop / PC / mac and then press its *reset* button. The Dongle has two buttons: a round-ish user button (SW1) and a square-ish reset button (RESET); the latter is mounted "sideways". The buttons are next to each other. The RESET button is mounted closer to the edge of the board that has the Nordic logo on silkscreen and the actual button is facing towards that edge. The opposite edge of the board is narrower and has the surface USB connector; this is the end that goes into your PC USB port.
When the Dongle is in bootloader mode its red LED will oscillate in intensity. The Dongle will also appear as a USB CDC ACM device with vendor ID `0x1915` and product ID `0x521f`. If you have the `lsusb` tool installed (Linux distributions have it) then you can run it to check the presence of the USB device:
When the Dongle is in bootloader mode its red LED will oscillate in intensity. The Dongle will also appear as a USB CDC ACM device with vendor ID `0x1915` and product ID `0x521f`.
``` console
$ lsusb
(..)
Bus 001 Device 016: ID 1915:521f Nordic Semiconductor ASA 4-Port USB 2.0 Hub
```
If you don't have the `lsusb` tool installed, we have provided a cross-platform version of it in the `advanced/host/lsusb` folder. Call `cargo run` from that directory to run the tool.
In the `tools` folder you'll find `usb-list`: a minimal cross-platform version of the `lsusb` tool. Run it (`cargo run` from `tools/usb-list`) to list all USB devices; the Dongle will be highlighted in the output.
``` console
$ cargo run
@ -280,7 +282,7 @@ Device programmed.
After the device has been programmed it will automatically reset and start running the new application.
The `loopback` application will *blink* the red LED in a heartbeat fashion: two fast blinks (LED on then off) followed by two periods of silence (LED off). The application will also make the Dongle enumerate itself as a CDC ACM device. If you run our `lsusb` tool (from the `advanced/host/lsusb` directory) you should see the newly enumerated Dongle in the output:
The `loopback` application will *blink* the red LED in a heartbeat fashion: two fast blinks (LED on then off) followed by two periods of silence (LED off). The application will also make the Dongle enumerate itself as a CDC ACM device. If you run `usb-list` tool (from the `tools/usb-list` directory) you should see the newly enumerated Dongle in the output:
``` console
$ cargo run
@ -301,15 +303,13 @@ deviceid=588c06af0877c8f2 channel=20 TxPower=+8dBm
(..)
```
> NOTE The application may take a while to print to the console
This line is printed by the `loopback` app on boot. It contains the device ID of the dongle, a 64-bit unique identifier (so everyone will see a different number); the radio channel that the device will use to communicate; and the transmission power of the radio in dBm.
Leave the Dongle connected and the `serial-term` application running. Now we'll switch back to the Development Kit.
## Radio out
Open the `beginner/apps` folder in VS Code; then open the `src/bin/radio-send.rs` file.
Open the `src/bin/radio-send.rs` file.
In this section you'll send radio packets from the DK to the Dongle and get familiar with the different settings of the radio API.
@ -343,7 +343,7 @@ In this section we'll explore the `recv` method of the Radio API. As the name im
The `loopback` application running on the Dongle will broadcast a radio packet after receiving one over channel 20. The contents of this outgoing packet will be the contents of the received one but reversed.
Open the `beginner/apps` folder in VS Code; then open the `src/bin/radio-recv.rs` file and click the "Run" button.
Open the `src/bin/radio-recv.rs` file and click the "Run" button.
The Dongle will response as soon as it receives a packet. If you insert a delay between the `send` operation and the `recv` operation in the `radio-recv` program this will result in the DK not seeing the Dongle's response.

View file

@ -18,11 +18,8 @@ fn main() -> ! {
let mut packet = Packet::new();
packet.copy_from_slice(b"Hello");
//radio.send(&packet);
let res = radio.try_send(&packet);
log::info!("{:?}", res);
loop {
asm::bkpt();
}
dk::exit();
}

View file

@ -1,9 +0,0 @@
[package]
name = "consts"
version = "0.1.0"
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -1,5 +0,0 @@
#![no_std]
// Workshop date
pub const VID: u16 = 0x2020;
pub const PID: u16 = 0x0715;

684
tools/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -3,4 +3,5 @@ members = [
"dk-run",
"dongle-flash",
"serial-term",
"usb-list",
]

View file

@ -6,6 +6,6 @@ version = "0.1.0"
[dependencies]
anyhow = "1.0.30"
consts = { path = "../../common/consts" }
consts = { path = "../../advanced/common/consts" }
ctrlc = "3.1.4"
serialport = "3.3.0"

View file

@ -2,13 +2,12 @@ use core::sync::atomic::{AtomicBool, Ordering};
use std::{
fs::OpenOptions,
io::{self, Read as _, Write as _},
thread,
time::Duration,
};
use serialport::SerialPortType;
#[cfg(target_os = "windows")]
compiler_error!("FIXME(Windows)");
fn main() -> Result<(), anyhow::Error> {
let mut once = true;
let dongle = loop {
@ -28,12 +27,7 @@ fn main() -> Result<(), anyhow::Error> {
}
};
// FIXME works with ttyUSB devices but not with ttyACM devices (?)
// let mut port = serialport::open(&dongle.port_name)?;
let mut port = OpenOptions::new()
.read(true)
.write(true)
.open(&dongle.port_name)?;
let mut port = serialport::open(&dongle.port_name)?;
static CONTINUE: AtomicBool = AtomicBool::new(true);
@ -44,14 +38,14 @@ fn main() -> Result<(), anyhow::Error> {
let mut stdout = stdout.lock();
let mut read_buf = [0; 64];
while CONTINUE.load(Ordering::Relaxed) {
// if port.bytes_to_read()? != 0 {
let n = port.read(&mut read_buf)?;
stdout.write_all(&read_buf[..n])?;
stdout.flush()?;
// } else {
// time span between consecutive FS USB packets
// thread::sleep(Duration::from_millis(1));
// }
if port.bytes_to_read()? != 0 {
let n = port.read(&mut read_buf)?;
stdout.write_all(&read_buf[..n])?;
stdout.flush()?;
} else {
// time span between two consecutive FS USB packets
thread::sleep(Duration::from_millis(1));
}
}
eprintln!("(closing the serial port)");

View file

@ -1,9 +1,9 @@
[package]
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
edition = "2018"
name = "lsusb"
name = "usb-list"
version = "0.1.0"
[dependencies]
consts = { path = "../../common/consts" }
consts = { path = "../../advanced/common/consts" }
rusb = "0.5.5"