mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 08:12:30 +00:00
Merge pull request #2422 from embassy-rs/cleanup-docs
Cleanup docs and example
This commit is contained in:
commit
da17e67954
4
docs/README.md
Normal file
4
docs/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# embassy docs
|
||||
|
||||
The documentation hosted at [https://embassy.dev/book](https://embassy.dev/book). Building the documentation requires
|
||||
cloning the [embassy-book](https://github.com/embassy-rs/embassy-book) repository and following the instructions.
|
@ -3,11 +3,11 @@
|
||||
** xref:project_structure.adoc[Project Structure]
|
||||
** xref:new_project.adoc[Starting a new Embassy project]
|
||||
** xref:best_practices.adoc[Best Practices]
|
||||
* xref:layer_by_layer.adoc[Bare metal to async]
|
||||
* xref:runtime.adoc[Executor]
|
||||
* xref:delaying_a_task.adoc[Delaying a Task]
|
||||
* xref:sharing_peripherals.adoc[Sharing peripherals between tasks]
|
||||
* xref::time_keeping.adoc[Time-keeping]
|
||||
* xref:sharing_peripherals.adoc[Sharing peripherals]
|
||||
* xref:hal.adoc[HAL]
|
||||
** xref:layer_by_layer.adoc[Anatomy of an async HAL]
|
||||
** xref:nrf.adoc[nRF]
|
||||
** xref:stm32.adoc[STM32]
|
||||
* xref:bootloader.adoc[Bootloader]
|
||||
|
@ -1,28 +0,0 @@
|
||||
= Delaying a Task
|
||||
|
||||
In an embedded program, delaying a task is one of the most common actions taken. In an event loop, delays will need to be inserted to ensure
|
||||
that other tasks have a chance to run before the next iteration of the loop is called, if no other I/O is performed. Embassy provides an abstraction
|
||||
to delay the current task for a specified interval of time.
|
||||
|
||||
Timing is serviced by the `embassy::time::Timer` struct, which provides two timing methods.
|
||||
|
||||
`Timer::at` creates a future that completes at the specified `Instant`, relative to the system boot time.
|
||||
`Timer::after` creates a future that completes after the specified `Duration`, relative to when the future was created.
|
||||
|
||||
An example of a delay is provided as follows:
|
||||
|
||||
[,rust]
|
||||
----
|
||||
use embassy::executor::{task, Executor};
|
||||
use embassy::time::{Duration, Timer};
|
||||
|
||||
#[task]
|
||||
/// Task that ticks periodically
|
||||
async fn tick_periodic() -> ! {
|
||||
loop {
|
||||
rprintln!("tick!");
|
||||
// async sleep primitive, suspends the task for 500ms.
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
}
|
||||
}
|
||||
----
|
@ -18,7 +18,7 @@ my-project
|
||||
|- rust-toolchain.toml
|
||||
----
|
||||
|
||||
=== .cargo/config.toml
|
||||
== .cargo/config.toml
|
||||
|
||||
This directory/file describes what platform you're on, and configures link:https://github.com/probe-rs/probe-rs[probe-rs] to deploy to your device.
|
||||
|
||||
@ -36,17 +36,17 @@ target = "thumbv6m-none-eabi" # <-change for your platform
|
||||
DEFMT_LOG = "trace" # <- can change to info, warn, or error
|
||||
----
|
||||
|
||||
=== build.rs
|
||||
== build.rs
|
||||
|
||||
This is the build script for your project. It links defmt (what is defmt?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example].
|
||||
|
||||
=== Cargo.toml
|
||||
== Cargo.toml
|
||||
|
||||
This is your manifest file, where you can configure all of the embassy components to use the features you need.
|
||||
|
||||
TODO: someone should exhaustively describe every feature for every component!
|
||||
|
||||
=== memory.x
|
||||
== memory.x
|
||||
|
||||
This file outlines the flash/ram usage of your program. It is especially useful when using link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] on an nRF5x.
|
||||
|
||||
@ -63,7 +63,7 @@ MEMORY
|
||||
}
|
||||
----
|
||||
|
||||
=== rust-toolchain.toml
|
||||
== rust-toolchain.toml
|
||||
|
||||
This file configures the rust version and configuration to use.
|
||||
|
||||
|
@ -1,6 +1,12 @@
|
||||
= Sharing peripherals between tasks
|
||||
|
||||
Often times, more than one task needs access to the same resource (pin, communication interface, etc.). The following example shows how to use the on-board LED on a Raspberry Pi Pico board by two tasks simultaneously.
|
||||
Often times, more than one task needs access to the same resource (pin, communication interface, etc.). Embassy provides many different synchronization primitives in the link:https://crates.io/crates/embassy-sync[embassy-sync] crate.
|
||||
|
||||
The following examples shows different ways to use the on-board LED on a Raspberry Pi Pico board by two tasks simultaneously.
|
||||
|
||||
== Sharing using a Mutex
|
||||
|
||||
Using mutual exclusion is the simplest way to share a peripheral.
|
||||
|
||||
[,rust]
|
||||
----
|
||||
@ -29,13 +35,12 @@ async fn main(spawner: Spawner) {
|
||||
let dt = 100 * 1_000_000;
|
||||
let k = 1.003;
|
||||
|
||||
unwrap!(spawner.spawn(toggle(&LED, Duration::from_nanos(dt))));
|
||||
unwrap!(spawner.spawn(toggle_slightly_slower(
|
||||
&LED,
|
||||
Duration::from_nanos((dt as f64 * k) as u64)
|
||||
)));
|
||||
unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));
|
||||
unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
|
||||
}
|
||||
|
||||
// A pool size of 2 means you can spawn two instances of this task.
|
||||
#[embassy_executor::task(pool_size = 2)]
|
||||
async fn toggle_led(led: &'static LedType, delay: Duration) {
|
||||
let mut ticker = Ticker::every(delay);
|
||||
loop {
|
||||
@ -48,31 +53,74 @@ async fn toggle_led(led: &'static LedType, delay: Duration) {
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
#[embassy_executor::task]
|
||||
async fn toggle(led: &'static LedType, delay: Duration) {
|
||||
toggle_led(led, delay).await
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn toggle_slightly_slower(led: &'static LedType, delay: Duration) {
|
||||
toggle_led(led, delay).await
|
||||
}
|
||||
----
|
||||
|
||||
The structure facilitating access to the resource is the defined `LedType`.
|
||||
|
||||
== Why so complicated
|
||||
=== Why so complicated
|
||||
|
||||
Unwrapping the layers gives insight into why each one is needed.
|
||||
|
||||
=== `Mutex<RawMutexType, T>`
|
||||
==== `Mutex<RawMutexType, T>`
|
||||
|
||||
The mutex is there so if one task gets the resource first and begins modifying it, all other tasks wanting to write will have to wait (the `led.lock().await` will return immediately if no task has locked the mutex, and will block if it is accessed somewhere else).
|
||||
|
||||
=== `Option<T>`
|
||||
==== `Option<T>`
|
||||
|
||||
The `LED` variable needs to be defined outside the main task as references accepted by tasks need to be `'static`. However, if it is outside the main task, it cannot be initialised to point to any pin, as the pins themselves are not initialised. Thus, it is set to `None`.
|
||||
|
||||
=== `Output<AnyPin>`
|
||||
==== `Output<AnyPin>`
|
||||
|
||||
To indicate that the pin will be set to an Output. The `AnyPin` could have been `embassy_rp::peripherals::PIN_25`, however this option lets the `toggle_led` function be more generic.
|
||||
|
||||
== Sharing using a Channel
|
||||
|
||||
A channel is another way to ensure exclusive access to a resource. Using a channel is great in the cases where the access can happen at a later point in time, allowing you to enqueue operations and do other things.
|
||||
|
||||
[,rust]
|
||||
----
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio;
|
||||
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||
use embassy_sync::channel::{Channel, Sender};
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use gpio::{AnyPin, Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
enum LedState {
|
||||
Toggle,
|
||||
}
|
||||
static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);
|
||||
|
||||
let dt = 100 * 1_000_000;
|
||||
let k = 1.003;
|
||||
|
||||
unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
|
||||
unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos((dt as f64 * k) as u64))));
|
||||
|
||||
loop {
|
||||
match CHANNEL.receive().await {
|
||||
LedState::Toggle => led.toggle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A pool size of 2 means you can spawn two instances of this task.
|
||||
#[embassy_executor::task(pool_size = 2)]
|
||||
async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {
|
||||
let mut ticker = Ticker::every(delay);
|
||||
loop {
|
||||
control.send(LedState::Toggle).await;
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
This example replaces the Mutex with a Channel, and uses another task (the main loop) to drive the LED. The advantage of this approach is that only a single task references the peripheral, separating concerns. However, using a Mutex has a lower overhead and might be necessary if you need to ensure
|
||||
that the operation is ecompleted before continuing to do other work in your task.
|
||||
|
60
docs/modules/ROOT/pages/time_keeping.adoc
Normal file
60
docs/modules/ROOT/pages/time_keeping.adoc
Normal file
@ -0,0 +1,60 @@
|
||||
= Time-keeping
|
||||
|
||||
In an embedded program, delaying a task is one of the most common actions taken. In an event loop, delays will need to be inserted to ensure
|
||||
that other tasks have a chance to run before the next iteration of the loop is called, if no other I/O is performed. Embassy provides abstractions
|
||||
to delay the current task for a specified interval of time.
|
||||
|
||||
The interface for time-keeping in Embassy is handled by the link:https://crates.io/crates/embassy-time[embassy-time] crate. The types can be used with the internal
|
||||
timer queue in link:https://crates.io/crates/embassy-executor[embassy-executor] or a custom timer queue implementation.
|
||||
|
||||
== Timer
|
||||
|
||||
The `embassy::time::Timer` type provides two timing methods.
|
||||
|
||||
`Timer::at` creates a future that completes at the specified `Instant`, relative to the system boot time.
|
||||
`Timer::after` creates a future that completes after the specified `Duration`, relative to when the future was created.
|
||||
|
||||
An example of a delay is provided as follows:
|
||||
|
||||
[,rust]
|
||||
----
|
||||
use embassy::executor::{task, Executor};
|
||||
use embassy::time::{Duration, Timer};
|
||||
|
||||
#[task]
|
||||
/// Task that ticks periodically
|
||||
async fn tick_periodic() -> ! {
|
||||
loop {
|
||||
rprintln!("tick!");
|
||||
// async sleep primitive, suspends the task for 500ms.
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
== Delay
|
||||
|
||||
The `embassy::time::Delay` type provides an implementation of the link:https://docs.rs/embedded-hal/1.0.0/embedded_hal/delay/index.html[embedded-hal] and
|
||||
link:https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/index.html[embedded-hal-async] traits. This can be used for drivers
|
||||
that expect a generic delay implementation to be provided.
|
||||
|
||||
An example of how this can be used:
|
||||
|
||||
[,rust]
|
||||
----
|
||||
use embassy::executor::{task, Executor};
|
||||
|
||||
#[task]
|
||||
/// Task that ticks periodically
|
||||
async fn tick_periodic() -> ! {
|
||||
loop {
|
||||
rprintln!("tick!");
|
||||
// async sleep primitive, suspends the task for 500ms.
|
||||
generic_delay(embassy::time::Delay).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn generic_delay<D: embedded_hal_async::delay::DelayNs>(delay: D) {
|
||||
delay.delay_ms(500).await;
|
||||
}
|
||||
----
|
@ -1,5 +1,5 @@
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
runner = "probe-rs run --chip RP2040"
|
||||
runner = "probe-rs run --chip RP2040 --probe 1209:4853:0e0039001450563641333620"
|
||||
|
||||
[build]
|
||||
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
||||
|
50
examples/rp/src/bin/blinky_two_channels.rs
Normal file
50
examples/rp/src/bin/blinky_two_channels.rs
Normal file
@ -0,0 +1,50 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
/// This example demonstrates how to access a given pin from more than one embassy task
|
||||
/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
|
||||
/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
|
||||
/// to interference and the 'beats' you can hear if you play two frequencies close to one another
|
||||
/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio;
|
||||
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||
use embassy_sync::channel::{Channel, Sender};
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use gpio::{AnyPin, Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
enum LedState {
|
||||
Toggle,
|
||||
}
|
||||
static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);
|
||||
|
||||
let dt = 100 * 1_000_000;
|
||||
let k = 1.003;
|
||||
|
||||
unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
|
||||
unwrap!(spawner.spawn(toggle_led(
|
||||
CHANNEL.sender(),
|
||||
Duration::from_nanos((dt as f64 * k) as u64)
|
||||
)));
|
||||
|
||||
loop {
|
||||
match CHANNEL.receive().await {
|
||||
LedState::Toggle => led.toggle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task(pool_size = 2)]
|
||||
async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {
|
||||
let mut ticker = Ticker::every(delay);
|
||||
loop {
|
||||
control.send(LedState::Toggle).await;
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
@ -30,13 +30,11 @@ async fn main(spawner: Spawner) {
|
||||
let dt = 100 * 1_000_000;
|
||||
let k = 1.003;
|
||||
|
||||
unwrap!(spawner.spawn(toggle(&LED, Duration::from_nanos(dt))));
|
||||
unwrap!(spawner.spawn(toggle_slightly_slower(
|
||||
&LED,
|
||||
Duration::from_nanos((dt as f64 * k) as u64)
|
||||
)));
|
||||
unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));
|
||||
unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
|
||||
}
|
||||
|
||||
#[embassy_executor::task(pool_size = 2)]
|
||||
async fn toggle_led(led: &'static LedType, delay: Duration) {
|
||||
let mut ticker = Ticker::every(delay);
|
||||
loop {
|
||||
@ -49,12 +47,3 @@ async fn toggle_led(led: &'static LedType, delay: Duration) {
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
#[embassy_executor::task]
|
||||
async fn toggle(led: &'static LedType, delay: Duration) {
|
||||
toggle_led(led, delay).await
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn toggle_slightly_slower(led: &'static LedType, delay: Duration) {
|
||||
toggle_led(led, delay).await
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user