mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-25 21:19:35 +01:00
Update delay.md file to be a bit easier to read, and add spoiler tags for the walls of code
This commit is contained in:
parent
3d98102a54
commit
a14d24007a
1 changed files with 36 additions and 22 deletions
|
@ -1,18 +1,20 @@
|
||||||
# Tasks with delay
|
# Tasks with delay
|
||||||
|
|
||||||
A convenient way to express *miniminal* timing requirements is by means of delaying progression.
|
A convenient way to express miniminal timing requirements is by delaying progression.
|
||||||
|
|
||||||
This can be achieved by instantiating a monotonic timer:
|
This can be achieved by instantiating a monotonic timer (for implementations, see [`rtic-monotonics`]):
|
||||||
|
|
||||||
|
[`rtic-monotonics`]: https://github.com/rtic-rs/rtic/tree/master/rtic-monotonics
|
||||||
|
[`rtic-time`]: https://github.com/rtic-rs/rtic/tree/master/rtic-time
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
...
|
...
|
||||||
rtic_monotonics::make_systick_handler!();
|
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
fn init(cx: init::Context) -> (Shared, Local) {
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
hprintln!("init");
|
hprintln!("init");
|
||||||
|
|
||||||
Systick::start(cx.core.SYST, 12_000_000);
|
let token = rtic_monotonics::create_systick_token!();
|
||||||
|
Systick::start(cx.core.SYST, 12_000_000, token);
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -28,11 +30,14 @@ async fn foo(_cx: foo::Context) {
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Technically, the timer queue is implemented as a list based priority queue, where list-nodes are statically allocated as part of the underlying task `Future`. Thus, the timer queue is infallible at run-time (its size and allocation is determined at compile time).
|
<!-- TODO: move technical explanation to internals -->
|
||||||
|
|
||||||
|
Technically, the timer queue is implemented as a list based priority queue, where list-nodes are statically allocated as part of the underlying task `Future`. Thus, the timer queue is infallible at run-time (its size and allocation are determined at compile time).
|
||||||
|
|
||||||
Similarly the channels implementation, the timer-queue implementation relies on a global *Critical Section* (CS) for race protection. For the examples a CS implementation is provided by adding `--features test-critical-section` to the build options.
|
Similarly the channels implementation, the timer-queue implementation relies on a global *Critical Section* (CS) for race protection. For the examples a CS implementation is provided by adding `--features test-critical-section` to the build options.
|
||||||
|
|
||||||
For a complete example:
|
<details>
|
||||||
|
<summary>A complete example</summary>
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../rtic/examples/async-delay.rs}}
|
{{#include ../../../../rtic/examples/async-delay.rs}}
|
||||||
|
@ -46,11 +51,15 @@ $ cargo run --target thumbv7m-none-eabi --example async-delay --features test-cr
|
||||||
{{#include ../../../../rtic/ci/expected/async-delay.run}}
|
{{#include ../../../../rtic/ci/expected/async-delay.run}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## Timeout
|
## Timeout
|
||||||
|
|
||||||
Rust `Futures` (underlying Rust `async`/`await`) are composable. This makes it possible to `select` in between `Futures` that have completed.
|
Rust [`Future`]s (underlying Rust `async`/`await`) are composable. This makes it possible to `select` in between `Futures` that have completed.
|
||||||
|
|
||||||
A common use case is transactions with associated timeout. In the examples shown below, we introduce a fake HAL device which performs some transaction. We have modelled the time it takes based on the input parameter (`n`) as `350ms + n * 100ms)`.
|
[`Future`]: https://doc.rust-lang.org/std/future/trait.Future.html
|
||||||
|
|
||||||
|
A common use case is transactions with an associated timeout. In the examples shown below, we introduce a fake HAL device that performs some transaction. We have modelled the time it takes based on the input parameter (`n`) as `350ms + n * 100ms`.
|
||||||
|
|
||||||
Using the `select_biased` macro from the `futures` crate it may look like this:
|
Using the `select_biased` macro from the `futures` crate it may look like this:
|
||||||
|
|
||||||
|
@ -62,19 +71,13 @@ select_biased! {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Assuming the `hal_get` will take 450ms to finish, a short timeout of 200ms will expire.
|
Assuming the `hal_get` will take 450ms to finish, a short timeout of 200ms will expire before `hal_get` can complete.
|
||||||
|
|
||||||
``` rust
|
Extending the timeout to 1000ms would cause `hal_get` will to complete first.
|
||||||
// Call hal with long relative timeout using `select_biased`
|
|
||||||
select_biased! {
|
|
||||||
v = hal_get(1).fuse() => hprintln!("hal returned {}", v), // hal finish first
|
|
||||||
_ = Systick::delay(1000.millis()).fuse() => hprintln!("timeout", ),
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
By extending the timeout to 1000ms, the `hal_get` will finish first.
|
Using `select_biased` any number of futures can be combined, so its very powerful. However, as the timeout pattern is frequently used, more ergonomic support is baked into RTIC, provided by the [`rtic-monotonics`] and [`rtic-time`] crates.
|
||||||
|
|
||||||
Using `select_biased` any number of futures can be combined, so its very powerful. However, as the timeout pattern is frequently used, it is directly supported by the RTIC [rtc-monotonics] and [rtic-time] crates. The second example from above using `timeout_after`:
|
Rewriting the second example from above using `timeout_after` gives:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
// Call hal with long relative timeout using monotonic `timeout_after`
|
// Call hal with long relative timeout using monotonic `timeout_after`
|
||||||
|
@ -84,7 +87,7 @@ match Systick::timeout_after(1000.millis(), hal_get(1)).await {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In cases you want exact control over time without drift. For this purpose we can use exact points in time using `Instance`, and spans of time using `Duration`. Operations on the `Instance` and `Duration` types are given by the [fugit] crate.
|
In cases where you want exact control over time without drift we can use exact points in time using `Instant`, and spans of time using `Duration`. Operations on the `Instant` and `Duration` types come from the [`fugit`] crate.
|
||||||
|
|
||||||
[fugit]: https://crates.io/crates/fugit
|
[fugit]: https://crates.io/crates/fugit
|
||||||
|
|
||||||
|
@ -109,10 +112,20 @@ for n in 0..3 {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`instant = Systick::now()` gives the baseline (i.e., the absolute current point in time). We want to call `hal_get` after 1000ms relative to this absolute point in time. This can be accomplished by `Systick::delay_until(instant).await;`. We define the absolute point in time for the `timeout`, and call `Systick::timeout_at(timeout, hal_get(n)).await`. For the first loop iteration `n == 0`, and the `hal_get` will take 350ms (and finishes before the timeout). For the second iteration `n == 1`, and `hal_get` will take 450ms (and again succeeds to finish before the timeout). For the third iteration `n == 2` (`hal_get` will take 5500ms to finish). In this case we will run into a timeout.
|
`let mut instant = Systick::now()` sets the starting time of execution.
|
||||||
|
|
||||||
|
We want to call `hal_get` after 1000ms relative to this starting time. This can be accomplished by using `Systick::delay_until(instant).await`.
|
||||||
|
|
||||||
The complete example:
|
Then, we define a point in time called `timeout`, and call `Systick::timeout_at(timeout, hal_get(n)).await`.
|
||||||
|
|
||||||
|
For the first iteration of the loop, with `n == 0`, the `hal_get` will take 350ms (and finishes before the timeout).
|
||||||
|
|
||||||
|
For the second iteration, with `n == 1`, the `hal_get` will take 450ms (and again succeeds to finish before the timeout).
|
||||||
|
|
||||||
|
For the third iteration, with `n == 2`, `hal_get` will take 550ms to finish, in which case we will run into a timeout.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>A complete example</summary>
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../rtic/examples/async-timeout.rs}}
|
{{#include ../../../../rtic/examples/async-timeout.rs}}
|
||||||
|
@ -125,3 +138,4 @@ $ cargo run --target thumbv7m-none-eabi --example async-timeout --features test-
|
||||||
``` console
|
``` console
|
||||||
{{#include ../../../../rtic/ci/expected/async-timeout.run}}
|
{{#include ../../../../rtic/ci/expected/async-timeout.run}}
|
||||||
```
|
```
|
||||||
|
</details>
|
||||||
|
|
Loading…
Reference in a new issue