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