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
.
//! examples/idle.rs
#![no_main]
#![no_std]
#![deny(warnings)]
#![deny(unsafe_code)]
#![deny(missing_docs)]
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) {
hprintln!("init");
(Shared {}, Local {})
}
#[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 xtask qemu --verbose --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()
.
//! examples/idle-wfi.rs
#![no_main]
#![no_std]
#![deny(warnings)]
#![deny(unsafe_code)]
#![deny(missing_docs)]
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) {
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 {})
}
#[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 xtask qemu --verbose --example idle-wfi
init
idle
Notice: The
idle
task cannot be used together with software tasks running at priority zero. The reason is thatidle
is running as a non-returning Rust function at priority zero. Thus there would be no way for an executor at priority zero to give control to software tasks at the same priority.