Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

バックグラウンドタスク #[idle]

idle 属性でマークされた関数は、モジュール内に任意で記述できます。これは特別な idle タスク となり、シグネチャは fn(idle::Context) -> ! でなければなりません。

存在する場合、ランタイムは init の後に idle タスクを実行します。init とは異なり、idle割り込みが有効な状態で 実行され、-> ! の関数シグネチャが示すとおり、決してリターンしてはいけません。 Rust の型 ! は「never」を意味します

init と同様に、ローカルに宣言されたリソースは安全にアクセスできる 'static ライフタイムを持ちます。

以下の例は、idleinit の後に実行されることを示しています。

//! 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 のエグゼキュータが同じ優先度の ソフトウェア タスクに制御を渡す方法がありません。