diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/full-syntax.rs | 81 | ||||
-rw-r--r-- | examples/generics.rs | 69 | ||||
-rw-r--r-- | examples/nested.rs | 125 | ||||
-rw-r--r-- | examples/one-task.rs | 91 | ||||
-rw-r--r-- | examples/preemption.rs | 70 | ||||
-rw-r--r-- | examples/two-tasks.rs | 75 | ||||
-rw-r--r-- | examples/zero-tasks.rs | 49 |
7 files changed, 560 insertions, 0 deletions
diff --git a/examples/full-syntax.rs b/examples/full-syntax.rs new file mode 100644 index 00000000..d1466781 --- /dev/null +++ b/examples/full-syntax.rs @@ -0,0 +1,81 @@ +//! A showcase of the `app!` macro syntax + +#![deny(unsafe_code)] +#![feature(const_fn)] +#![feature(proc_macro)] +#![no_std] + +#[macro_use(task)] +extern crate cortex_m_rtfm as rtfm; +extern crate stm32f103xx; + +use rtfm::{app, Resource, Threshold}; + +app! { + device: stm32f103xx, + + resources: { + static CO_OWNED: u32 = 0; + static OWNED: bool = false; + static SHARED: bool = false; + }, + + init: { + path: init_, // this is a path to the "init" function + }, + + idle: { + locals: { + static COUNTER: u32 = 0; + }, + path: idle_, // this is a path to the "idle" function + resources: [OWNED, SHARED], + }, + + tasks: { + SYS_TICK: { + priority: 1, + resources: [CO_OWNED, SHARED], + }, + + TIM2: { + enabled: true, + priority: 1, + resources: [CO_OWNED], + }, + }, +} + +fn init_(_p: init::Peripherals, _r: init::Resources) {} + +fn idle_(t: &mut Threshold, l: &mut idle::Locals, mut r: idle::Resources) -> ! { + loop { + *l.COUNTER += 1; + + **r.OWNED != **r.OWNED; + + if **r.OWNED { + if r.SHARED.claim(t, |shared, _| **shared) { + rtfm::wfi(); + } + } else { + r.SHARED.claim_mut(t, |shared, _| **shared = !**shared); + } + } +} + +task!(SYS_TICK, sys_tick, Local { + static STATE: bool = true; +}); + +fn sys_tick(_t: &mut Threshold, l: &mut Local, r: SYS_TICK::Resources) { + *l.STATE = !*l.STATE; + + **r.CO_OWNED += 1; +} + +task!(TIM2, tim2); + +fn tim2(_t: &mut Threshold, r: TIM2::Resources) { + **r.CO_OWNED += 1; +} diff --git a/examples/generics.rs b/examples/generics.rs new file mode 100644 index 00000000..335d159b --- /dev/null +++ b/examples/generics.rs @@ -0,0 +1,69 @@ +//! Working with resources in a generic fashion + +#![deny(unsafe_code)] +#![feature(proc_macro)] +#![no_std] + +#[macro_use(task)] +extern crate cortex_m_rtfm as rtfm; +extern crate stm32f103xx; + +use rtfm::{app, Resource, Threshold}; +use stm32f103xx::{SPI1, GPIOA}; + +app! { + device: stm32f103xx, + + tasks: { + EXTI0: { + enabled: true, + priority: 1, + resources: [GPIOA, SPI1], + }, + + EXTI1: { + enabled: true, + priority: 2, + resources: [GPIOA, SPI1], + }, + }, +} + +fn init(_p: init::Peripherals) {} + +fn idle() -> ! { + loop { + rtfm::wfi(); + } +} + +// a generic function to use resources in any task (regardless of its priority) +fn work<G, S>(t: &mut Threshold, gpioa: &G, spi1: &S) +where + G: Resource<Data = GPIOA>, + S: Resource<Data = SPI1>, +{ + gpioa.claim(t, |_gpioa, t| { + // drive NSS low + + spi1.claim(t, |_spi1, _| { + // transfer data + }); + + // drive NSS high + }); +} + +task!(EXTI0, exti0); + +// this task needs critical sections to access the resources +fn exti0(t: &mut Threshold, r: EXTI0::Resources) { + work(t, &r.GPIOA, &r.SPI1); +} + +task!(EXTI1, exti1); + +// this task has direct access to the resources +fn exti1(t: &mut Threshold, r: EXTI1::Resources) { + work(t, r.GPIOA, r.SPI1); +} diff --git a/examples/nested.rs b/examples/nested.rs new file mode 100644 index 00000000..d307634c --- /dev/null +++ b/examples/nested.rs @@ -0,0 +1,125 @@ +//! Nesting claims and how the preemption threshold works +//! +//! If you run this program you'll hit the breakpoints as indicated by the +//! letters in the comments: A, then B, then C, etc. + +#![deny(unsafe_code)] +#![feature(const_fn)] +#![feature(proc_macro)] +#![no_std] + +#[macro_use(task)] +extern crate cortex_m_rtfm as rtfm; +extern crate stm32f103xx; + +use stm32f103xx::Interrupt; +use rtfm::{app, Resource, Threshold}; + +app! { + device: stm32f103xx, + + resources: { + static LOW: u64 = 0; + static HIGH: u64 = 0; + }, + + tasks: { + EXTI0: { + enabled: true, + priority: 1, + resources: [LOW, HIGH], + }, + + EXTI1: { + enabled: true, + priority: 2, + resources: [LOW], + }, + + EXTI2: { + enabled: true, + priority: 3, + resources: [HIGH], + }, + }, +} + +fn init(_p: init::Peripherals, _r: init::Resources) {} + +fn idle() -> ! { + // sets task `exti0` as pending + // + // because `exti0` has higher priority than `idle` it will be executed + // immediately + rtfm::set_pending(Interrupt::EXTI0); // ~> exti0 + + loop { + rtfm::wfi(); + } +} + +task!(EXTI0, exti0); + +fn exti0(t: &mut Threshold, r: EXTI0::Resources) { + // because this task has a priority of 1 the preemption threshold is also 1 + + // A + rtfm::bkpt(); + + // because `exti1` has higher priority than `exti0` it can preempt it + rtfm::set_pending(Interrupt::EXTI1); // ~> exti1 + + // a claim creates a critical section + r.LOW.claim_mut(t, |_low, t| { + // this claim increases the preemption threshold to 2 + // just high enough to not race with task `exti1` for access to the + // `LOW` resource + + // C + rtfm::bkpt(); + + // now `exti1` can't preempt this task because its priority is equal to + // the current preemption threshold + rtfm::set_pending(Interrupt::EXTI1); + + // but `exti2` can, because its priority is higher than the current + // preemption threshold + rtfm::set_pending(Interrupt::EXTI2); // ~> exti2 + + // E + rtfm::bkpt(); + + // claims can be nested + r.HIGH.claim_mut(t, |_high, _| { + // This claim increases the preemption threshold to 3 + + // now `exti2` can't preempt this task + rtfm::set_pending(Interrupt::EXTI2); + + // F + rtfm::bkpt(); + }); + + // upon leaving the critical section the preemption threshold drops to 2 + // and `exti2` immediately preempts this task + // ~> exti2 + }); + + // once again the preemption threshold drops to 1 + // now the pending `exti1` can preempt this task + // ~> exti1 +} + +task!(EXTI1, exti1); + +fn exti1(_t: &mut Threshold, _r: EXTI1::Resources) { + // B, H + rtfm::bkpt(); +} + +task!(EXTI2, exti2); + +fn exti2(_t: &mut Threshold, _r: EXTI2::Resources) { + // D, G + rtfm::bkpt(); +} diff --git a/examples/one-task.rs b/examples/one-task.rs new file mode 100644 index 00000000..8cfe089c --- /dev/null +++ b/examples/one-task.rs @@ -0,0 +1,91 @@ +//! An application with one task + +#![deny(unsafe_code)] +#![feature(const_fn)] +#![feature(proc_macro)] +#![no_std] + +extern crate cortex_m; +#[macro_use(task)] +extern crate cortex_m_rtfm as rtfm; +extern crate stm32f103xx; + +use cortex_m::peripheral::SystClkSource; +use rtfm::{app, Threshold}; + +app! { + device: stm32f103xx, + + // Here tasks are declared + // + // Each task corresponds to an interrupt or an exception. Every time the + // interrupt or exception becomes *pending* the corresponding task handler + // will be executed. + tasks: { + // Here we declare that we'll use the SYS_TICK exception as a task + SYS_TICK: { + // This is the priority of the task. + // 1 is the lowest priority a task can have. + // The maximum priority is determined by the number of priority bits + // the device has. This device has 4 priority bits so 16 is the + // maximum value. + priority: 1, + + // These are the *resources* associated with this task + // + // The peripherals that the task needs can be listed here + resources: [GPIOC], + }, + } +} + +fn init(p: init::Peripherals) { + // power on GPIOC + p.RCC.apb2enr.modify(|_, w| w.iopcen().enabled()); + + // configure PC13 as output + p.GPIOC.bsrr.write(|w| w.bs13().set()); + p.GPIOC + .crh + .modify(|_, w| w.mode13().output().cnf13().push()); + + // configure the system timer to generate one interrupt every second + p.SYST.set_clock_source(SystClkSource::Core); + p.SYST.set_reload(8_000_000); // 1s + p.SYST.enable_interrupt(); + p.SYST.enable_counter(); +} + +fn idle() -> ! { + loop { + rtfm::wfi(); + } +} + +// This binds the `sys_tick` handler to the `SYS_TICK` task +// +// This particular handler has local state associated to it. The value of the +// `STATE` variable will be preserved across invocations of this handler +task!(SYS_TICK, sys_tick, Locals { + static STATE: bool = false; +}); + +// This is the task handler of the SYS_TICK exception +// +// `t` is the preemption threshold token. We won't use it this time. +// `l` is the data local to this task. The type here must match the one declared +// in `task!`. +// `r` is the resources this task has access to. `SYS_TICK::Resources` has one +// field per resource declared in `app!`. +fn sys_tick(_t: &mut Threshold, l: &mut Locals, r: SYS_TICK::Resources) { + // toggle state + *l.STATE = !*l.STATE; + + if *l.STATE { + // set the pin PC13 high + r.GPIOC.bsrr.write(|w| w.bs13().set()); + } else { + // set the pin PC13 low + r.GPIOC.bsrr.write(|w| w.br13().reset()); + } +} diff --git a/examples/preemption.rs b/examples/preemption.rs new file mode 100644 index 00000000..2ca6f951 --- /dev/null +++ b/examples/preemption.rs @@ -0,0 +1,70 @@ +//! Two tasks running at different priorities with access to the same resource + +#![deny(unsafe_code)] +#![feature(const_fn)] +#![feature(proc_macro)] +#![no_std] + +#[macro_use(task)] +extern crate cortex_m_rtfm as rtfm; +extern crate stm32f103xx; + +use rtfm::{app, Resource, Threshold}; + +app! { + device: stm32f103xx, + + resources: { + static COUNTER: u64 = 0; + }, + + tasks: { + // the task `SYS_TICK` has higher priority than `TIM2` + SYS_TICK: { + priority: 2, + resources: [COUNTER], + }, + + TIM2: { + enabled: true, + priority: 1, + resources: [COUNTER], + }, + }, +} + +fn init(_p: init::Peripherals, _r: init::Resources) { + // .. +} + +fn idle() -> ! { + loop { + rtfm::wfi(); + } +} + +task!(SYS_TICK, sys_tick); + +fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { + // .. + + // this task can't be preempted by `tim2` so it has direct access to the + // resource data + **r.COUNTER += 1; + + // .. +} + +task!(TIM2, tim2); + +fn tim2(t: &mut Threshold, mut r: TIM2::Resources) { + // .. + + // as this task runs at lower priority it needs a critical section to + // prevent `sys_tick` from preempting it while it modifies this resource + // data. The critical section is required to prevent data races which can + // lead to data corruption or data loss + r.COUNTER.claim_mut(t, |counter, _t| { **counter += 1; }); + + // .. +} diff --git a/examples/two-tasks.rs b/examples/two-tasks.rs new file mode 100644 index 00000000..eb74fa8f --- /dev/null +++ b/examples/two-tasks.rs @@ -0,0 +1,75 @@ +//! Two tasks running at the same priority with access to the same resource + +#![deny(unsafe_code)] +#![feature(const_fn)] +#![feature(proc_macro)] +#![no_std] + +#[macro_use(task)] +extern crate cortex_m_rtfm as rtfm; +extern crate stm32f103xx; + +use rtfm::{app, Threshold}; + +app! { + device: stm32f103xx, + + // Resources that are plain data, not peripherals + resources: { + // Declaration of resources looks like the declaration of `static` + // variables + static COUNTER: u64 = 0; + }, + + tasks: { + SYS_TICK: { + priority: 1, + // Both this task and TIM2 have access to the `COUNTER` resource + resources: [COUNTER], + }, + + // An interrupt as a task + TIM2: { + // For interrupts the `enabled` field must be specified. It + // indicates if the interrupt will be enabled or disabled once + // `idle` starts + enabled: true, + priority: 1, + resources: [COUNTER], + }, + }, +} + +// when data resources are declared in the top `resources` field, `init` will +// have full access to them +fn init(_p: init::Peripherals, _r: init::Resources) { + // .. +} + +fn idle() -> ! { + loop { + rtfm::wfi(); + } +} + +task!(SYS_TICK, sys_tick); + +// As both tasks are running at the same priority one can't preempt the other. +// Thus both tasks have direct access to the resource +fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { + // .. + + **r.COUNTER += 1; + + // .. +} + +task!(TIM2, tim2); + +fn tim2(_t: &mut Threshold, r: TIM2::Resources) { + // .. + + **r.COUNTER += 1; + + // .. +} diff --git a/examples/zero-tasks.rs b/examples/zero-tasks.rs new file mode 100644 index 00000000..13201677 --- /dev/null +++ b/examples/zero-tasks.rs @@ -0,0 +1,49 @@ +//! Minimal example with zero tasks + +#![deny(unsafe_code)] +#![feature(proc_macro)] // IMPORTANT always include this feature gate +#![no_std] + +extern crate cortex_m_rtfm as rtfm; // IMPORTANT always do this rename +extern crate stm32f103xx; // the device crate + +// import the procedural macro +use rtfm::app; + +// This macro call indicates that this is a RTFM application +// +// This macro will expand to a `main` function so you don't need to supply +// `main` yourself. +app! { + // this is a path to the device crate + device: stm32f103xx, +} + +// The initialization phase. +// +// This runs first and within a *global* critical section. Nothing can preempt +// this function. +fn init(p: init::Peripherals) { + // This function has access to all the peripherals of the device + p.GPIOA; + p.RCC; + // .. + + // You'll hit this breakpoint first + rtfm::bkpt(); +} + +// The idle loop. +// +// This runs afterwards and has a priority of 0. All tasks can preempt this +// function. This function can never return so it must contain some sort of +// endless loop. +fn idle() -> ! { + // And then this breakpoint + rtfm::bkpt(); + + loop { + // This puts the processor to sleep until there's a task to service + rtfm::wfi(); + } +} |