aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/full-syntax.rs81
-rw-r--r--examples/generics.rs69
-rw-r--r--examples/nested.rs125
-rw-r--r--examples/one-task.rs91
-rw-r--r--examples/preemption.rs70
-rw-r--r--examples/two-tasks.rs75
-rw-r--r--examples/zero-tasks.rs49
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();
+ }
+}