The background task #[idle]
A function marked with the idle
attribute can optionally appear in the
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 must never return,
as the -> !
function signature indicates.
The Rust type !
means “never”.
Like in init
, locally declared resources will have 'static
lifetimes that
are safe to access.
The example below shows that idle
runs after init
.
#![allow(unused)] fn main() { //! examples/idle.rs #![deny(unsafe_code)] #![deny(warnings)] #![deny(missing_docs)] #![no_main] #![no_std] use panic_semihosting as _; #[rtic::app(device = lm3s6965)] mod app { use cortex_m_semihosting::{debug, hprintln}; #[shared] struct Shared {} #[local] struct Local {} #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { hprintln!("init"); (Shared {}, Local {}, init::Monotonics()) } #[idle(local = [x: u32 = 0])] fn idle(cx: idle::Context) -> ! { // Locals in idle have lifetime 'static let _x: &'static mut u32 = cx.local.x; hprintln!("idle"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator loop { cortex_m::asm::nop(); } } } }
$ cargo run --target thumbv7m-none-eabi --example idle
init
idle
By default, the RTIC idle
task does not try to optimize for any specific targets.
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.
Consult your hardware specific documentation as this is outside the scope of RTIC.
The following example shows how to enable sleep by setting the
SLEEPONEXIT
and providing a custom idle
task replacing the
default nop()
with wfi()
.
#![allow(unused)] fn main() { //! examples/idle-wfi.rs #![deny(unsafe_code)] #![deny(warnings)] #![deny(missing_docs)] #![no_main] #![no_std] use panic_semihosting as _; #[rtic::app(device = lm3s6965)] mod app { use cortex_m_semihosting::{debug, hprintln}; #[shared] struct Shared {} #[local] struct Local {} #[init] fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics) { hprintln!("init"); // Set the ARM SLEEPONEXIT bit to go to sleep after handling interrupts // See https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit cx.core.SCB.set_sleepdeep(); (Shared {}, Local {}, init::Monotonics()) } #[idle(local = [x: u32 = 0])] fn idle(cx: idle::Context) -> ! { // Locals in idle have lifetime 'static let _x: &'static mut u32 = cx.local.x; hprintln!("idle"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator loop { // Now Wait For Interrupt is used instead of a busy-wait loop // to allow MCU to sleep between interrupts // https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/WFI rtic::export::wfi() } } } }
$ cargo run --target thumbv7m-none-eabi --example idle-wfi
init
idle