mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-24 19:09:33 +01:00
Docs: By-example
This commit is contained in:
parent
37facfb5bf
commit
4357d8be15
8 changed files with 90 additions and 61 deletions
|
@ -3,15 +3,15 @@
|
|||
This part of the book introduces the Real-Time Interrupt-driven Concurrency (RTIC) framework
|
||||
to new users by walking them through examples of increasing complexity.
|
||||
|
||||
All examples in this part of the book can be found in the GitHub [repository] of
|
||||
the project. The examples can be run on QEMU (emulating a Cortex M3 target) so no special hardware
|
||||
is required to follow along.
|
||||
All examples in this part of the book are accessible at the
|
||||
[GitHub repository][repoexamples].
|
||||
The examples are runnable on QEMU (emulating a Cortex M3 target),
|
||||
thus no special hardware required to follow along.
|
||||
|
||||
[repository]: https://github.com/rtic-rs/cortex-m-rtic
|
||||
[repoexamples]: https://github.com/rtic-rs/cortex-m-rtic/tree/master/examples
|
||||
|
||||
To run the examples on your computer you'll need the `qemu-system-arm`
|
||||
program. Check [the embedded Rust book] for instructions on how to set up an
|
||||
To run the examples with QEMU you will need the `qemu-system-arm` program.
|
||||
Check [the embedded Rust book] for instructions on how to set up an
|
||||
embedded development environment that includes QEMU.
|
||||
|
||||
[the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html
|
||||
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
## Requirements on the `app` attribute
|
||||
|
||||
All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute
|
||||
must be applied to a `mod`-item containing the RTIC application. The `app`
|
||||
attribute has a mandatory `device`
|
||||
argument that takes a *path* as a value. This must be a full path pointing to a
|
||||
only applies to a `mod`-item containing the RTIC application. The `app`
|
||||
attribute has a mandatory `device` argument that takes a *path* as a value.
|
||||
This must be a full path pointing to a
|
||||
*peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or
|
||||
newer.
|
||||
|
||||
The `app` attribute will expand into a suitable entry point so it's not required
|
||||
to use the [`cortex_m_rt::entry`] attribute.
|
||||
The `app` attribute will expand into a suitable entry point and thus replaces
|
||||
the use of the [`cortex_m_rt::entry`] attribute.
|
||||
|
||||
[`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html
|
||||
[`svd2rust`]: https://crates.io/crates/svd2rust
|
||||
|
@ -18,9 +18,9 @@ to use the [`cortex_m_rt::entry`] attribute.
|
|||
|
||||
## An RTIC application example
|
||||
|
||||
To give a flavor of RTIC, the following example contains commonly used features. In the following sections we will go through each feature in detail.
|
||||
To give a flavour of RTIC, the following example contains commonly used features.
|
||||
In the following sections we will go through each feature in detail.
|
||||
|
||||
``` rust
|
||||
{{#include ../../../../examples/common.rs}}
|
||||
```
|
||||
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
# The background task `#[idle]`
|
||||
|
||||
A function marked with the `idle` attribute can optionally appear in the
|
||||
module. This function is used as the special *idle task* and must have
|
||||
signature `fn(idle::Context) -> !`.
|
||||
module. This becomes the special *idle task* and must have signature
|
||||
`fn(idle::Context) -> !`.
|
||||
|
||||
When present, the runtime will execute the `idle` task after `init`. Unlike
|
||||
`init`, `idle` will run *with interrupts enabled* and it's not allowed to return
|
||||
so it must run forever.
|
||||
`init`, `idle` will run *with interrupts enabled* and must never return,
|
||||
as the `-> !` function signature indicates.
|
||||
[The Rust type `!` means “never”][nevertype].
|
||||
|
||||
Like in `init`, locally declared resources will have `'static` lifetimes that are safe to access.
|
||||
[nevertype]: https://doc.rust-lang.org/core/primitive.never.html
|
||||
|
||||
Like in `init`, locally declared resources will have `'static` lifetimes that
|
||||
are safe to access.
|
||||
|
||||
The example below shows that `idle` runs after `init`.
|
||||
|
||||
|
@ -21,9 +25,9 @@ $ cargo run --target thumbv7m-none-eabi --example idle
|
|||
{{#include ../../../../ci/expected/idle.run}}
|
||||
```
|
||||
|
||||
By default the RTIC `idle` task does not try to optimise for any specific targets.
|
||||
By default, the RTIC `idle` task does not try to optimize for any specific targets.
|
||||
|
||||
A common useful optimisation is to enable the [SLEEPONEXIT] and allow the MCU
|
||||
A common useful optimization is to enable the [SLEEPONEXIT] and allow the MCU
|
||||
to enter sleep when reaching `idle`.
|
||||
|
||||
>**Caution** some hardware unless configured disables the debug unit during sleep mode.
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
# App initialization and the `#[init]` task
|
||||
|
||||
An RTIC application is required an `init` task setting up the system. The corresponding function must have the signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are the resource structures defined by the user.
|
||||
An RTIC application requires an `init` task setting up the system. The corresponding `init` function must have the
|
||||
signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are the resource
|
||||
structures defined by the user.
|
||||
|
||||
On system reset, the `init` task is executed (after the optionally defined `pre-init` and internal RTIC initialization). The `init` task runs *with interrupts disabled* and has exclusive access to Cortex-M (the `bare_metal::CriticalSection` token is available as `cs`) while device specific peripherals are available through the `core` and `device` fields of `init::Context`.
|
||||
The `init` task executes after system reset (after the optionally defined `pre-init` and internal RTIC
|
||||
initialization). The `init` task runs *with interrupts disabled* and has exclusive access to Cortex-M (the
|
||||
`bare_metal::CriticalSection` token is available as `cs`) while device specific peripherals are available through
|
||||
the `core` and `device` fields of `init::Context`.
|
||||
|
||||
## Example
|
||||
|
||||
The example below shows the types of the `core`, `device` and `cs` fields, and showcases the use of a `local` variable with `'static` lifetime. As we will see later, such variables can later be delegated from `init` to other tasks of the RTIC application.
|
||||
The example below shows the types of the `core`, `device` and `cs` fields, and showcases the use of a `local`
|
||||
variable with `'static` lifetime.
|
||||
Such variables can be delegated from the `init` task to other tasks of the RTIC application.
|
||||
|
||||
The `device` field is only available when the `peripherals` argument is set to `true` (which is the default). In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`.
|
||||
The `device` field is available when the `peripherals` argument is set to the default value `true`.
|
||||
In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`.
|
||||
|
||||
``` rust
|
||||
{{#include ../../../../examples/init.rs}}
|
||||
|
@ -16,13 +24,13 @@ The `device` field is only available when the `peripherals` argument is set to `
|
|||
|
||||
Running the example will print `init` to the console and then exit the QEMU process.
|
||||
|
||||
``` console
|
||||
``` console
|
||||
$ cargo run --target thumbv7m-none-eabi --example init
|
||||
{{#include ../../../../ci/expected/init.run}}
|
||||
```
|
||||
|
||||
> **NOTE**: You can choose target device by passing a target
|
||||
> triple to cargo (e.g `cargo run --example init --target thumbv7m-none-eabi`) or
|
||||
> triple to cargo (e.g. `cargo run --example init --target thumbv7m-none-eabi`) or
|
||||
> configure a default target in `.cargo/config.toml`.
|
||||
>
|
||||
> For running the examples, we use a Cortex M3 emulated in QEMU so the target is `thumbv7m-none-eabi`.
|
||||
> For running the examples, we use a Cortex M3 emulated in QEMU, so the target is `thumbv7m-none-eabi`.
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
# Defining tasks with `#[task]`
|
||||
|
||||
Tasks, defined with `#[task]`, are the main mechanism of getting work done in RTIC. Every task can be spawned, now or later, be sent messages (message passing) and be given priorities for preemptive multitasking.
|
||||
Tasks, defined with `#[task]`, are the main mechanism of getting work done in RTIC.
|
||||
|
||||
There are two kinds of tasks, software tasks and hardware tasks, and the difference is that hardware tasks are bound to a specific interrupt vector in the MCU while software tasks are not. This means that if a hardware task is bound to the UART's RX interrupt the task will run every time a character is received.
|
||||
Tasks can
|
||||
|
||||
* Be spawned (now or in the future)
|
||||
* Receive messages (message passing)
|
||||
* Prioritized allowing preemptive multitasking
|
||||
* Optionally bind to a hardware interrupt
|
||||
|
||||
RTIC makes a distinction between “software tasks” and “hardware tasks”.
|
||||
Hardware tasks are tasks that are bound to a specific interrupt vector in the MCU while software tasks are not.
|
||||
|
||||
This means that if a hardware task is bound to an UART RX interrupt the task will run every
|
||||
time this interrupt triggers, usually when a character is received.
|
||||
|
||||
In the coming pages we will explore both tasks and the different options available.
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
# Hardware tasks
|
||||
|
||||
At its core RTIC is based on using the interrupt controller in the hardware to do scheduling and
|
||||
run tasks, as all tasks in the framework are run as interrupt handlers (except `#[init]` and
|
||||
`#[idle]`). This also means that you can directly bind tasks to interrupt handlers.
|
||||
At its core RTIC is using the hardware interrupt controller ([ARM NVIC on cortex-m][NVIC])
|
||||
to perform scheduling and executing tasks, and all tasks except `#[init]` and `#[idle]`
|
||||
run as interrupt handlers.
|
||||
This also means that you can manually bind tasks to interrupt handlers.
|
||||
|
||||
To declare interrupt handlers the `#[task]` attribute takes a `binds = InterruptName` argument whose
|
||||
value is the name of the interrupt to which the handler will be bound to; the
|
||||
function used with this attribute becomes the interrupt handler. Within the
|
||||
framework these type of tasks are referred to as *hardware* tasks, because they
|
||||
start executing in reaction to a hardware event.
|
||||
To bind an interrupt use the `#[task]` attribute argument `binds = InterruptName`.
|
||||
This task becomes the interrupt handler for this hardware interrupt vector.
|
||||
|
||||
Providing an interrupt name that does not exist will cause a compile error to help with accidental
|
||||
errors.
|
||||
All tasks bound to an explicit interrupt are *hardware tasks* since they
|
||||
start execution in reaction to a hardware event.
|
||||
|
||||
Specifying a non-existing interrupt name will cause a compilation error. The interrupt names
|
||||
are commonly defined by [PAC or HAL][pacorhal] crates.
|
||||
|
||||
[pacorhal]: https://docs.rust-embedded.org/book/start/registers.html
|
||||
[NVIC]: https://developer.arm.com/documentation/100166/0001/Nested-Vectored-Interrupt-Controller/NVIC-functional-description/NVIC-interrupts
|
||||
|
||||
The example below demonstrates the use of the `#[task]` attribute to declare an
|
||||
interrupt handler.
|
||||
|
@ -24,4 +28,3 @@ interrupt handler.
|
|||
$ cargo run --target thumbv7m-none-eabi --example hardware
|
||||
{{#include ../../../../ci/expected/hardware.run}}
|
||||
```
|
||||
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
# Resource usage
|
||||
|
||||
The RTIC framework manages shared and task local resources which allows data to be persistently
|
||||
stored and safely accessed without the use of unsafe code.
|
||||
The RTIC framework manages shared and task local resources allowing persistent data
|
||||
storage and safe accesses without the use of `unsafe` code.
|
||||
|
||||
RTIC resources are visible only to functions declared within the `#[app]` module and the framework
|
||||
gives the user complete control (on a per-task basis) over resource accessibility.
|
||||
|
||||
System wide resources are declared as **two** `struct`'s within the `#[app]` module annotated with
|
||||
the attribute `#[local]` and `#[shared]` respectively. Each field in these structures corresponds
|
||||
to a different resource (identified by field name). The difference between these two sets of
|
||||
resources will be covered below.
|
||||
Declaration of system-wide resources are by annotating **two** `struct`s within the `#[app]` module
|
||||
with the attribute `#[local]` and `#[shared]`.
|
||||
Each field in these structures corresponds to a different resource (identified by field name).
|
||||
The difference between these two sets of resources will be covered below.
|
||||
|
||||
Each task must declare the resources it intends to access in its corresponding metadata attribute
|
||||
using the `local` and `shared` arguments. Each argument takes a list of resource identifiers. The
|
||||
listed resources are made available to the context under the `local` and `shared` fields of the
|
||||
using the `local` and `shared` arguments. Each argument takes a list of resource identifiers.
|
||||
The listed resources are made available to the context under the `local` and `shared` fields of the
|
||||
`Context` structure.
|
||||
|
||||
The `init` task returns the initial values for the system wide (`#[shared]` and `#[local]`)
|
||||
The `init` task returns the initial values for the system-wide (`#[shared]` and `#[local]`)
|
||||
resources, and the set of initialized timers used by the application. The monotonic timers will be
|
||||
further discussed in [Monotonic & `spawn_{at/after}`](./monotonic.md).
|
||||
|
||||
|
@ -27,6 +27,9 @@ access the resource and does so without locks or critical sections. This allows
|
|||
commonly drivers or large objects, to be initialized in `#[init]` and then be passed to a specific
|
||||
task.
|
||||
|
||||
Thus, a task `#[local]` resource can only be accessed by one singular task.
|
||||
Attempting to assign the same `#[local]` resource to more than one task is a compile-time error.
|
||||
|
||||
The example application shown below contains two tasks where each task has access to its own
|
||||
`#[local]` resource, plus that the `idle` task has its own `#[local]` as well.
|
||||
|
||||
|
@ -39,15 +42,12 @@ $ cargo run --target thumbv7m-none-eabi --example locals
|
|||
{{#include ../../../../ci/expected/locals.run}}
|
||||
```
|
||||
|
||||
A `#[local]` resource cannot be accessed from outside the task it was associated to in a `#[task]` attribute.
|
||||
Assigning the same `#[local]` resource to more than one task is a compile-time error.
|
||||
|
||||
### Task local initialized resources
|
||||
|
||||
A special use-case of local resources are the ones specified directly in the resource claim,
|
||||
`#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`, this allows for creating locals which do no need to be
|
||||
initialized in `#[init]`.
|
||||
Moreover local resources in `#[init]` and `#[idle]` have `'static` lifetimes, this is safe since both are not re-entrant.
|
||||
Moreover, local resources in `#[init]` and `#[idle]` have `'static` lifetimes, this is safe since both are not re-entrant.
|
||||
|
||||
In the example below the different uses and lifetimes are shown:
|
||||
|
||||
|
@ -96,7 +96,7 @@ $ cargo run --target thumbv7m-none-eabi --example lock
|
|||
## Multi-lock
|
||||
|
||||
As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The
|
||||
following examples shows this in use:
|
||||
following examples show this in use:
|
||||
|
||||
``` rust
|
||||
{{#include ../../../../examples/multilock.rs}}
|
||||
|
@ -109,12 +109,12 @@ $ cargo run --target thumbv7m-none-eabi --example multilock
|
|||
|
||||
## Only shared (`&-`) access
|
||||
|
||||
By default the framework assumes that all tasks require exclusive access (`&mut-`) to resources but
|
||||
it is possible to specify that a task only requires shared access (`&-`) to a resource using the
|
||||
By default, the framework assumes that all tasks require exclusive access (`&mut-`) to resources,
|
||||
but it is possible to specify that a task only requires shared access (`&-`) to a resource using the
|
||||
`&resource_name` syntax in the `shared` list.
|
||||
|
||||
The advantage of specifying shared access (`&-`) to a resource is that no locks are required to
|
||||
access the resource even if the resource is contended by several tasks running at different
|
||||
access the resource even if the resource is contended by more than one task running at different
|
||||
priorities. The downside is that the task only gets a shared reference (`&-`) to the resource,
|
||||
limiting the operations it can perform on it, but where a shared reference is enough this approach
|
||||
reduces the number of required locks. In addition to simple immutable data, this shared access can
|
||||
|
@ -142,8 +142,11 @@ $ cargo run --target thumbv7m-none-eabi --example only-shared-access
|
|||
A critical section is *not* required to access a `#[shared]` resource that's only accessed by tasks
|
||||
running at the *same* priority. In this case, you can opt out of the `lock` API by adding the
|
||||
`#[lock_free]` field-level attribute to the resource declaration (see example below). Note that
|
||||
this is merely a convenience: if you do use the `lock` API, at runtime the framework will
|
||||
**not** produce a critical section. Also worth noting: using `#[lock_free]` on resources shared by
|
||||
this is merely a convenience to reduce needless resource locking code, because even if the
|
||||
`lock` API is used, at runtime the framework will **not** produce a critical section due to how
|
||||
the underlying resource-ceiling preemption works.
|
||||
|
||||
Also worth noting: using `#[lock_free]` on resources shared by
|
||||
tasks running at different priorities will result in a *compile-time* error -- not using the `lock`
|
||||
API would be a data race in that case.
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
# Preface
|
||||
|
||||
This book contains user level documentation for the Real-Time Interrupt-driven Concurrency
|
||||
(RTIC) framework. The API reference can be found [here](../../api/).
|
||||
(RTIC) framework. The API reference available [here](../../api/).
|
||||
|
||||
Formerly known as Real-Time For the Masses.
|
||||
|
||||
|
|
Loading…
Reference in a new issue