バックグラウンドタスク #[idle]
idle 属性でマークされた関数は、モジュール内に任意で記述できます。これは特別な idle タスク となり、シグネチャは fn(idle::Context) -> ! でなければなりません。
存在する場合、ランタイムは init の後に idle タスクを実行します。init とは異なり、idle は 割り込みが有効な状態で 実行され、-> ! の関数シグネチャが示すとおり、決してリターンしてはいけません。
Rust の型 ! は「never」を意味します。
init と同様に、ローカルに宣言されたリソースは安全にアクセスできる 'static ライフタイムを持ちます。
以下の例は、idle が 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
デフォルトでは、RTIC の idle タスクは特定のターゲット向けの最適化を行いません。
一般的で有用な最適化の 1 つは、SLEEPONEXIT を有効にして、idle に到達したときに MCU がスリープに入れるようにすることです。
注意: 一部のハードウェアでは、設定しない限り、スリープモード中にデバッグユニットが無効になります。
これは RTIC の範囲外であるため、ハードウェア固有のドキュメントを参照してください。
次の例は、
SLEEPONEXIT を設定し、デフォルトの nop() を wfi() に置き換えるカスタム idle タスクを提供することで、スリープを有効にする方法を示しています。
//! 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/documentation/100737/0100/Power-management/Sleep-mode/Sleep-on-exit-bit
cx.core.SCB.set_sleeponexit();
(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
注意:
idleタスクは、優先度 0 で動作する ソフトウェア タスクと一緒には使用できません。理由は、idleが優先度 0 でリターンしない Rust 関数として実行されるためです。そのため、優先度 0 のエグゼキュータが同じ優先度の ソフトウェア タスクに制御を渡す方法がありません。