diff options
Diffstat (limited to 'book/en/src/by-example/tips.md')
-rw-r--r-- | book/en/src/by-example/tips.md | 141 |
1 files changed, 114 insertions, 27 deletions
diff --git a/book/en/src/by-example/tips.md b/book/en/src/by-example/tips.md index c0bfc56e..d8264c90 100644 --- a/book/en/src/by-example/tips.md +++ b/book/en/src/by-example/tips.md @@ -2,10 +2,21 @@ ## Generics -Resources shared between two or more tasks implement the `Mutex` trait in *all* -contexts, even on those where a critical section is not required to access the -data. This lets you easily write generic code that operates on resources and can -be called from different tasks. Here's one such example: +Resources may appear in contexts as resource proxies or as unique references +(`&mut-`) depending on the priority of the task. Because the same resource may +appear as *different* types in different contexts one cannot refactor a common +operation that uses resources into a plain function; however, such refactor is +possible using *generics*. + +All resource proxies implement the `rtic::Mutex` trait. On the other hand, +unique references (`&mut-`) do *not* implement this trait (due to limitations in +the trait system) but one can wrap these references in the [`rtic::Exclusive`] +newtype which does implement the `Mutex` trait. With the help of this newtype +one can write a generic function that operates on generic resources and call it +from different tasks to perform some operation on the same set of resources. +Here's one such example: + +[`rtic::Exclusive`]: ../../../api/rtic/struct.Exclusive.html ``` rust {{#include ../../../../examples/generics.rs}} @@ -13,19 +24,18 @@ be called from different tasks. Here's one such example: ``` console $ cargo run --example generics -{{#include ../../../../ci/expected/generics.run}}``` +{{#include ../../../../ci/expected/generics.run}} +``` -This also lets you change the static priorities of tasks without having to -rewrite code. If you consistently use `lock`s to access the data behind shared -resources then your code will continue to compile when you change the priority -of tasks. +Using generics also lets you change the static priorities of tasks during +development without having to rewrite a bunch code every time. ## Conditional compilation -You can use conditional compilation (`#[cfg]`) on resources (`static [mut]` -items) and tasks (`fn` items). The effect of using `#[cfg]` attributes is that -the resource / task will *not* be injected into the prelude of tasks that use -them (see `resources`, `spawn` and `schedule`) if the condition doesn't hold. +You can use conditional compilation (`#[cfg]`) on resources (the fields of +`struct Resources`) and tasks (the `fn` items). The effect of using `#[cfg]` +attributes is that the resource / task will *not* be available through the +corresponding `Context` `struct` if the condition doesn't hold. The example below logs a message whenever the `foo` task is spawned, but only if the program has been compiled using the `dev` profile. @@ -34,10 +44,17 @@ the program has been compiled using the `dev` profile. {{#include ../../../../examples/cfg.rs}} ``` +``` console +$ cargo run --example cfg --release + +$ cargo run --example cfg +{{#include ../../../../ci/expected/cfg.run}} +``` + ## Running tasks from RAM -The main goal of moving the specification of RTFM applications to attributes in -RTFM v0.4.x was to allow inter-operation with other attributes. For example, the +The main goal of moving the specification of RTIC applications to attributes in +RTIC v0.4.0 was to allow inter-operation with other attributes. For example, the `link_section` attribute can be applied to tasks to place them in RAM; this can improve performance in some cases. @@ -63,31 +80,101 @@ Running this program produces the expected output. ``` console $ cargo run --example ramfunc -{{#include ../../../../ci/expected/ramfunc.run}}``` +{{#include ../../../../ci/expected/ramfunc.run}} +``` One can look at the output of `cargo-nm` to confirm that `bar` ended in RAM (`0x2000_0000`), whereas `foo` ended in Flash (`0x0000_0000`). ``` console $ cargo nm --example ramfunc --release | grep ' foo::' -{{#include ../../../../ci/expected/ramfunc.grep.foo}}``` +{{#include ../../../../ci/expected/ramfunc.grep.foo}} +``` ``` console $ cargo nm --example ramfunc --release | grep ' bar::' -{{#include ../../../../ci/expected/ramfunc.grep.bar}}``` +{{#include ../../../../ci/expected/ramfunc.grep.bar}} +``` + +## Indirection for faster message passing + +Message passing always involves copying the payload from the sender into a +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 +`memcpy`s. To minimize the message passing overhead one can use indirection: +instead of sending the buffer by value, one can send an owning pointer into the +buffer. + +One can use a global allocator to achieve indirection (`alloc::Box`, +`alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.37.0, +or one can use a statically allocated memory pool like [`heapless::Pool`]. + +[`heapless::Pool`]: https://docs.rs/heapless/0.5.0/heapless/pool/index.html + +Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes. + +``` rust +{{#include ../../../../examples/pool.rs}} +``` +``` console +$ cargo run --example pool +{{#include ../../../../ci/expected/pool.run}} +``` + +## Inspecting the expanded code + +`#[rtic::app]` is a procedural macro that produces support code. If for some +reason you need to inspect the code generated by this macro you have two +options: -## `binds` +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!) +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` +over it before you read it. -**NOTE**: Requires RTFM ~0.4.2 +``` console +$ cargo build --example foo + +$ rustfmt target/rtic-expansion.rs -You can give hardware tasks more task-like names using the `binds` argument: you -name the function as you wish and specify the name of the interrupt / exception -in the `binds` argument. Types like `Spawn` will be placed in a module named -after the function, not the interrupt / exception. Example below: +$ tail target/rtic-expansion.rs +``` ``` rust -{{#include ../../../../examples/binds.rs}} +#[doc = r" Implementation details"] +mod app { + #[doc = r" Always include the device crate which contains the vector table"] + use lm3s6965 as _; + #[no_mangle] + unsafe extern "C" fn main() -> ! { + rtic::export::interrupt::disable(); + let mut core: rtic::export::Peripherals = core::mem::transmute(()); + core.SCB.scr.modify(|r| r | 1 << 1); + rtic::export::interrupt::enable(); + loop { + rtic::export::wfi() + } + } +} ``` + +Or, you can use the [`cargo-expand`] sub-command. This sub-command will expand +*all* the macros, including the `#[rtic::app]` attribute, and modules in your +crate and print the output to the console. + +[`cargo-expand`]: https://crates.io/crates/cargo-expand + ``` console -$ cargo run --example binds -{{#include ../../../../ci/expected/binds.run}}``` +$ # produces the same output as before +$ cargo expand --example smallest | tail +``` + +## Resource de-structure-ing + +When having a task taking multiple resources it can help in readability to split +up the resource struct. Here are two examples on how this can be done: + +``` rust +{{#include ../../../../examples/destructure.rs}} +``` |