mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-16 21:05:35 +01:00
Resource trait, docs, examples and rtfm-syntax related changes
This commit is contained in:
parent
23425f2f06
commit
c7b9507a57
22 changed files with 1482 additions and 171 deletions
81
examples/full-syntax.rs
Normal file
81
examples/full-syntax.rs
Normal file
|
|
@ -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;
|
||||
}
|
||||
69
examples/generics.rs
Normal file
69
examples/generics.rs
Normal file
|
|
@ -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);
|
||||
}
|
||||
125
examples/nested.rs
Normal file
125
examples/nested.rs
Normal file
|
|
@ -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();
|
||||
}
|
||||
91
examples/one-task.rs
Normal file
91
examples/one-task.rs
Normal file
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
70
examples/preemption.rs
Normal file
70
examples/preemption.rs
Normal file
|
|
@ -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; });
|
||||
|
||||
// ..
|
||||
}
|
||||
75
examples/two-tasks.rs
Normal file
75
examples/two-tasks.rs
Normal file
|
|
@ -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;
|
||||
|
||||
// ..
|
||||
}
|
||||
49
examples/zero-tasks.rs
Normal file
49
examples/zero-tasks.rs
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue