Rust challenge 79/100 - embedded workshop @ rustfest
Table of content
What is this
The rules of the game are explained in my original.
79th Challenge
Challenge
Prepare for the rust workshop hosted by rustfest.ch and summarize what was learnt.
Solution
To get things flashed and get debugging with probe.rs. I am going to try different things starting with the probe.
But first I install all the required dependencies.
An we are ready to go!
Hardware Setup
Listing available probes
I have various probes I am going to test with. An ST-Link v2 clone, an st-link nucleo, a segger basic and a rusty-probe
Listing the three probes is not an issue on Mac OS 14.5
. I plug the probes, I run probe-rs
(see documentation at https://probe.rs/) list and I see them.
RP2040 compatible debuggers
Next I want to run some code on the chips so I clone the repository and I do cargo run
.
Somehow I have issues. It turns out, that st-link is vendor locked and can only be used to debug and flash st products. I found this out on the porbe-rs matrix channel. Rusty-probe initially does not work but soon it works due to SWDIO
and SWCLK
being swapped. Further the segger basic also works, however, I had to connect like I showed in one of my first posts. For the rusty-probe I just need GND
, SWIO
and SWCLK
and I can debug.
Debugging with VS Code
To debug within VS Code
I installed the VS Code extension and created two debug settings (see repos, and also here):
With these I was able to debug. Debugging the host code was the same as debugging any other existing program. I found the com port created by Pi used in the host application by listing the devices available and then diffing when plugging the RP2040 in and out.
Software Setup
There are two repos that are mentioned in the workshop. One is https://github.com/mike-kfed/embedded_drivers and the other is the already mentioned https://github.com/bedroombuilds/pico-usb
Understanding the dependencies
The dependencies and a URL to the crates documentation page is listed bellow.
RP2040 code dependencies
Crate | Link | Description |
---|---|---|
cortex-m |
cortex-m | Low-level access to Cortex-M processors, providing functionality like registers and interrupts. |
cortex-m-rt |
cortex-m-rt | Startup and runtime features for Cortex-M microcontrollers. |
embedded-hal |
embedded-hal | Hardware abstraction layer for embedded systems, defining traits for hardware interfacing. |
defmt |
defmt | A highly efficient logging framework for embedded systems, designed for resource-constrained devices. |
defmt-rtt |
defmt-rtt | Implements the RTT (Real-Time Transfer) transport layer for the defmt logging framework. |
panic-probe |
panic-probe | A panic handler for embedded systems that logs panic messages using probe-based debugging. |
rp-pico |
rp-pico | Board support package for the Raspberry Pi Pico board, providing low-level access to its features. |
usb-device |
usb-device | A platform-agnostic USB device library, allowing the creation of USB devices. |
usbd-serial |
usbd-serial | USB serial port implementation for the usb-device library. |
rp2040-hal |
rp2040-hal | Hardware abstraction layer for the RP2040 microcontroller, used in Raspberry Pi Pico and similar boards. |
rp2040-boot2 |
rp2040-boot2 | Low-level boot configuration for RP2040 microcontrollers. |
sparkfun-pro-micro-rp2040 |
sparkfun-pro-micro-rp2040 | Board support package for the SparkFun Pro Micro RP2040 board. |
Dependencies that were mentioned in the talk related to RTOS:
Crate | Link | Description |
---|---|---|
embassy |
embassy | An async, more rigid alternative to traditional Rust async frameworks. |
RTIC v2 |
rtic | Version 2 of the Real-Time Interrupt-driven Concurrency framework, preferred for its async support. |
Bern RTOS |
No Link Available | A Swiss-made real-time operating system noted for its cool and efficient performance. |
Tock |
tock | A highly sandboxed OS; complex and large, ideal for managing intricate systems like train controls. |
Dependencies that were mentioned related to debugging / tracing:
Crate | Link | Description |
---|---|---|
defmt |
defmt | A highly efficient logging framework for embedded systems. |
defmt-rtt |
defmt-rtt | Implements the RTT (Real-Time Transfer) transport layer for the defmt logging framework. |
ufmt |
ufmt | A minimal, #![no_std] compatible formatting library, designed for resource-constrained environments. |
modular-bitfield |
modular-bitfield | Provides macros to define bitfields in a modular way for improved code safety and readability. |
heapless |
heapless | Enables the use of static memory allocation for data structures like Vec and HashMap, with ufmt implementation support. |
And others
Crate | Link | Description |
---|---|---|
kebab-case |
kebab-case | A utility crate for converting strings to kebab-case, commonly used in URLs and IDs. |
nom |
nom | A parser combinator library that allows for easy string parsing and protocol analysis. |
Host code dependencies
Crate | Link | Description |
---|---|---|
serialport |
serialport | Provides an interface for working with serial ports in Rust. |
anyhow |
anyhow | Provides flexible error handling with ‘anyhow::Error’, a trait object based error system. |
defmt |
defmt | A highly efficient logging framework for embedded systems. |
heapless |
heapless | Enables the use of static memory allocation for data structures like Vec and HashMap. |
postcard |
postcard | A no_std, zero-allocation serialization library based on Serde. |
serde |
serde | A serialization/deserialization framework that is both powerful and generic. |
Developing for embedded targets in rust
General
Here are some important things to know about rust embedded development.
- Rust 1.0 came out in 2015, embedded_hal 1.0 only recently.
- embedded development is done with
no_std
, meaning things like Smart pointers and Display traits are not included i.e. “batteries not included”. The box type is also not included but may be added with ``core::alloc` -
PAC/HAL/BSP
: often contain unsafe code-
BSP
: implements hardware layer -
HAL
: “only” traits, this way it works on all uP, mostly works, has improved a lot. Interrupts are still hard to abstract, injecting an interrupt into code is hard -
PAC
: is specific to a micro controller and generated directly from the micro controller’s description (usually in SVD or similar format). A PAC provides low-level register access that is faithful to the micro controller’s specification, allowing developers direct interaction with the hardware through Rust’s type system, which ensures memory safety and concurrency.
-
-
Targets have varying level of support. Best supported are ARM / RISC. There is a platform support page that puts support in different categories. [Tier-1 are the best.]https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-1-with-host-tools)
- read embedded-hal docs
- section for driver authors
- search crates.io using “embedded-hal”
- search for projects on github with “embedded-hal” referenced
Setup Clippy / Lint to be more strict can help
`.clippy.toml for settings of clippy following settings can be helpful
some additional helpful lints are:
Typestate pattern
With this, method / pattern can be bound to states. It is checked at compile time that state allows function / transition. The example that was given was the following:
With this method, super mario cannot die. It is clear at compile time.
Note: Methods should also be testable with another method
Traits
trait bounds (refresher):
(a) with “where” for a lot of trait bounds
(b) with “dyn” for few e.g. “delay: &mut dyn DelaysMs
see also rust by example
- Rust has no inheritance, only traits
- important for embedded Hal, if we want to implement an i2c, we implement a trait for it
- default traits can be overridden
- critical section used for adding for example stuff that the HAL does not support like two CS for SPI
challenge: write drivers that are platform-agnostic.
Usually trait bounds are defined for the implementation not for the struct, even though it is possible.
Associated type for traits
some embedded files in a crate that are not normally found in a rust workspace
- build.rs <- script to read the linker file
- memory.x <- linker file
- .cargo, this is where the
probe-rs run --chip
is hidden! makes thing magically run with cargo run