mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-29 15:04:32 +01:00
Merge #563
563: Docs touchup r=korken89 a=AfoHT Unleashed some language linters on the book Co-authored-by: Henrik Tjäder <henrik@grepit.se> Co-authored-by: perlindgren <per.lindgren@ltu.se>
This commit is contained in:
commit
c78177c37e
23 changed files with 291 additions and 185 deletions
|
@ -1 +1,8 @@
|
||||||
# Awesome RTIC examples
|
# Awesome RTIC examples
|
||||||
|
|
||||||
|
See the [`rtic-rs/rtic-examples`][rticexamples] repository for community
|
||||||
|
provided complete examples.
|
||||||
|
|
||||||
|
Pull-requests to this repo are welcome!
|
||||||
|
|
||||||
|
[rticexamples]: https://github.com/rtic-rs/rtic-examples
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
This part of the book introduces the Real-Time Interrupt-driven Concurrency (RTIC) framework
|
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.
|
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
|
All examples in this part of the book are accessible at the
|
||||||
the project. The examples can be run on QEMU (emulating a Cortex M3 target) so no special hardware
|
[GitHub repository][repoexamples].
|
||||||
is required to follow along.
|
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`
|
To run the examples with QEMU you will need the `qemu-system-arm` program.
|
||||||
program. Check [the embedded Rust book] for instructions on how to set up an
|
Check [the embedded Rust book] for instructions on how to set up an
|
||||||
embedded development environment that includes QEMU.
|
embedded development environment that includes QEMU.
|
||||||
|
|
||||||
[the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html
|
[the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
## Requirements on the `app` attribute
|
## Requirements on the `app` attribute
|
||||||
|
|
||||||
All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute
|
All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute
|
||||||
must be applied to a `mod`-item containing the RTIC application. The `app`
|
only applies to a `mod`-item containing the RTIC application. The `app`
|
||||||
attribute has a mandatory `device`
|
attribute has a mandatory `device` argument that takes a *path* as a value.
|
||||||
argument that takes a *path* as a value. This must be a full path pointing to a
|
This must be a full path pointing to a
|
||||||
*peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or
|
*peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or
|
||||||
newer.
|
newer.
|
||||||
|
|
||||||
The `app` attribute will expand into a suitable entry point so it's not required
|
The `app` attribute will expand into a suitable entry point and thus replaces
|
||||||
to use the [`cortex_m_rt::entry`] attribute.
|
the use of the [`cortex_m_rt::entry`] attribute.
|
||||||
|
|
||||||
[`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html
|
[`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html
|
||||||
[`svd2rust`]: https://crates.io/crates/svd2rust
|
[`svd2rust`]: https://crates.io/crates/svd2rust
|
||||||
|
@ -18,9 +18,9 @@ to use the [`cortex_m_rt::entry`] attribute.
|
||||||
|
|
||||||
## An RTIC application example
|
## 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
|
``` rust
|
||||||
{{#include ../../../../examples/common.rs}}
|
{{#include ../../../../examples/common.rs}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
# The background task `#[idle]`
|
# The background task `#[idle]`
|
||||||
|
|
||||||
A function marked with the `idle` attribute can optionally appear in the
|
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
|
module. This becomes the special *idle task* and must have signature
|
||||||
signature `fn(idle::Context) -> !`.
|
`fn(idle::Context) -> !`.
|
||||||
|
|
||||||
When present, the runtime will execute the `idle` task after `init`. Unlike
|
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
|
`init`, `idle` will run *with interrupts enabled* and must never return,
|
||||||
so it must run forever.
|
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`.
|
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}}
|
{{#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`.
|
to enter sleep when reaching `idle`.
|
||||||
|
|
||||||
>**Caution** some hardware unless configured disables the debug unit during sleep mode.
|
>**Caution** some hardware unless configured disables the debug unit during sleep mode.
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
# App initialization and the `#[init]` task
|
# 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
|
## 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
|
``` rust
|
||||||
{{#include ../../../../examples/init.rs}}
|
{{#include ../../../../examples/init.rs}}
|
||||||
|
@ -22,7 +30,7 @@ $ cargo run --target thumbv7m-none-eabi --example init
|
||||||
```
|
```
|
||||||
|
|
||||||
> **NOTE**: You can choose target device by passing a target
|
> **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`.
|
> 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`.
|
||||||
|
|
|
@ -2,26 +2,41 @@
|
||||||
|
|
||||||
## Priorities
|
## Priorities
|
||||||
|
|
||||||
The static priority of each handler can be declared in the `task` attribute
|
The `priority` argument declares the static priority of each `task`.
|
||||||
using the `priority` argument. For Cortex-M, tasks can have priorities in the range `1..=(1 <<
|
|
||||||
NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device`
|
For Cortex-M, tasks can have priorities in the range `1..=(1 << NVIC_PRIO_BITS)`
|
||||||
crate. When the `priority` argument is omitted, the priority is assumed to be
|
where `NVIC_PRIO_BITS` is a constant defined in the `device` crate.
|
||||||
`1`. The `idle` task has a non-configurable static priority of `0`, the lowest priority.
|
|
||||||
|
Omitting the `priority` argument the task priority defaults to `1`.
|
||||||
|
The `idle` task has a non-configurable static priority of `0`, the lowest priority.
|
||||||
|
|
||||||
> A higher number means a higher priority in RTIC, which is the opposite from what
|
> A higher number means a higher priority in RTIC, which is the opposite from what
|
||||||
> Cortex-M does in the NVIC peripheral.
|
> Cortex-M does in the NVIC peripheral.
|
||||||
> Explicitly, this means that number `10` has a **higher** priority than number `9`.
|
> Explicitly, this means that number `10` has a **higher** priority than number `9`.
|
||||||
|
|
||||||
When several tasks are ready to be executed the one with highest static
|
The highest static priority task takes precedence when more than one
|
||||||
priority will be executed first. Task prioritization can be observed in the
|
task are ready to execute.
|
||||||
following scenario: during the execution of a low
|
|
||||||
priority task a higher priority task is spawned; this puts the higher priority task in the pending state.
|
|
||||||
The difference in priority results in the higher priority task preempting the
|
|
||||||
lower priority one: the execution of the lower priority task is suspended and
|
|
||||||
the higher priority task is executed to completion. Once the higher priority
|
|
||||||
task has terminated the lower priority task is resumed.
|
|
||||||
|
|
||||||
The following example showcases the priority based scheduling of tasks.
|
The following scenario demonstrates task prioritization:
|
||||||
|
Spawning a higher priority task A during execution of a lower priority task B pends
|
||||||
|
task A. Task A has higher priority thus preempting task B which gets suspended
|
||||||
|
until task A completes execution. Thus, when task A completes task B resumes execution.
|
||||||
|
|
||||||
|
```text
|
||||||
|
Task Priority
|
||||||
|
┌────────────────────────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
3 │ Preempts │
|
||||||
|
2 │ A─────────► │
|
||||||
|
1 │ B─────────► - - - - B────────► │
|
||||||
|
0 │Idle┌─────► Resumes ┌──────────► │
|
||||||
|
├────┴──────────────────────────────────┴────────────────┤
|
||||||
|
│ │
|
||||||
|
└────────────────────────────────────────────────────────┘Time
|
||||||
|
```
|
||||||
|
|
||||||
|
The following example showcases the priority based scheduling of tasks:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../examples/preempt.rs}}
|
{{#include ../../../../examples/preempt.rs}}
|
||||||
|
@ -33,13 +48,24 @@ $ cargo run --target thumbv7m-none-eabi --example preempt
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the task `bar` does *not* preempt task `baz` because its priority
|
Note that the task `bar` does *not* preempt task `baz` because its priority
|
||||||
is the *same* as `baz`'s. However, once `baz` returns, the execution of
|
is the *same* as `baz`'s. The higher priority task `bar` runs before `foo`
|
||||||
task `bar` is prioritized over `foo` due to its higher priority. `foo`
|
when `baz`returns. When `bar` returns `foo` can resume.
|
||||||
is resumed only after `bar` returns.
|
|
||||||
|
|
||||||
One more note about priorities: choosing a priority higher than what the device
|
One more note about priorities: choosing a priority higher than what the device
|
||||||
supports will result in a compile error. Due to
|
supports will result in a compilation error.
|
||||||
limitations in the language, the error message is currently far from helpful: it
|
The error is cryptic due to limitations in the language,
|
||||||
will say something along the lines of "evaluation of constant value failed" and
|
if `priority = 9` for task `uart0_interrupt` in `example/common.rs` this looks like:
|
||||||
the span of the error will *not* point out to the problematic interrupt value --
|
|
||||||
we are sorry about this!
|
```text
|
||||||
|
error[E0080]: evaluation of constant value failed
|
||||||
|
--> examples/common.rs:10:1
|
||||||
|
|
|
||||||
|
10 | #[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0])]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `8_usize - 9_usize`, which would overflow
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `rtic::app` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The error message incorrectly points to the starting point of the macro, but at least the
|
||||||
|
value subtracted (in this case 9) will suggest which task causes the error.
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
# Defining tasks with `#[task]`
|
# 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.
|
In the coming pages we will explore both tasks and the different options available.
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
# Hardware tasks
|
# Hardware tasks
|
||||||
|
|
||||||
At its core RTIC is based on using the interrupt controller in the hardware to do scheduling and
|
At its core RTIC is using the hardware interrupt controller ([ARM NVIC on cortex-m][NVIC])
|
||||||
run tasks, as all tasks in the framework are run as interrupt handlers (except `#[init]` and
|
to perform scheduling and executing tasks, and all tasks except `#[init]` and `#[idle]`
|
||||||
`#[idle]`). This also means that you can directly bind tasks to interrupt handlers.
|
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
|
To bind an interrupt use the `#[task]` attribute argument `binds = InterruptName`.
|
||||||
value is the name of the interrupt to which the handler will be bound to; the
|
This task becomes the interrupt handler for this hardware interrupt vector.
|
||||||
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.
|
|
||||||
|
|
||||||
Providing an interrupt name that does not exist will cause a compile error to help with accidental
|
All tasks bound to an explicit interrupt are *hardware tasks* since they
|
||||||
errors.
|
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
|
The example below demonstrates the use of the `#[task]` attribute to declare an
|
||||||
interrupt handler.
|
interrupt handler.
|
||||||
|
@ -24,4 +28,3 @@ interrupt handler.
|
||||||
$ cargo run --target thumbv7m-none-eabi --example hardware
|
$ cargo run --target thumbv7m-none-eabi --example hardware
|
||||||
{{#include ../../../../ci/expected/hardware.run}}
|
{{#include ../../../../ci/expected/hardware.run}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
# Message passing & capacity
|
# Message passing & capacity
|
||||||
|
|
||||||
Software tasks have support for message passing, this means that they can be spawned with an argument
|
Software tasks support message passing, this means that software tasks can be spawned
|
||||||
as `foo::spawn(1)` which will run the task `foo` with the argument `1`. The number of arguments is not
|
with an argument: `foo::spawn(1)` which will run the task `foo` with the argument `1`.
|
||||||
limited and is exemplified in the following:
|
|
||||||
|
Capacity sets the size of the spawn queue for the task, if not specified capacity defaults to 1.
|
||||||
|
|
||||||
|
In the example below, the capacity of task `foo` is `3`, allowing three simultaneous
|
||||||
|
pending spawns of `foo`. Exceeding this capacity is an `Error`.
|
||||||
|
|
||||||
|
The number of arguments to a task is not limited:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../examples/message_passing.rs}}
|
{{#include ../../../../examples/message_passing.rs}}
|
||||||
|
|
|
@ -1,33 +1,38 @@
|
||||||
# Monotonic & spawn_{at/after}
|
# Monotonic & spawn_{at/after}
|
||||||
|
|
||||||
The understanding of time is an important concept in embedded systems, and to be able to run tasks
|
The understanding of time is an important concept in embedded systems, and to be able to run tasks
|
||||||
based on time is very useful. For this use-case the framework provides the static methods
|
based on time is useful. For this use-case the framework provides the static methods
|
||||||
`task::spawn_after(/* duration */)` and `task::spawn_at(/* specific time instant */)`.
|
`task::spawn_after(/* duration */)` and `task::spawn_at(/* specific time instant */)`.
|
||||||
Mostly one uses `spawn_after`, but in cases where it's needed to have spawns happen without drift or
|
`spawn_after` is more commonly used, but in cases where it's needed to have spawns happen
|
||||||
to a fixed baseline `spawn_at` is available.
|
without drift or to a fixed baseline `spawn_at` is available.
|
||||||
|
|
||||||
To support this the `#[monotonic]` attribute exists which is applied to a type alias definition.
|
The `#[monotonic]` attribute, applied to a type alias definition, exists to support this.
|
||||||
This type alias must point to a type which implements the [`rtic_monotonic::Monotonic`] trait.
|
This type alias must point to a type which implements the [`rtic_monotonic::Monotonic`] trait.
|
||||||
This is generally some timer which handles the timing of the system. One or more monotonics can be
|
This is generally some timer which handles the timing of the system.
|
||||||
used in the same system, for example a slow timer that is used to wake the system from sleep and another
|
One or more monotonics can coexist in the same system, for example a slow timer that wakes the
|
||||||
that is used for high granularity scheduling while the system is awake.
|
system from sleep and another which purpose is for fine grained scheduling while the
|
||||||
|
system is awake.
|
||||||
|
|
||||||
[`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic
|
[`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic
|
||||||
|
|
||||||
The attribute has one required parameter and two optional parameters, `binds`, `default` and
|
The attribute has one required parameter and two optional parameters, `binds`, `default` and
|
||||||
`priority` respectively. `binds = InterruptName` defines which interrupt vector is associated to
|
`priority` respectively.
|
||||||
the timer's interrupt, `default = true` enables a shorthand API when spawning and accessing the
|
The required parameter, `binds = InterruptName`, associates an interrupt vector to the timer's
|
||||||
time (`monotonics::now()` vs `monotonics::MyMono::now()`), and `priority` sets the priority the
|
interrupt, while `default = true` enables a shorthand API when spawning and accessing
|
||||||
interrupt vector has.
|
time (`monotonics::now()` vs `monotonics::MyMono::now()`), and `priority` sets the priority
|
||||||
|
of the interrupt vector.
|
||||||
|
|
||||||
> By default `priority` is set to the **maximum priority** of the system but a lower priority
|
> The default `priority` is the **maximum priority** of the system.
|
||||||
> can be selected if a high priority task cannot take the jitter introduced by the scheduling.
|
> If your system has a high priority task with tight scheduling requirements,
|
||||||
> This can however introduce jitter and delays into the scheduling, making it a trade-off.
|
> it might be desirable to demote the `monotonic` task to a lower priority
|
||||||
|
> to reduce scheduling jitter for the high priority task.
|
||||||
|
> This however might introduce jitter and delays into scheduling via the `monotonic`,
|
||||||
|
> making it a trade-off.
|
||||||
|
|
||||||
Finally, the monotonics must be initialized in `#[init]` and returned in the `init::Monotonic( ... )` tuple.
|
The monotonics are initialized in `#[init]` and returned within the `init::Monotonic( ... )` tuple.
|
||||||
This moves the monotonics into the active state which makes it possible to use them.
|
This activates the monotonics making it possible to use them.
|
||||||
|
|
||||||
An example is provided below:
|
See the following example:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../examples/schedule.rs}}
|
{{#include ../../../../examples/schedule.rs}}
|
||||||
|
@ -40,8 +45,8 @@ $ cargo run --target thumbv7m-none-eabi --example message
|
||||||
|
|
||||||
## Canceling or rescheduling a scheduled task
|
## Canceling or rescheduling a scheduled task
|
||||||
|
|
||||||
Tasks spawned using `task::spawn_after` and `task::spawn_at` has as returns a `SpawnHandle`,
|
Tasks spawned using `task::spawn_after` and `task::spawn_at` returns a `SpawnHandle`,
|
||||||
where the `SpawnHandle` can be used to cancel or reschedule a task that will run in the future.
|
which allows canceling or rescheduling of the task scheduled to run in the future.
|
||||||
If `cancel` or `reschedule_at`/`reschedule_after` returns an `Err` it means that the operation was
|
If `cancel` or `reschedule_at`/`reschedule_after` returns an `Err` it means that the operation was
|
||||||
too late and that the task is already sent for execution. The following example shows this in action:
|
too late and that the task is already sent for execution. The following example shows this in action:
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
# Resource usage
|
# Resource usage
|
||||||
|
|
||||||
The RTIC framework manages shared and task local resources which allows data to be persistently
|
The RTIC framework manages shared and task local resources allowing persistent data
|
||||||
stored and safely accessed without the use of unsafe code.
|
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
|
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.
|
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
|
Declaration of system-wide resources are by annotating **two** `struct`s within the `#[app]` module
|
||||||
the attribute `#[local]` and `#[shared]` respectively. Each field in these structures corresponds
|
with the attribute `#[local]` and `#[shared]`.
|
||||||
to a different resource (identified by field name). The difference between these two sets of
|
Each field in these structures corresponds to a different resource (identified by field name).
|
||||||
resources will be covered below.
|
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
|
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
|
using the `local` and `shared` arguments. Each argument takes a list of resource identifiers.
|
||||||
listed resources are made available to the context under the `local` and `shared` fields of the
|
The listed resources are made available to the context under the `local` and `shared` fields of the
|
||||||
`Context` structure.
|
`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
|
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).
|
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
|
commonly drivers or large objects, to be initialized in `#[init]` and then be passed to a specific
|
||||||
task.
|
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
|
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.
|
`#[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}}
|
{{#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
|
### Task local initialized resources
|
||||||
|
|
||||||
A special use-case of local resources are the ones specified directly in the resource claim,
|
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
|
`#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`, this allows for creating locals which do no need to be
|
||||||
initialized in `#[init]`.
|
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:
|
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
|
## Multi-lock
|
||||||
|
|
||||||
As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The
|
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
|
``` rust
|
||||||
{{#include ../../../../examples/multilock.rs}}
|
{{#include ../../../../examples/multilock.rs}}
|
||||||
|
@ -109,12 +109,12 @@ $ cargo run --target thumbv7m-none-eabi --example multilock
|
||||||
|
|
||||||
## Only shared (`&-`) access
|
## Only shared (`&-`) access
|
||||||
|
|
||||||
By default the framework assumes that all tasks require exclusive access (`&mut-`) to resources but
|
By default, the framework assumes that all tasks require exclusive access (`&mut-`) to resources,
|
||||||
it is possible to specify that a task only requires shared access (`&-`) to a resource using the
|
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.
|
`&resource_name` syntax in the `shared` list.
|
||||||
|
|
||||||
The advantage of specifying shared access (`&-`) to a resource is that no locks are required to
|
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,
|
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
|
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
|
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
|
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
|
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
|
`#[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
|
this is merely a convenience to reduce needless resource locking code, because even if the
|
||||||
**not** produce a critical section. Also worth noting: using `#[lock_free]` on resources shared by
|
`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`
|
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.
|
API would be a data race in that case.
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,31 @@
|
||||||
# Software tasks & spawn
|
# Software tasks & spawn
|
||||||
|
|
||||||
Software tasks, as hardware tasks, are run as interrupt handlers where all software tasks at the
|
Software tasks are tasks which are not directly assigned to a specific interrupt vector.
|
||||||
same priority shares a "free" interrupt handler to run from, called a dispatcher. These free
|
|
||||||
interrupts are interrupt vectors not used by hardware tasks.
|
|
||||||
|
|
||||||
To declare tasks in the framework the `#[task]` attribute is used on a function.
|
They run as interrupt handlers where all software tasks at the
|
||||||
By default these tasks are referred to as software tasks as they do not have a direct coupling to
|
same priority level shares a "free" interrupt handler acting as a dispatcher.
|
||||||
an interrupt handler. Software tasks can be spawned (started) using the `task_name::spawn()` static
|
Thus, what differentiates software and hardware tasks are the dispatcher versus
|
||||||
method which will directly run the task given that there are no higher priority tasks running.
|
bound interrupt vector.
|
||||||
|
|
||||||
To indicate to the framework which interrupts are free for use to dispatch software tasks with the
|
These free interrupts used as dispatchers are interrupt vectors not used by hardware tasks.
|
||||||
`#[app]` attribute has a `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` argument. You need
|
|
||||||
to provide as many dispatchers as there are priority levels used by software tasks, as an
|
|
||||||
dispatcher is assigned per interrupt level. The framework will also give a compile error if there
|
|
||||||
are not enough dispatchers provided.
|
|
||||||
|
|
||||||
This is exemplified in the following:
|
The `#[task]` attribute used on a function declare it as a software tasks.
|
||||||
|
The static method `task_name::spawn()` spawn (start) a software task and
|
||||||
|
given that there are no higher priority tasks running the task will start executing directly.
|
||||||
|
|
||||||
|
A list of “free” and usable interrupts allows the framework to dispatch software tasks.
|
||||||
|
This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an
|
||||||
|
argument to the `#[app]` attribute.
|
||||||
|
|
||||||
|
Each interrupt vector acting as dispatcher gets assigned to one priority level meaning that
|
||||||
|
the list of dispatchers need to cover all priority levels used by software tasks.
|
||||||
|
|
||||||
|
Example: The `dispatchers =` argument needs to have at least 3 entries for an application using
|
||||||
|
three different priorities for software tasks.
|
||||||
|
|
||||||
|
The framework will give a compilation error if there are not enough dispatchers provided.
|
||||||
|
|
||||||
|
See the following example:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../examples/spawn.rs}}
|
{{#include ../../../../examples/spawn.rs}}
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
# Starting a new project
|
# Starting a new project
|
||||||
|
|
||||||
When starting an RTIC project from scratch it is recommended to follow RTIC's [`defmt-app-template`].
|
A recommendation when starting a RTIC project from scratch is to follow RTIC's [`defmt-app-template`].
|
||||||
|
|
||||||
[`defmt-app-template`]: https://github.com/rtic-rs/defmt-app-template
|
[`defmt-app-template`]: https://github.com/rtic-rs/defmt-app-template
|
||||||
|
|
||||||
This will give you an RTIC application with support for RTT logging with [`defmt`] and stack overflow
|
This will give you an RTIC application with support for RTT logging with [`defmt`] and stack overflow
|
||||||
protection using [`flip-link`]. There are also an multitude of examples available provided by the community:
|
protection using [`flip-link`]. There are also a multitude of examples available provided by the community:
|
||||||
|
|
||||||
|
- [`rtic-examples`] - Multiple projects
|
||||||
- [https://github.com/kalkyl/f411-rtic](https://github.com/kalkyl/f411-rtic)
|
- [https://github.com/kalkyl/f411-rtic](https://github.com/kalkyl/f411-rtic)
|
||||||
- ... More to come
|
- ... More to come
|
||||||
|
|
||||||
[`defmt`]: https://github.com/knurling-rs/defmt/
|
[`defmt`]: https://github.com/knurling-rs/defmt/
|
||||||
[`flip-link`]: https://github.com/knurling-rs/flip-link/
|
[`flip-link`]: https://github.com/knurling-rs/flip-link/
|
||||||
|
[`rtic-examples`]: https://github.com/rtic-rs/rtic-examples
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# Resource de-structure-ing
|
# Resource de-structure-ing
|
||||||
|
|
||||||
When having a task taking multiple resources it can help in readability to split
|
Destructuring task resources might help readability if a task takes multiple
|
||||||
up the resource struct. Here are two examples on how this can be done:
|
resources.
|
||||||
|
Here are two examples on how to split up the resource struct:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../examples/destructure.rs}}
|
{{#include ../../../../examples/destructure.rs}}
|
||||||
|
|
|
@ -6,7 +6,7 @@ RTIC v0.4.0 was to allow inter-operation with other attributes. For example, the
|
||||||
improve performance in some cases.
|
improve performance in some cases.
|
||||||
|
|
||||||
> **IMPORTANT**: In general, the `link_section`, `export_name` and `no_mangle`
|
> **IMPORTANT**: In general, the `link_section`, `export_name` and `no_mangle`
|
||||||
> attributes are very powerful but also easy to misuse. Incorrectly using any of
|
> attributes are powerful but also easy to misuse. Incorrectly using any of
|
||||||
> these attributes can cause undefined behavior; you should always prefer to use
|
> these attributes can cause undefined behavior; you should always prefer to use
|
||||||
> safe, higher level attributes around them like `cortex-m-rt`'s `interrupt` and
|
> safe, higher level attributes around them like `cortex-m-rt`'s `interrupt` and
|
||||||
> `exception` attributes.
|
> `exception` attributes.
|
||||||
|
@ -42,4 +42,3 @@ $ cargo nm --example ramfunc --release | grep ' foo::'
|
||||||
$ cargo nm --example ramfunc --release | grep ' bar::'
|
$ cargo nm --example ramfunc --release | grep ' bar::'
|
||||||
{{#include ../../../../ci/expected/ramfunc.grep.bar}}
|
{{#include ../../../../ci/expected/ramfunc.grep.bar}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
Message passing always involves copying the payload from the sender into a
|
Message passing always involves copying the payload from the sender into a
|
||||||
static variable and then from the static variable into the receiver. Thus
|
static variable and then from the static variable into the receiver. Thus
|
||||||
sending a large buffer, like a `[u8; 128]`, as a message involves two expensive
|
sending a large buffer, like a `[u8; 128]`, as a message involves two expensive
|
||||||
`memcpy`s. To minimize the message passing overhead one can use indirection:
|
`memcpy`s.
|
||||||
|
|
||||||
|
Indirection can minimize message passing overhead:
|
||||||
instead of sending the buffer by value, one can send an owning pointer into the
|
instead of sending the buffer by value, one can send an owning pointer into the
|
||||||
buffer.
|
buffer.
|
||||||
|
|
||||||
|
@ -23,4 +25,3 @@ Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes.
|
||||||
$ cargo run --target thumbv7m-none-eabi --example pool
|
$ cargo run --target thumbv7m-none-eabi --example pool
|
||||||
{{#include ../../../../ci/expected/pool.run}}
|
{{#include ../../../../ci/expected/pool.run}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
# Implementing a `Monotonic` timer for scheduling
|
# Implementing a `Monotonic` timer for scheduling
|
||||||
|
|
||||||
The framework is very flexible in that it can utilize any timer which has compare-match and (optional)
|
The framework is flexible because it can use any timer which has compare-match and optionally
|
||||||
overflow interrupts for scheduling. The only thing needed to make a timer usable with RTIC is to
|
supporting overflow interrupts for scheduling.
|
||||||
implement the [`rtic_monotonic::Monotonic`] trait.
|
The single requirement to make a timer usable with RTIC is implementing the
|
||||||
|
[`rtic_monotonic::Monotonic`] trait.
|
||||||
|
|
||||||
Implementing time that supports a vast range is generally **very** difficult, and in RTIC 0.5 it was a
|
Implementing time counting that supports large time spans is generally **difficult**, in RTIC 0.5
|
||||||
common problem how to implement time handling and not get stuck in weird special cases. Moreover
|
implementing time handling was a common problem.
|
||||||
it was difficult to understand the relation between time and the timers used for scheduling. For
|
Moreover, the relation between time and timers used for scheduling was difficult to understand.
|
||||||
RTIC 0.6 we have moved to assume the user has a time library, e.g. [`fugit`] or [`embedded_time`],
|
|
||||||
as the basis for all time-based operations when implementing `Monotonic`. This is why in RTIC 0.6
|
|
||||||
it is almost trivial to implement the `Monotonic` trait and use any timer in a system for scheduling.
|
|
||||||
|
|
||||||
The trait documents the requirements for each method, however below you can find a list of
|
For RTIC 0.6 we instead assume the user has a time library, e.g. [`fugit`] or [`embedded_time`],
|
||||||
implementations in the wild that can be used as inspiration:
|
as the basis for all time-based operations when implementing `Monotonic`.
|
||||||
|
This makes it almost trivial to implement the `Monotonic` trait allowing the use of any timer in
|
||||||
|
the system for scheduling.
|
||||||
|
|
||||||
|
The trait documents the requirements for each method,
|
||||||
|
and for inspiration here is a list of `Monotonic` implementations:
|
||||||
|
|
||||||
- [`STM32F411 series`], implemented for the 32-bit timers
|
- [`STM32F411 series`], implemented for the 32-bit timers
|
||||||
- [`Nordic nRF52 series`], implemented for the 32-bit timers
|
- [`Nordic nRF52 series`], implemented for the 32-bit timers
|
||||||
|
@ -28,4 +31,3 @@ If you know of more implementations feel free to add them to this list.
|
||||||
[`Nordic nRF52 series`]: https://github.com/kalkyl/nrf-play/blob/main/src/bin/mono.rs
|
[`Nordic nRF52 series`]: https://github.com/kalkyl/nrf-play/blob/main/src/bin/mono.rs
|
||||||
[`Systick based`]: https://github.com/rtic-rs/systick-monotonic
|
[`Systick based`]: https://github.com/rtic-rs/systick-monotonic
|
||||||
[`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic
|
[`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
# 'static super-powers
|
# 'static super-powers
|
||||||
|
|
||||||
As discussed earlier `local` resources are given `'static` lifetime in `#[init]` and `#[idle]`,
|
In `#[init]` and `#[idle]` `local` resources has `'static` lifetime.
|
||||||
this can be used to allocate an object and then split it up or give the pre-allocated object to a
|
|
||||||
task, driver or some other object.
|
|
||||||
This is very useful when needing to allocate memory for drivers, such as USB drivers, and using
|
|
||||||
data structures that can be split such as [`heapless::spsc::Queue`].
|
|
||||||
|
|
||||||
In the following example an [`heapless::spsc::Queue`] is given to two different tasks for lock-free access
|
Useful when pre-allocating and/or splitting resources between tasks, drivers
|
||||||
to the shared queue.
|
or some other object.
|
||||||
|
This comes in handy when drivers, such as USB drivers, need to allocate memory and
|
||||||
|
when using splittable data structures such as [`heapless::spsc::Queue`].
|
||||||
|
|
||||||
|
In the following example two different tasks share a [`heapless::spsc::Queue`]
|
||||||
|
for lock-free access to the shared queue.
|
||||||
|
|
||||||
[`heapless::spsc::Queue`]: https://docs.rs/heapless/0.7.5/heapless/spsc/struct.Queue.html
|
[`heapless::spsc::Queue`]: https://docs.rs/heapless/0.7.5/heapless/spsc/struct.Queue.html
|
||||||
|
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../examples/static.rs}}
|
{{#include ../../../../examples/static.rs}}
|
||||||
```
|
```
|
||||||
|
|
|
@ -7,7 +7,7 @@ options:
|
||||||
You can inspect the file `rtic-expansion.rs` inside the `target` directory. This
|
You can inspect the file `rtic-expansion.rs` inside the `target` directory. This
|
||||||
file contains the expansion of the `#[rtic::app]` item (not your whole program!)
|
file contains the expansion of the `#[rtic::app]` item (not your whole program!)
|
||||||
of the *last built* (via `cargo build` or `cargo check`) RTIC application. The
|
of the *last built* (via `cargo build` or `cargo check`) RTIC application. The
|
||||||
expanded code is not pretty printed by default so you'll want to run `rustfmt`
|
expanded code is not pretty printed by default, so you'll want to run `rustfmt`
|
||||||
on it before you read it.
|
on it before you read it.
|
||||||
|
|
||||||
``` console
|
``` console
|
||||||
|
@ -15,7 +15,7 @@ $ cargo build --example foo
|
||||||
|
|
||||||
$ rustfmt target/rtic-expansion.rs
|
$ rustfmt target/rtic-expansion.rs
|
||||||
|
|
||||||
$ tail target/rtic-expansion.rs
|
tail target/rtic-expansion.rs
|
||||||
```
|
```
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
|
@ -43,6 +43,6 @@ crate and print the output to the console.
|
||||||
[`cargo-expand`]: https://crates.io/crates/cargo-expand
|
[`cargo-expand`]: https://crates.io/crates/cargo-expand
|
||||||
|
|
||||||
``` console
|
``` console
|
||||||
$ # produces the same output as before
|
# produces the same output as before
|
||||||
$ cargo expand --example smallest | tail
|
cargo expand --example smallest | tail
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Migration Guides
|
# Migration Guides
|
||||||
|
|
||||||
This section describes how to migrate between different version of RTIC.
|
This section describes how to migrate between different versions of RTIC.
|
||||||
It also acts as a comparing reference between versions.
|
It also acts as a comparing reference between versions.
|
||||||
|
|
|
@ -1,19 +1,31 @@
|
||||||
# Migrating from v0.4.x to v0.5.0
|
# Migrating from v0.4.x to v0.5.0
|
||||||
|
|
||||||
This section covers how to upgrade an application written against RTIC v0.4.x to
|
This section covers how to upgrade an application written against RTFM v0.4.x to
|
||||||
the version v0.5.0 of the framework.
|
the version v0.5.0 of the framework.
|
||||||
|
|
||||||
|
## Project name change RTFM -> RTIC
|
||||||
|
|
||||||
|
With release [v0.5.2][rtic0.5.2] the name was change to Real-Time Interrupt-driven Concurrency
|
||||||
|
|
||||||
|
All occurrences of `RTFM` needs to change to `RTIC`.
|
||||||
|
|
||||||
|
See [migration guide RTFM to RTIC](./migration_rtic.md)
|
||||||
|
|
||||||
|
[rtic0.5.2]: https://crates.io/crates/cortex-m-rtic/0.5.2
|
||||||
|
|
||||||
## `Cargo.toml`
|
## `Cargo.toml`
|
||||||
|
|
||||||
First, the version of the `cortex-m-rtic` dependency needs to be updated to
|
Change the version of `cortex-m-rtfm` to
|
||||||
`"0.5.0"`. The `timer-queue` feature needs to be removed.
|
`"0.5.0"`, change `rtfm` to `rtic`.
|
||||||
|
Remove the `timer-queue` feature.
|
||||||
|
|
||||||
``` toml
|
``` toml
|
||||||
[dependencies.cortex-m-rtic]
|
[dependencies.cortex-m-rtfm]
|
||||||
# change this
|
# change this
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
|
||||||
# into this
|
# into this
|
||||||
|
[dependencies.cortex-m-rtic]
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
|
||||||
# and remove this Cargo feature
|
# and remove this Cargo feature
|
||||||
|
@ -23,15 +35,15 @@ features = ["timer-queue"]
|
||||||
|
|
||||||
## `Context` argument
|
## `Context` argument
|
||||||
|
|
||||||
All functions inside the `#[rtic::app]` item need to take as first argument a
|
All functions inside the `#[rtfm::app]` item need to take as first argument a
|
||||||
`Context` structure. This `Context` type will contain the variables that were
|
`Context` structure. This `Context` type will contain the variables that were
|
||||||
magically injected into the scope of the function by version v0.4.x of the
|
magically injected into the scope of the function by version v0.4.x of the
|
||||||
framework: `resources`, `spawn`, `schedule` -- these variables will become
|
framework: `resources`, `spawn`, `schedule` -- these variables will become
|
||||||
fields of the `Context` structure. Each function within the `#[rtic::app]` item
|
fields of the `Context` structure. Each function within the `#[rtfm::app]` item
|
||||||
gets a different `Context` type.
|
gets a different `Context` type.
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
#[rtic::app(/* .. */)]
|
#[rtfm::app(/* .. */)]
|
||||||
const APP: () = {
|
const APP: () = {
|
||||||
// change this
|
// change this
|
||||||
#[task(resources = [x], spawn = [a], schedule = [b])]
|
#[task(resources = [x], spawn = [a], schedule = [b])]
|
||||||
|
@ -75,11 +87,11 @@ const APP: () = {
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
The syntax used to declare resources has been changed from `static mut`
|
The syntax used to declare resources has changed from `static mut`
|
||||||
variables to a `struct Resources`.
|
variables to a `struct Resources`.
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
#[rtic::app(/* .. */)]
|
#[rtfm::app(/* .. */)]
|
||||||
const APP: () = {
|
const APP: () = {
|
||||||
// change this
|
// change this
|
||||||
static mut X: u32 = 0;
|
static mut X: u32 = 0;
|
||||||
|
@ -101,13 +113,13 @@ const APP: () = {
|
||||||
|
|
||||||
If your application was accessing the device peripherals in `#[init]` through
|
If your application was accessing the device peripherals in `#[init]` through
|
||||||
the `device` variable then you'll need to add `peripherals = true` to the
|
the `device` variable then you'll need to add `peripherals = true` to the
|
||||||
`#[rtic::app]` attribute to continue to access the device peripherals through
|
`#[rtfm::app]` attribute to continue to access the device peripherals through
|
||||||
the `device` field of the `init::Context` structure.
|
the `device` field of the `init::Context` structure.
|
||||||
|
|
||||||
Change this:
|
Change this:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
#[rtic::app(/* .. */)]
|
#[rtfm::app(/* .. */)]
|
||||||
const APP: () = {
|
const APP: () = {
|
||||||
#[init]
|
#[init]
|
||||||
fn init() {
|
fn init() {
|
||||||
|
@ -121,7 +133,7 @@ const APP: () = {
|
||||||
Into this:
|
Into this:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
#[rtic::app(/* .. */, peripherals = true)]
|
#[rtfm::app(/* .. */, peripherals = true)]
|
||||||
// ^^^^^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^^^^^
|
||||||
const APP: () = {
|
const APP: () = {
|
||||||
#[init]
|
#[init]
|
||||||
|
@ -137,13 +149,14 @@ const APP: () = {
|
||||||
|
|
||||||
## `#[interrupt]` and `#[exception]`
|
## `#[interrupt]` and `#[exception]`
|
||||||
|
|
||||||
The `#[interrupt]` and `#[exception]` attributes have been removed. To declare
|
Remove the attributes `#[interrupt]` and `#[exception]`.
|
||||||
hardware tasks in v0.5.x use the `#[task]` attribute with the `binds` argument.
|
To declare hardware tasks in v0.5.x use the `#[task]`
|
||||||
|
attribute with the `binds` argument instead.
|
||||||
|
|
||||||
Change this:
|
Change this:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
#[rtic::app(/* .. */)]
|
#[rtfm::app(/* .. */)]
|
||||||
const APP: () = {
|
const APP: () = {
|
||||||
// hardware tasks
|
// hardware tasks
|
||||||
#[exception]
|
#[exception]
|
||||||
|
@ -163,7 +176,7 @@ const APP: () = {
|
||||||
Into this:
|
Into this:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
#[rtic::app(/* .. */)]
|
#[rtfm::app(/* .. */)]
|
||||||
const APP: () = {
|
const APP: () = {
|
||||||
#[task(binds = SVCall)]
|
#[task(binds = SVCall)]
|
||||||
// ^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^
|
||||||
|
@ -183,25 +196,26 @@ const APP: () = {
|
||||||
|
|
||||||
## `schedule`
|
## `schedule`
|
||||||
|
|
||||||
The `schedule` API no longer requires the `timer-queue` cargo feature, which has
|
The `schedule` API no longer requires the `timer-queue` cargo feature.
|
||||||
been removed. To use the `schedule` API one must
|
To use the `schedule` API one must first define the monotonic timer the
|
||||||
first define the monotonic timer the runtime will use using the `monotonic`
|
runtime will use using the `monotonic` argument of the `#[rtfm::app]` attribute.
|
||||||
argument of the `#[rtic::app]` attribute. To continue using the cycle counter
|
To continue using the cycle counter (CYCCNT) as the monotonic timer,
|
||||||
(CYCCNT) as the monotonic timer, and match the behavior of version v0.4.x, add
|
and match the behavior of version v0.4.x, add the `monotonic = rtfm::cyccnt::CYCCNT`
|
||||||
the `monotonic = rtic::cyccnt::CYCCNT` argument to the `#[rtic::app]` attribute.
|
argument to the `#[rtfm::app]` attribute.
|
||||||
|
|
||||||
Also, the `Duration` and `Instant` types and the `U32Ext` trait have been moved
|
Also, the `Duration` and `Instant` types and the `U32Ext` trait moved
|
||||||
into the `rtic::cyccnt` module. This module is only available on ARMv7-M+
|
into the `rtfm::cyccnt` module.
|
||||||
devices. The removal of the `timer-queue` also brings back the `DWT` peripheral
|
This module is only available on ARMv7-M+ devices.
|
||||||
inside the core peripherals struct, this will need to be enabled by the application
|
The removal of the `timer-queue` also brings back the `DWT` peripheral
|
||||||
inside `init`.
|
inside the core peripherals struct, if `DWT` is required,
|
||||||
|
ensure it is enabled by the application inside `init`.
|
||||||
|
|
||||||
Change this:
|
Change this:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
use rtic::{Duration, Instant, U32Ext};
|
use rtfm::{Duration, Instant, U32Ext};
|
||||||
|
|
||||||
#[rtic::app(/* .. */)]
|
#[rtfm::app(/* .. */)]
|
||||||
const APP: () = {
|
const APP: () = {
|
||||||
#[task(schedule = [b])]
|
#[task(schedule = [b])]
|
||||||
fn a() {
|
fn a() {
|
||||||
|
@ -213,10 +227,10 @@ const APP: () = {
|
||||||
Into this:
|
Into this:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
use rtic::cyccnt::{Duration, Instant, U32Ext};
|
use rtfm::cyccnt::{Duration, Instant, U32Ext};
|
||||||
// ^^^^^^^^
|
// ^^^^^^^^
|
||||||
|
|
||||||
#[rtic::app(/* .. */, monotonic = rtic::cyccnt::CYCCNT)]
|
#[rtfm::app(/* .. */, monotonic = rtfm::cyccnt::CYCCNT)]
|
||||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
const APP: () = {
|
const APP: () = {
|
||||||
#[init]
|
#[init]
|
||||||
|
|
|
@ -71,7 +71,7 @@ mod app {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Move Dispatchers from `extern "C"` to app arguments.
|
## Move Dispatchers from `extern "C"` to app arguments
|
||||||
|
|
||||||
Change
|
Change
|
||||||
|
|
||||||
|
@ -171,7 +171,10 @@ fn b(_: b::Context) {}
|
||||||
|
|
||||||
## Symmetric locks
|
## Symmetric locks
|
||||||
|
|
||||||
Now RTIC utilizes symmetric locks, this means that the `lock` method need to be used for all `shared` resource access. In old code one could do the following as the high priority task has exclusive access to the resource:
|
Now RTIC utilizes symmetric locks, this means that the `lock` method need
|
||||||
|
to be used for all `shared` resource access.
|
||||||
|
In old code one could do the following as the high priority
|
||||||
|
task has exclusive access to the resource:
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
#[task(priority = 2, resources = [r])]
|
#[task(priority = 2, resources = [r])]
|
||||||
|
@ -354,6 +357,7 @@ Note that the attributes `spawn` and `schedule` are no longer needed.
|
||||||
|
|
||||||
### Extern tasks
|
### Extern tasks
|
||||||
|
|
||||||
Both software and hardware tasks can now be defined external to the `mod app`. Previously this was possible only by implementing a trampoline calling out the task implementation.
|
Both software and hardware tasks can now be defined external to the `mod app`.
|
||||||
|
Previously this was possible only by implementing a trampoline calling out the task implementation.
|
||||||
|
|
||||||
See examples `examples/extern_binds.rs` and `examples/extern_spawn.rs`.
|
See examples `examples/extern_binds.rs` and `examples/extern_spawn.rs`.
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
# Preface
|
# Preface
|
||||||
|
|
||||||
This book contains user level documentation for the Real-Time Interrupt-driven Concurrency
|
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 is available [here](../../api/).
|
||||||
|
|
||||||
Formerly known as Real-Time For the Masses.
|
Formerly known as Real-Time For the Masses.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue