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.
Cargo.toml
First, the version of the cortex-m-rtfm dependency needs to be updated to
"0.5.0". The timer-queue feature needs to be removed.
[dependencies.cortex-m-rtfm]
# change this
version = "0.4.3"
# into this
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_variables)] #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 been changed from static mut
variables to a struct Resources.
# #![allow(unused_variables)] #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_variables)] #fn main() { #[rtfm::app(/* .. */)] const APP: () = { #[init] fn init() { device.SOME_PERIPHERAL.write(something); } // .. }; #}
Into this:
# #![allow(unused_variables)] #fn main() { #[rtfm::app(/* .. */, peripherals = true)] // ^^^^^^^^^^^^^^^^^^ const APP: () = { #[init] fn init(cx: init::Context) { // ^^^^^^^^^^^^^^^^^ cx.device.SOME_PERIPHERAL.write(something); // ^^^ } // .. }; #}
#[interrupt] and #[exception]
The #[interrupt] and #[exception] attributes have been removed. To declare
hardware tasks in v0.5.x use the #[task] attribute with the binds argument.
Change this:
# #![allow(unused_variables)] #fn main() { #[rtfm::app(/* .. */)] const APP: () = { // hardware tasks #[exception] fn SVCall() { /* .. */ } #[interrupt] fn UART0() { /* .. */ } // software task #[task] fn foo() { /* .. */ } // .. }; #}
Into this:
# #![allow(unused_variables)] #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 timer-queue feature has been removed. 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 have been 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, this will need to be enabled by the application
inside init.
Change this:
# #![allow(unused_variables)] #fn main() { use rtfm::{Duration, Instant, U32Ext}; #[rtfm::app(/* .. */)] const APP: () = { #[task(schedule = [b])] fn a() { // .. } }; #}
Into this:
# #![allow(unused_variables)] #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) { // .. } }; #}