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

Migrating from v0.4.x to v0.5.0

This section covers how to upgrade an application written against RTFM v0.4.x to the version v0.5.0 of the framework.

Project name change RTFM -> RTIC

With release v0.5.2 the name was change to Real-Time Interrupt-driven Concurrency

All occurrences of RTFM needs to change to RTIC.

See migration guide RTFM to RTIC

Cargo.toml

Change the version of cortex-m-rtfm to "0.5.0", change rtfm to rtic. Remove the timer-queue feature.

[dependencies.cortex-m-rtfm]
# change this
version = "0.4.3"

# into this
[dependencies.cortex-m-rtic]
version = "0.5.0"

# and remove this Cargo feature
features = ["timer-queue"]
#           ^^^^^^^^^^^^^

Context argument

All functions inside the #[rtfm::app] item need to take as first argument a Context structure. This Context type will contain the variables that were magically injected into the scope of the function by version v0.4.x of the framework: resources, spawn, schedule -- these variables will become fields of the Context structure. Each function within the #[rtfm::app] item gets a different Context type.

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */)]
const APP: () = {
    // change this
    #[task(resources = [x], spawn = [a], schedule = [b])]
    fn foo() {
        resources.x.lock(|x| /* .. */);
        spawn.a(message);
        schedule.b(baseline);
    }

    // into this
    #[task(resources = [x], spawn = [a], schedule = [b])]
    fn foo(mut cx: foo::Context) {
        // ^^^^^^^^^^^^^^^^^^^^

        cx.resources.x.lock(|x| /* .. */);
    //  ^^^

        cx.spawn.a(message);
    //  ^^^

        cx.schedule.b(message, baseline);
    //  ^^^
    }

    // change this
    #[init]
    fn init() {
        // ..
    }

    // into this
    #[init]
    fn init(cx: init::Context) {
        //  ^^^^^^^^^^^^^^^^^
        // ..
    }

    // ..
};
}

Resources

The syntax used to declare resources has changed from static mut variables to a struct Resources.

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */)]
const APP: () = {
    // change this
    static mut X: u32 = 0;
    static mut Y: u32 = (); // late resource

    // into this
    struct Resources {
        #[init(0)] // <- initial value
        X: u32, // NOTE: we suggest changing the naming style to `snake_case`

        Y: u32, // late resource
    }

    // ..
};
}

Device peripherals

If your application was accessing the device peripherals in #[init] through the device variable then you'll need to add peripherals = true to the #[rtfm::app] attribute to continue to access the device peripherals through the device field of the init::Context structure.

Change this:

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */)]
const APP: () = {
    #[init]
    fn init() {
        device.SOME_PERIPHERAL.write(something);
    }

    // ..
};
}

Into this:

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */, peripherals = true)]
//                    ^^^^^^^^^^^^^^^^^^
const APP: () = {
    #[init]
    fn init(cx: init::Context) {
        //  ^^^^^^^^^^^^^^^^^
        cx.device.SOME_PERIPHERAL.write(something);
    //  ^^^
    }

    // ..
};
}

#[interrupt] and #[exception]

Remove the attributes #[interrupt] and #[exception]. To declare hardware tasks in v0.5.x use the #[task] attribute with the binds argument instead.

Change this:

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */)]
const APP: () = {
    // hardware tasks
    #[exception]
    fn SVCall() { /* .. */ }

    #[interrupt]
    fn UART0() { /* .. */ }

    // software task
    #[task]
    fn foo() { /* .. */ }

    // ..
};
}

Into this:

#![allow(unused)]
fn main() {
#[rtfm::app(/* .. */)]
const APP: () = {
    #[task(binds = SVCall)]
    //     ^^^^^^^^^^^^^^
    fn svcall(cx: svcall::Context) { /* .. */ }
    // ^^^^^^ we suggest you use a `snake_case` name here

    #[task(binds = UART0)]
    //     ^^^^^^^^^^^^^
    fn uart0(cx: uart0::Context) { /* .. */ }

    #[task]
    fn foo(cx: foo::Context) { /* .. */ }

    // ..
};
}

schedule

The schedule API no longer requires the timer-queue cargo feature. To use the schedule API one must first define the monotonic timer the runtime will use using the monotonic argument of the #[rtfm::app] attribute. To continue using the cycle counter (CYCCNT) as the monotonic timer, and match the behavior of version v0.4.x, add the monotonic = rtfm::cyccnt::CYCCNT argument to the #[rtfm::app] attribute.

Also, the Duration and Instant types and the U32Ext trait moved into the rtfm::cyccnt module. This module is only available on ARMv7-M+ devices. The removal of the timer-queue also brings back the DWT peripheral inside the core peripherals struct, if DWT is required, ensure it is enabled by the application inside init.

Change this:

#![allow(unused)]
fn main() {
use rtfm::{Duration, Instant, U32Ext};

#[rtfm::app(/* .. */)]
const APP: () = {
    #[task(schedule = [b])]
    fn a() {
        // ..
    }
};
}

Into this:

#![allow(unused)]
fn main() {
use rtfm::cyccnt::{Duration, Instant, U32Ext};
//        ^^^^^^^^

#[rtfm::app(/* .. */, monotonic = rtfm::cyccnt::CYCCNT)]
//                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
const APP: () = {
    #[init]
    fn init(cx: init::Context) {
        cx.core.DWT.enable_cycle_counter();
        // optional, configure the DWT run without a debugger connected
        cx.core.DCB.enable_trace();
    }
    #[task(schedule = [b])]
    fn a(cx: a::Context) {
        // ..
    }
};
}