## 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

# Hardware tasks

At its core RTIC is using a hardware interrupt controller (ARM NVIC on cortex-m) to schedule and start execution of tasks. All tasks except `pre-init`, `#[init]` and `#[idle]` run as interrupt handlers.

Hardware tasks are explicitly bound to interrupt handlers.

To bind a task to an interrupt, use the `#[task]` attribute argument `binds = InterruptName`. This task then becomes the interrupt handler for this hardware interrupt vector.

All tasks bound to an explicit interrupt are called hardware tasks since they start execution in reaction to a hardware event.

Specifying a non-existing interrupt name will cause a compilation error. The interrupt names are commonly defined by PAC or HAL crates.

Any available interrupt vector should work. Specific devices may bind specific interrupt priorities to specific interrupt vectors outside user code control. See for example the nRF “softdevice”.

Beware of using interrupt vectors that are used internally by hardware features; RTIC is unaware of such hardware specific details.

The example below demonstrates the use of the `#[task(binds = InterruptName)]` attribute to declare a hardware task bound to an interrupt handler.

    #![allow(unused)]
    fn main() {
    //! examples/hardware.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};
        use lm3s6965::Interrupt;

        #[shared]
        struct Shared {}

        #[local]
        struct Local {}

        #[init]
        fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
            // Pends the UART0 interrupt but its handler won't run until *after*
            // `init` returns because interrupts are disabled
            rtic::pend(Interrupt::UART0); // equivalent to NVIC::pend

            hprintln!("init");

            (Shared {}, Local {}, init::Monotonics())
        }

        #[idle]
        fn idle(_: idle::Context) -> ! {
            // interrupts are enabled again; the `UART0` handler runs at this point

            hprintln!("idle");

            rtic::pend(Interrupt::UART0);

            loop {
                // Exit moved after nop to ensure that rtic::pend gets
                // to run before exiting
                cortex_m::asm::nop();
                debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
            }
        }

        #[task(binds = UART0, local = [times: u32 = 0])]
        fn uart0(cx: uart0::Context) {
            // Safe access to local `static mut` variable
            *cx.local.times += 1;

            hprintln!(
                "UART0 called {} time{}",
                *cx.local.times,
                if *cx.local.times > 1 { "s" } else { "" }
            );
        }
    }
    }

    $ cargo run --target thumbv7m-none-eabi --example hardware
    init
    UART0 called 1 time
    idle
    UART0 called 2 times