aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2024-01-10 09:48:09 +0100
committerUlf Lilleengen <[email protected]>2024-01-10 09:52:46 +0100
commitff5f5021fb4f469f72b3645760238ebe1c4d99af (patch)
tree9ecdc1b4876a3d566a8135aa88ae3de95a78f650 /docs
parentb8672458947b4d48b5d5c950a60edd677d87e29e (diff)
cleanup docs and add channel synchronization example
Diffstat (limited to 'docs')
-rw-r--r--docs/README.md4
-rw-r--r--docs/modules/ROOT/nav.adoc6
-rw-r--r--docs/modules/ROOT/pages/delaying_a_task.adoc28
-rw-r--r--docs/modules/ROOT/pages/project_structure.adoc10
-rw-r--r--docs/modules/ROOT/pages/sharing_peripherals.adoc86
-rw-r--r--docs/modules/ROOT/pages/time_keeping.adoc60
6 files changed, 139 insertions, 55 deletions
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 000000000..0bf3a6c89
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,4 @@
1# embassy docs
2
3The documentation hosted at [https://embassy.dev/book](https://embassy.dev/book). Building the documentation requires
4cloning the [embassy-book](https://github.com/embassy-rs/embassy-book) repository and following the instructions.
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index b692c44e1..44b0eddb9 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -3,11 +3,11 @@
3** xref:project_structure.adoc[Project Structure] 3** xref:project_structure.adoc[Project Structure]
4** xref:new_project.adoc[Starting a new Embassy project] 4** xref:new_project.adoc[Starting a new Embassy project]
5** xref:best_practices.adoc[Best Practices] 5** xref:best_practices.adoc[Best Practices]
6* xref:layer_by_layer.adoc[Bare metal to async]
7* xref:runtime.adoc[Executor] 6* xref:runtime.adoc[Executor]
8* xref:delaying_a_task.adoc[Delaying a Task] 7* xref::time_keeping.adoc[Time-keeping]
9* xref:sharing_peripherals.adoc[Sharing peripherals between tasks] 8* xref:sharing_peripherals.adoc[Sharing peripherals]
10* xref:hal.adoc[HAL] 9* xref:hal.adoc[HAL]
10** xref:layer_by_layer.adoc[Anatomy of an async HAL]
11** xref:nrf.adoc[nRF] 11** xref:nrf.adoc[nRF]
12** xref:stm32.adoc[STM32] 12** xref:stm32.adoc[STM32]
13* xref:bootloader.adoc[Bootloader] 13* xref:bootloader.adoc[Bootloader]
diff --git a/docs/modules/ROOT/pages/delaying_a_task.adoc b/docs/modules/ROOT/pages/delaying_a_task.adoc
deleted file mode 100644
index 3171e3515..000000000
--- a/docs/modules/ROOT/pages/delaying_a_task.adoc
+++ /dev/null
@@ -1,28 +0,0 @@
1= Delaying a Task
2
3In 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
4that 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
5to delay the current task for a specified interval of time.
6
7Timing is serviced by the `embassy::time::Timer` struct, which provides two timing methods.
8
9`Timer::at` creates a future that completes at the specified `Instant`, relative to the system boot time.
10`Timer::after` creates a future that completes after the specified `Duration`, relative to when the future was created.
11
12An example of a delay is provided as follows:
13
14[,rust]
15----
16use embassy::executor::{task, Executor};
17use embassy::time::{Duration, Timer};
18
19#[task]
20/// Task that ticks periodically
21async fn tick_periodic() -> ! {
22 loop {
23 rprintln!("tick!");
24 // async sleep primitive, suspends the task for 500ms.
25 Timer::after(Duration::from_millis(500)).await;
26 }
27}
28---- \ No newline at end of file
diff --git a/docs/modules/ROOT/pages/project_structure.adoc b/docs/modules/ROOT/pages/project_structure.adoc
index 3e6008ec4..61ffd05a6 100644
--- a/docs/modules/ROOT/pages/project_structure.adoc
+++ b/docs/modules/ROOT/pages/project_structure.adoc
@@ -18,7 +18,7 @@ my-project
18|- rust-toolchain.toml 18|- rust-toolchain.toml
19---- 19----
20 20
21=== .cargo/config.toml 21== .cargo/config.toml
22 22
23This 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. 23This 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.
24 24
@@ -36,17 +36,17 @@ target = "thumbv6m-none-eabi" # <-change for your platform
36DEFMT_LOG = "trace" # <- can change to info, warn, or error 36DEFMT_LOG = "trace" # <- can change to info, warn, or error
37---- 37----
38 38
39=== build.rs 39== build.rs
40 40
41This 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]. 41This 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].
42 42
43=== Cargo.toml 43== Cargo.toml
44 44
45This is your manifest file, where you can configure all of the embassy components to use the features you need. 45This is your manifest file, where you can configure all of the embassy components to use the features you need.
46 46
47TODO: someone should exhaustively describe every feature for every component! 47TODO: someone should exhaustively describe every feature for every component!
48 48
49=== memory.x 49== memory.x
50 50
51This 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. 51This 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.
52 52
@@ -63,7 +63,7 @@ MEMORY
63} 63}
64---- 64----
65 65
66=== rust-toolchain.toml 66== rust-toolchain.toml
67 67
68This file configures the rust version and configuration to use. 68This file configures the rust version and configuration to use.
69 69
diff --git a/docs/modules/ROOT/pages/sharing_peripherals.adoc b/docs/modules/ROOT/pages/sharing_peripherals.adoc
index 41f467942..fcba0e27b 100644
--- a/docs/modules/ROOT/pages/sharing_peripherals.adoc
+++ b/docs/modules/ROOT/pages/sharing_peripherals.adoc
@@ -1,6 +1,12 @@
1= Sharing peripherals between tasks 1= Sharing peripherals between tasks
2 2
3Often 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. 3Often 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.
4
5The following examples shows different ways to use the on-board LED on a Raspberry Pi Pico board by two tasks simultaneously.
6
7== Sharing using a Mutex
8
9Using mutual exclusion is the simplest way to share a peripheral.
4 10
5[,rust] 11[,rust]
6---- 12----
@@ -29,13 +35,12 @@ async fn main(spawner: Spawner) {
29 let dt = 100 * 1_000_000; 35 let dt = 100 * 1_000_000;
30 let k = 1.003; 36 let k = 1.003;
31 37
32 unwrap!(spawner.spawn(toggle(&LED, Duration::from_nanos(dt)))); 38 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));
33 unwrap!(spawner.spawn(toggle_slightly_slower( 39 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
34 &LED,
35 Duration::from_nanos((dt as f64 * k) as u64)
36 )));
37} 40}
38 41
42// A pool size of 2 means you can spawn two instances of this task.
43#[embassy_executor::task(pool_size = 2)]
39async fn toggle_led(led: &'static LedType, delay: Duration) { 44async fn toggle_led(led: &'static LedType, delay: Duration) {
40 let mut ticker = Ticker::every(delay); 45 let mut ticker = Ticker::every(delay);
41 loop { 46 loop {
@@ -48,31 +53,74 @@ async fn toggle_led(led: &'static LedType, delay: Duration) {
48 ticker.next().await; 53 ticker.next().await;
49 } 54 }
50} 55}
51#[embassy_executor::task]
52async fn toggle(led: &'static LedType, delay: Duration) {
53 toggle_led(led, delay).await
54}
55
56#[embassy_executor::task]
57async fn toggle_slightly_slower(led: &'static LedType, delay: Duration) {
58 toggle_led(led, delay).await
59}
60---- 56----
61 57
62The structure facilitating access to the resource is the defined `LedType`. 58The structure facilitating access to the resource is the defined `LedType`.
63 59
64== Why so complicated 60=== Why so complicated
65 61
66Unwrapping the layers gives insight into why each one is needed. 62Unwrapping the layers gives insight into why each one is needed.
67 63
68=== `Mutex<RawMutexType, T>` 64==== `Mutex<RawMutexType, T>`
69 65
70The 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). 66The 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).
71 67
72=== `Option<T>` 68==== `Option<T>`
73 69
74The `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`. 70The `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`.
75 71
76=== `Output<AnyPin>` 72==== `Output<AnyPin>`
77 73
78To 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. 74To 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.
75
76== Sharing using a Channel
77
78A 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.
79
80[,rust]
81----
82use defmt::*;
83use embassy_executor::Spawner;
84use embassy_rp::gpio;
85use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
86use embassy_sync::channel::{Channel, Sender};
87use embassy_time::{Duration, Ticker};
88use gpio::{AnyPin, Level, Output};
89use {defmt_rtt as _, panic_probe as _};
90
91enum LedState {
92 Toggle,
93}
94static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
95
96#[embassy_executor::main]
97async fn main(spawner: Spawner) {
98 let p = embassy_rp::init(Default::default());
99 let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);
100
101 let dt = 100 * 1_000_000;
102 let k = 1.003;
103
104 unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
105 unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos((dt as f64 * k) as u64))));
106
107 loop {
108 match CHANNEL.receive().await {
109 LedState::Toggle => led.toggle(),
110 }
111 }
112}
113
114// A pool size of 2 means you can spawn two instances of this task.
115#[embassy_executor::task(pool_size = 2)]
116async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {
117 let mut ticker = Ticker::every(delay);
118 loop {
119 control.send(LedState::Toggle).await;
120 ticker.next().await;
121 }
122}
123----
124
125This 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
126that the operation is ecompleted before continuing to do other work in your task.
diff --git a/docs/modules/ROOT/pages/time_keeping.adoc b/docs/modules/ROOT/pages/time_keeping.adoc
new file mode 100644
index 000000000..5068216ed
--- /dev/null
+++ b/docs/modules/ROOT/pages/time_keeping.adoc
@@ -0,0 +1,60 @@
1= Time-keeping
2
3In 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
4that 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
5to delay the current task for a specified interval of time.
6
7The 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
8timer queue in link:https://crates.io/crates/embassy-executor[embassy-executor] or a custom timer queue implementation.
9
10== Timer
11
12The `embassy::time::Timer` type provides two timing methods.
13
14`Timer::at` creates a future that completes at the specified `Instant`, relative to the system boot time.
15`Timer::after` creates a future that completes after the specified `Duration`, relative to when the future was created.
16
17An example of a delay is provided as follows:
18
19[,rust]
20----
21use embassy::executor::{task, Executor};
22use embassy::time::{Duration, Timer};
23
24#[task]
25/// Task that ticks periodically
26async fn tick_periodic() -> ! {
27 loop {
28 rprintln!("tick!");
29 // async sleep primitive, suspends the task for 500ms.
30 Timer::after(Duration::from_millis(500)).await;
31 }
32}
33----
34
35== Delay
36
37The `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
38link:https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/index.html[embedded-hal-async] traits. This can be used for drivers
39that expect a generic delay implementation to be provided.
40
41An example of how this can be used:
42
43[,rust]
44----
45use embassy::executor::{task, Executor};
46
47#[task]
48/// Task that ticks periodically
49async fn tick_periodic() -> ! {
50 loop {
51 rprintln!("tick!");
52 // async sleep primitive, suspends the task for 500ms.
53 generic_delay(embassy::time::Delay).await
54 }
55}
56
57async fn generic_delay<D: embedded_hal_async::delay::DelayNs>(delay: D) {
58 delay.delay_ms(500).await;
59}
60----