mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-01 16:04:33 +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
7
.cargo/config
Normal file
7
.cargo/config
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[target.thumbv7m-none-eabi]
|
||||||
|
runner = 'arm-none-eabi-gdb'
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "linker=arm-none-eabi-ld",
|
||||||
|
"-Z", "linker-flavor=ld",
|
||||||
|
]
|
14
Cargo.toml
14
Cargo.toml
|
@ -19,6 +19,16 @@ static-ref = "0.2.0"
|
||||||
[dependencies.cortex-m-rtfm-macros]
|
[dependencies.cortex-m-rtfm-macros]
|
||||||
path = "macros"
|
path = "macros"
|
||||||
|
|
||||||
[dev-dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
|
||||||
compiletest_rs = "0.2.8"
|
compiletest_rs = "0.2.8"
|
||||||
stm32f103xx = "0.7.1"
|
|
||||||
|
[dev-dependencies.cortex-m-rt]
|
||||||
|
features = ["abort-on-panic"]
|
||||||
|
version = "0.3.3"
|
||||||
|
|
||||||
|
[dev-dependencies.stm32f103xx]
|
||||||
|
features = ["rt"]
|
||||||
|
version = "0.7.1"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
5
Xargo.toml
Normal file
5
Xargo.toml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[dependencies.core]
|
||||||
|
stage = 0
|
||||||
|
|
||||||
|
[dependencies.compiler_builtins]
|
||||||
|
stage = 1
|
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();
|
||||||
|
}
|
||||||
|
}
|
55
gen-examples.sh
Normal file
55
gen-examples.sh
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# Converts the examples in the `examples` directory into documentation in the
|
||||||
|
# `examples` module (`src/examples/*.rs`)
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
main() {
|
||||||
|
local examples=(
|
||||||
|
zero-tasks
|
||||||
|
one-task
|
||||||
|
two-tasks
|
||||||
|
preemption
|
||||||
|
nested
|
||||||
|
generics
|
||||||
|
full-syntax
|
||||||
|
)
|
||||||
|
|
||||||
|
rm -rf src/examples
|
||||||
|
|
||||||
|
mkdir src/examples
|
||||||
|
|
||||||
|
cat >src/examples/mod.rs <<'EOF'
|
||||||
|
//! Examples
|
||||||
|
// Auto-generated. Do not modify.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
local i=0 out=
|
||||||
|
for ex in ${examples[@]}; do
|
||||||
|
name=_${i}_${ex//-/_}
|
||||||
|
out=src/examples/${name}.rs
|
||||||
|
|
||||||
|
echo "pub mod $name;" >> src/examples/mod.rs
|
||||||
|
|
||||||
|
grep '//!' examples/$ex.rs > $out
|
||||||
|
echo '//!' >> $out
|
||||||
|
echo '//! ```' >> $out
|
||||||
|
grep -v '//!' examples/$ex.rs | (
|
||||||
|
IFS=''
|
||||||
|
|
||||||
|
while read line; do
|
||||||
|
echo "//! $line" >> $out;
|
||||||
|
done
|
||||||
|
)
|
||||||
|
echo '//! ```' >> $out
|
||||||
|
echo '// Auto-generated. Do not modify.' >> $out
|
||||||
|
|
||||||
|
|
||||||
|
chmod -x $out
|
||||||
|
|
||||||
|
i=$(( i + 1 ))
|
||||||
|
done
|
||||||
|
|
||||||
|
chmod -x src/examples/mod.rs
|
||||||
|
}
|
||||||
|
|
||||||
|
main
|
|
@ -64,15 +64,16 @@ fn idle(
|
||||||
let ty = &resource.ty;
|
let ty = &resource.ty;
|
||||||
|
|
||||||
lfields.push(quote! {
|
lfields.push(quote! {
|
||||||
pub #name: #ty,
|
pub #name: #krate::Static<#ty>,
|
||||||
});
|
});
|
||||||
|
|
||||||
lexprs.push(quote! {
|
lexprs.push(quote! {
|
||||||
#name: #expr,
|
#name: unsafe { #krate::Static::new(#expr) },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mod_items.push(quote! {
|
mod_items.push(quote! {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
pub struct Locals {
|
pub struct Locals {
|
||||||
#(#lfields)*
|
#(#lfields)*
|
||||||
}
|
}
|
||||||
|
@ -114,19 +115,24 @@ fn idle(
|
||||||
let ty = &resource.ty;
|
let ty = &resource.ty;
|
||||||
|
|
||||||
rfields.push(quote! {
|
rfields.push(quote! {
|
||||||
pub #name: &'static mut #ty,
|
pub #name: &'static mut ::#krate::Static<#ty>,
|
||||||
});
|
});
|
||||||
|
|
||||||
rexprs.push(quote! {
|
rexprs.push(quote! {
|
||||||
#name: &mut *#super_::#name.get(),
|
#name: #krate::Static::ref_mut(
|
||||||
|
&mut *#super_::#name.get(),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
rfields.push(quote! {
|
rfields.push(quote! {
|
||||||
pub #name: &'static mut ::#device::#name,
|
pub #name:
|
||||||
|
&'static mut ::#krate::Static<::#device::#name>,
|
||||||
});
|
});
|
||||||
|
|
||||||
rexprs.push(quote! {
|
rexprs.push(quote! {
|
||||||
#name: &mut *::#device::#name.get(),
|
#name: ::krate::Static::ref_mut(
|
||||||
|
&mut *::#device::#name.get(),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -329,8 +335,8 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
|
||||||
let ty = &resource.ty;
|
let ty = &resource.ty;
|
||||||
|
|
||||||
root.push(quote! {
|
root.push(quote! {
|
||||||
static #name: #krate::Resource<#ty> =
|
static #name: #krate::Cell<#ty> =
|
||||||
#krate::Resource::new(#expr);
|
#krate::Cell::new(#expr);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Peripheral
|
// Peripheral
|
||||||
|
@ -343,26 +349,30 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
|
||||||
let ty = &resource.ty;
|
let ty = &resource.ty;
|
||||||
|
|
||||||
root.push(quote! {
|
root.push(quote! {
|
||||||
static #name: #krate::Resource<#ty> =
|
static #name: #krate::Cell<#ty> =
|
||||||
#krate::Resource::new(#expr);
|
#krate::Cell::new(#expr);
|
||||||
});
|
});
|
||||||
|
|
||||||
impl_items.push(quote! {
|
impl_items.push(quote! {
|
||||||
pub fn borrow<'cs>(
|
type Data = #ty;
|
||||||
|
|
||||||
|
fn borrow<'cs>(
|
||||||
&'cs self,
|
&'cs self,
|
||||||
cs: &'cs #krate::CriticalSection,
|
_cs: &'cs #krate::CriticalSection,
|
||||||
) -> &'cs #krate::Static<#ty> {
|
) -> &'cs #krate::Static<#ty> {
|
||||||
unsafe { #name.borrow(cs) }
|
unsafe { #krate::Static::ref_(&*#name.get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn borrow_mut<'cs>(
|
fn borrow_mut<'cs>(
|
||||||
&'cs mut self,
|
&'cs mut self,
|
||||||
cs: &'cs #krate::CriticalSection,
|
_cs: &'cs #krate::CriticalSection,
|
||||||
) -> &'cs mut #krate::Static<#ty> {
|
) -> &'cs mut #krate::Static<#ty> {
|
||||||
unsafe { #name.borrow_mut(cs) }
|
unsafe {
|
||||||
|
#krate::Static::ref_mut(&mut *#name.get())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn claim<R, F>(
|
fn claim<R, F>(
|
||||||
&self,
|
&self,
|
||||||
t: &mut #krate::Threshold,
|
t: &mut #krate::Threshold,
|
||||||
f: F,
|
f: F,
|
||||||
|
@ -373,16 +383,18 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
|
||||||
&mut #krate::Threshold) -> R
|
&mut #krate::Threshold) -> R
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
#name.claim(
|
#krate::claim(
|
||||||
|
#name.get(),
|
||||||
#ceiling,
|
#ceiling,
|
||||||
#device::NVIC_PRIO_BITS,
|
#device::NVIC_PRIO_BITS,
|
||||||
t,
|
t,
|
||||||
f,
|
f,
|
||||||
|
|data| #krate::Static::ref_(&*data),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn claim_mut<R, F>(
|
fn claim_mut<R, F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
t: &mut #krate::Threshold,
|
t: &mut #krate::Threshold,
|
||||||
f: F,
|
f: F,
|
||||||
|
@ -393,45 +405,77 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
|
||||||
&mut #krate::Threshold) -> R
|
&mut #krate::Threshold) -> R
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
#name.claim_mut(
|
#krate::claim(
|
||||||
|
#name.get(),
|
||||||
#ceiling,
|
#ceiling,
|
||||||
#device::NVIC_PRIO_BITS,
|
#device::NVIC_PRIO_BITS,
|
||||||
t,
|
t,
|
||||||
f,
|
f,
|
||||||
|
|data| #krate::Static::ref_mut(&mut *data),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
root.push(quote! {
|
|
||||||
static #name: #krate::Peripheral<#device::#name> =
|
|
||||||
#krate::Peripheral::new(#device::#name);
|
|
||||||
});
|
|
||||||
|
|
||||||
impl_items.push(quote! {
|
impl_items.push(quote! {
|
||||||
pub fn borrow<'cs>(
|
type Data = #device::#name;
|
||||||
|
|
||||||
|
fn borrow<'cs>(
|
||||||
&'cs self,
|
&'cs self,
|
||||||
cs: &'cs #krate::CriticalSection,
|
_cs: &'cs #krate::CriticalSection,
|
||||||
) -> &'cs #device::#name {
|
) -> &'cs #krate::Static<#name> {
|
||||||
unsafe { #name.borrow(cs) }
|
unsafe { #krate::Static::ref_(&*#name.get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn claim<R, F>(
|
fn borrow_mut<'cs>(
|
||||||
|
&'cs mut self,
|
||||||
|
_cs: &'cs #krate::CriticalSection,
|
||||||
|
) -> &'cs mut #krate::Static<#name> {
|
||||||
|
unsafe {
|
||||||
|
#krate::Static::ref_mut(&mut *#name.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn claim<R, F>(
|
||||||
&self,
|
&self,
|
||||||
t: &mut #krate::Threshold,
|
t: &mut #krate::Threshold,
|
||||||
f: F,
|
f: F,
|
||||||
) -> R
|
) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(
|
F: FnOnce(
|
||||||
&#device::#name,
|
&#krate::Static<#name>,
|
||||||
&mut #krate::Threshold) -> R
|
&mut #krate::Threshold) -> R
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
#name.claim(
|
#krate::claim(
|
||||||
|
#device::#name.get(),
|
||||||
#ceiling,
|
#ceiling,
|
||||||
#device::NVIC_PRIO_BITS,
|
#device::NVIC_PRIO_BITS,
|
||||||
t,
|
t,
|
||||||
f,
|
f,
|
||||||
|
|data| #krate::Static::ref_(&*data),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn claim_mut<R, F>(
|
||||||
|
&mut self,
|
||||||
|
t: &mut #krate::Threshold,
|
||||||
|
f: F,
|
||||||
|
) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(
|
||||||
|
&mut #krate::Static<#name>,
|
||||||
|
&mut #krate::Threshold) -> R
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
#krate::claim(
|
||||||
|
#device::#name.get(),
|
||||||
|
#ceiling,
|
||||||
|
#device::NVIC_PRIO_BITS,
|
||||||
|
t,
|
||||||
|
f,
|
||||||
|
|data| #krate::Static::ref_mut(&mut *data),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,9 +483,8 @@ fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
impls.push(quote! {
|
impls.push(quote! {
|
||||||
#[allow(dead_code)]
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
impl _resource::#name {
|
impl #krate::Resource for _resource::#name {
|
||||||
#(#impl_items)*
|
#(#impl_items)*
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -512,11 +555,14 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
fields.push(quote! {
|
fields.push(quote! {
|
||||||
pub #name: &'a mut ::#device::#name,
|
pub #name:
|
||||||
|
&'a mut ::#krate::Static<::#device::#name>,
|
||||||
});
|
});
|
||||||
|
|
||||||
exprs.push(quote! {
|
exprs.push(quote! {
|
||||||
#name: &mut *::#device::#name.get(),
|
#name: ::#krate::Static::ref_mut(
|
||||||
|
&mut *::#device::#name.get(),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -558,12 +604,13 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec<Tokens>) {
|
||||||
|
|
||||||
let priority = task.priority;
|
let priority = task.priority;
|
||||||
root.push(quote!{
|
root.push(quote!{
|
||||||
#[allow(dead_code)]
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
mod #name {
|
mod #name {
|
||||||
#[deny(dead_code)]
|
#[deny(dead_code)]
|
||||||
pub const #name: u8 = #priority;
|
pub const #name: u8 = #priority;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[deny(const_err)]
|
#[deny(const_err)]
|
||||||
const CHECK_PRIORITY: (u8, u8) = (
|
const CHECK_PRIORITY: (u8, u8) = (
|
||||||
#priority - 1,
|
#priority - 1,
|
||||||
|
|
6
memory.x
Normal file
6
memory.x
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
/* STM32F103C8V6 */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
53
src/examples/_0_zero_tasks.rs
Normal file
53
src/examples/_0_zero_tasks.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
//! 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();
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
// Auto-generated. Do not modify.
|
95
src/examples/_1_one_task.rs
Normal file
95
src/examples/_1_one_task.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
//! 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());
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
// Auto-generated. Do not modify.
|
79
src/examples/_2_two_tasks.rs
Normal file
79
src/examples/_2_two_tasks.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
//! 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;
|
||||||
|
//!
|
||||||
|
//! // ..
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
// Auto-generated. Do not modify.
|
74
src/examples/_3_preemption.rs
Normal file
74
src/examples/_3_preemption.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//! 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; });
|
||||||
|
//!
|
||||||
|
//! // ..
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
// Auto-generated. Do not modify.
|
129
src/examples/_4_nested.rs
Normal file
129
src/examples/_4_nested.rs
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
//! 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();
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
// Auto-generated. Do not modify.
|
73
src/examples/_5_generics.rs
Normal file
73
src/examples/_5_generics.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
//! 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);
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
// Auto-generated. Do not modify.
|
85
src/examples/_6_full_syntax.rs
Normal file
85
src/examples/_6_full_syntax.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
//! 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;
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
// Auto-generated. Do not modify.
|
9
src/examples/mod.rs
Normal file
9
src/examples/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
//! Examples
|
||||||
|
// Auto-generated. Do not modify.
|
||||||
|
pub mod _0_zero_tasks;
|
||||||
|
pub mod _1_one_task;
|
||||||
|
pub mod _2_two_tasks;
|
||||||
|
pub mod _3_preemption;
|
||||||
|
pub mod _4_nested;
|
||||||
|
pub mod _5_generics;
|
||||||
|
pub mod _6_full_syntax;
|
288
src/lib.rs
288
src/lib.rs
|
@ -1,3 +1,57 @@
|
||||||
|
//! Real Time For the Masses (RTFM), a framework for building concurrent
|
||||||
|
//! applications, for ARM Cortex-M microcontrollers
|
||||||
|
//!
|
||||||
|
//! This crate is based on [the RTFM framework] created by the Embedded Systems
|
||||||
|
//! group at [Luleå University of Technology][ltu], led by Prof. Per Lindgren,
|
||||||
|
//! and uses a simplified version of the Stack Resource Policy as scheduling
|
||||||
|
//! policy (check the [references] for details).
|
||||||
|
//!
|
||||||
|
//! [the RTFM framework]: http://www.rtfm-lang.org/
|
||||||
|
//! [ltu]: https://www.ltu.se/?l=en
|
||||||
|
//! [per]: https://www.ltu.se/staff/p/pln-1.11258?l=en
|
||||||
|
//! [references]: ./index.html#references
|
||||||
|
//!
|
||||||
|
//! # Features
|
||||||
|
//!
|
||||||
|
//! - **Event triggered tasks** as the unit of concurrency.
|
||||||
|
//! - Support for prioritization of tasks and, thus, **preemptive
|
||||||
|
//! multitasking**.
|
||||||
|
//! - **Efficient and data race free memory sharing** through fine grained *non
|
||||||
|
//! global* critical sections.
|
||||||
|
//! - **Deadlock free execution** guaranteed at compile time.
|
||||||
|
//! - **Minimal scheduling overhead** as the scheduler has no "software
|
||||||
|
//! component": the hardware does all the scheduling.
|
||||||
|
//! - **Highly efficient memory usage**: All the tasks share a single call stack
|
||||||
|
//! and there's no hard dependency on a dynamic memory allocator.
|
||||||
|
//! - **All Cortex M devices are fully supported**.
|
||||||
|
//! - This task model is amenable to known WCET (Worst Case Execution Time)
|
||||||
|
//! analysis and scheduling analysis techniques. (Though we haven't yet
|
||||||
|
//! developed Rust friendly tooling for that.)
|
||||||
|
//!
|
||||||
|
//! # Constraints
|
||||||
|
//!
|
||||||
|
//! - Tasks must run to completion. That's it, tasks can't contain endless
|
||||||
|
//! loops. However, you can run an endless event loop in the `idle` function.
|
||||||
|
//!
|
||||||
|
//! - Task priorities must remain constant at runtime.
|
||||||
|
//!
|
||||||
|
//! # Dependencies
|
||||||
|
//!
|
||||||
|
//! - A device crate generated using [`svd2rust`] v0.11.x. The input SVD file
|
||||||
|
//! *must* contain [`<cpu>`] information.
|
||||||
|
//! - A `start` lang time: Vanilla `main` must be supported in binary crates.
|
||||||
|
//! You can use the [`cortex-m-rt`] crate to fulfill the requirement
|
||||||
|
//!
|
||||||
|
//! [`svd2rust`]: https://docs.rs/svd2rust/0..0/svd2rust/
|
||||||
|
//! [`<cpu>`]: https://www.keil.com/pack/doc/CMSIS/SVD/html/elem_cpu.html
|
||||||
|
//! [`cortex-m-rt`]: https://docs.rs/cortex-m-rt/0.3.0/cortex_m_rt/
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! In increasing grade of complexity: [examples](./examples/index.html)
|
||||||
|
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![deny(warnings)]
|
||||||
#![feature(asm)]
|
#![feature(asm)]
|
||||||
#![feature(const_fn)]
|
#![feature(const_fn)]
|
||||||
#![feature(optin_builtin_traits)]
|
#![feature(optin_builtin_traits)]
|
||||||
|
@ -17,11 +71,73 @@ pub use cortex_m::interrupt::free as atomic;
|
||||||
pub use static_ref::Static;
|
pub use static_ref::Static;
|
||||||
use cortex_m::interrupt::Nr;
|
use cortex_m::interrupt::Nr;
|
||||||
#[cfg(not(armv6m))]
|
#[cfg(not(armv6m))]
|
||||||
use cortex_m::register::{basepri_max, basepri};
|
use cortex_m::register::{basepri, basepri_max};
|
||||||
|
|
||||||
#[inline(always)]
|
pub mod examples;
|
||||||
unsafe fn claim<T, U, R, F, G>(
|
|
||||||
data: T,
|
/// A resource, a means to share data between tasks
|
||||||
|
pub trait Resource {
|
||||||
|
/// The data protected by the resource
|
||||||
|
type Data;
|
||||||
|
|
||||||
|
/// Borrows the resource data for the duration of a *global* critical
|
||||||
|
/// section
|
||||||
|
fn borrow<'cs>(
|
||||||
|
&'cs self,
|
||||||
|
cs: &'cs CriticalSection,
|
||||||
|
) -> &'cs Static<Self::Data>;
|
||||||
|
|
||||||
|
/// Mutable variant of `borrow`
|
||||||
|
fn borrow_mut<'cs>(
|
||||||
|
&'cs mut self,
|
||||||
|
cs: &'cs CriticalSection,
|
||||||
|
) -> &'cs mut Static<Self::Data>;
|
||||||
|
|
||||||
|
/// Claims the resource data for the span of the closure `f`. For the
|
||||||
|
/// duration of the closure other tasks that may access the resource data
|
||||||
|
/// are prevented from preempting the current task.
|
||||||
|
fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&Static<Self::Data>, &mut Threshold) -> R;
|
||||||
|
|
||||||
|
/// Mutable variant of `claim`
|
||||||
|
fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> R;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Resource for Static<T> {
|
||||||
|
type Data = T;
|
||||||
|
|
||||||
|
fn borrow<'cs>(&'cs self, _cs: &'cs CriticalSection) -> &'cs Static<T> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn borrow_mut<'cs>(
|
||||||
|
&'cs mut self,
|
||||||
|
_cs: &'cs CriticalSection,
|
||||||
|
) -> &'cs mut Static<T> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn claim<R, F>(&self, t: &mut Threshold, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&Static<Self::Data>, &mut Threshold) -> R,
|
||||||
|
{
|
||||||
|
f(self, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn claim_mut<R, F>(&mut self, t: &mut Threshold, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Static<Self::Data>, &mut Threshold) -> R,
|
||||||
|
{
|
||||||
|
f(self, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub unsafe fn claim<T, U, R, F, G>(
|
||||||
|
data: *mut T,
|
||||||
ceiling: u8,
|
ceiling: u8,
|
||||||
nvic_prio_bits: u8,
|
nvic_prio_bits: u8,
|
||||||
t: &mut Threshold,
|
t: &mut Threshold,
|
||||||
|
@ -30,10 +146,10 @@ unsafe fn claim<T, U, R, F, G>(
|
||||||
) -> R
|
) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(U, &mut Threshold) -> R,
|
F: FnOnce(U, &mut Threshold) -> R,
|
||||||
G: FnOnce(T) -> U,
|
G: FnOnce(*mut T) -> U,
|
||||||
{
|
{
|
||||||
let max_priority = 1 << nvic_prio_bits;
|
let max_priority = 1 << nvic_prio_bits;
|
||||||
if ceiling > t.0 {
|
if ceiling > t.value {
|
||||||
match () {
|
match () {
|
||||||
#[cfg(armv6m)]
|
#[cfg(armv6m)]
|
||||||
() => {
|
() => {
|
||||||
|
@ -47,7 +163,7 @@ where
|
||||||
let old = basepri::read();
|
let old = basepri::read();
|
||||||
let hw = (max_priority - ceiling) << (8 - nvic_prio_bits);
|
let hw = (max_priority - ceiling) << (8 - nvic_prio_bits);
|
||||||
basepri_max::write(hw);
|
basepri_max::write(hw);
|
||||||
let ret = f(g(data), &mut Threshold(ceiling));
|
let ret = f(g(data), &mut Threshold::new(ceiling));
|
||||||
basepri::write(old);
|
basepri::write(old);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -58,139 +174,44 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Peripheral<P>
|
#[doc(hidden)]
|
||||||
where
|
pub struct Cell<T> {
|
||||||
P: 'static,
|
|
||||||
{
|
|
||||||
// FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes
|
|
||||||
// claims (the ceiling value gets loaded at runtime rather than inlined)
|
|
||||||
// ceiling: u8,
|
|
||||||
peripheral: cortex_m::peripheral::Peripheral<P>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P> Peripheral<P> {
|
|
||||||
pub const fn new(peripheral: cortex_m::peripheral::Peripheral<P>) -> Self {
|
|
||||||
Peripheral { peripheral }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn borrow<'cs>(
|
|
||||||
&'static self,
|
|
||||||
_cs: &'cs CriticalSection,
|
|
||||||
) -> &'cs P {
|
|
||||||
&*self.peripheral.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn claim<R, F>(
|
|
||||||
&'static self,
|
|
||||||
ceiling: u8,
|
|
||||||
nvic_prio_bits: u8,
|
|
||||||
t: &mut Threshold,
|
|
||||||
f: F,
|
|
||||||
) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&P, &mut Threshold) -> R,
|
|
||||||
{
|
|
||||||
claim(
|
|
||||||
&self.peripheral,
|
|
||||||
ceiling,
|
|
||||||
nvic_prio_bits,
|
|
||||||
t,
|
|
||||||
f,
|
|
||||||
|peripheral| &*peripheral.get(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> *mut P {
|
|
||||||
self.peripheral.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<P> Sync for Peripheral<P>
|
|
||||||
where
|
|
||||||
P: Send,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Resource<T> {
|
|
||||||
// FIXME(rustc/LLVM bug?) storing the ceiling in the resource de-optimizes
|
|
||||||
// claims (the ceiling value gets loaded at runtime rather than inlined)
|
|
||||||
// ceiling: u8,
|
|
||||||
data: UnsafeCell<T>,
|
data: UnsafeCell<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Resource<T> {
|
#[doc(hidden)]
|
||||||
pub const fn new(value: T) -> Self {
|
impl<T> Cell<T> {
|
||||||
Resource {
|
pub const fn new(data: T) -> Self {
|
||||||
data: UnsafeCell::new(value),
|
Cell {
|
||||||
|
data: UnsafeCell::new(data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn borrow<'cs>(
|
|
||||||
&'static self,
|
|
||||||
_cs: &'cs CriticalSection,
|
|
||||||
) -> &'cs Static<T> {
|
|
||||||
Static::ref_(&*self.data.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn borrow_mut<'cs>(
|
|
||||||
&'static self,
|
|
||||||
_cs: &'cs CriticalSection,
|
|
||||||
) -> &'cs mut Static<T> {
|
|
||||||
Static::ref_mut(&mut *self.data.get())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn claim<R, F>(
|
|
||||||
&'static self,
|
|
||||||
ceiling: u8,
|
|
||||||
nvic_prio_bits: u8,
|
|
||||||
t: &mut Threshold,
|
|
||||||
f: F,
|
|
||||||
) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&Static<T>, &mut Threshold) -> R,
|
|
||||||
{
|
|
||||||
claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| {
|
|
||||||
Static::ref_(&*data.get())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn claim_mut<R, F>(
|
|
||||||
&'static self,
|
|
||||||
ceiling: u8,
|
|
||||||
nvic_prio_bits: u8,
|
|
||||||
t: &mut Threshold,
|
|
||||||
f: F,
|
|
||||||
) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Static<T>, &mut Threshold) -> R,
|
|
||||||
{
|
|
||||||
claim(&self.data, ceiling, nvic_prio_bits, t, f, |data| {
|
|
||||||
Static::ref_mut(&mut *data.get())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> *mut T {
|
pub fn get(&self) -> *mut T {
|
||||||
self.data.get()
|
self.data.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T> Sync for Resource<T>
|
unsafe impl<T> Sync for Cell<T>
|
||||||
where
|
where
|
||||||
T: Send,
|
T: Send,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Threshold(u8);
|
/// Preemption threshold token
|
||||||
|
///
|
||||||
|
/// The preemption threshold indicates the priority a task must have to preempt
|
||||||
|
/// the current context. For example a threshold of 2 indicates that only
|
||||||
|
/// interrupts / exceptions with a priority of 3 or greater can preempt the
|
||||||
|
/// current context
|
||||||
|
pub struct Threshold {
|
||||||
|
value: u8,
|
||||||
|
}
|
||||||
|
|
||||||
impl Threshold {
|
impl Threshold {
|
||||||
|
#[doc(hidden)]
|
||||||
pub unsafe fn new(value: u8) -> Self {
|
pub unsafe fn new(value: u8) -> Self {
|
||||||
Threshold(value)
|
Threshold { value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,14 +227,15 @@ where
|
||||||
nvic.set_pending(interrupt);
|
nvic.set_pending(interrupt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Binds a task `$handler` to the interrupt / exception `$NAME`
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! task {
|
macro_rules! task {
|
||||||
($NAME:ident, $body:path) => {
|
($NAME:ident, $handler:path) => {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn $NAME() {
|
pub unsafe extern "C" fn $NAME() {
|
||||||
let f: fn(&mut $crate::Threshold, ::$NAME::Resources) = $body;
|
let f: fn(&mut $crate::Threshold, ::$NAME::Resources) = $handler;
|
||||||
|
|
||||||
f(
|
f(
|
||||||
&mut $crate::Threshold::new(::$NAME::$NAME),
|
&mut $crate::Threshold::new(::$NAME::$NAME),
|
||||||
|
@ -221,11 +243,13 @@ macro_rules! task {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($NAME:ident, $body:path, $local:ident {
|
|
||||||
$($var:ident: $ty:ty = $expr:expr;)+
|
($NAME:ident, $handler:path, $locals:ident {
|
||||||
|
$(static $var:ident: $ty:ty = $expr:expr;)+
|
||||||
}) => {
|
}) => {
|
||||||
struct $local {
|
#[allow(non_snake_case)]
|
||||||
$($var: $ty,)+
|
struct $locals {
|
||||||
|
$($var: $crate::Static<$ty>,)+
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -234,17 +258,17 @@ macro_rules! task {
|
||||||
pub unsafe extern "C" fn $NAME() {
|
pub unsafe extern "C" fn $NAME() {
|
||||||
let f: fn(
|
let f: fn(
|
||||||
&mut $crate::Threshold,
|
&mut $crate::Threshold,
|
||||||
&mut $local,
|
&mut $locals,
|
||||||
::$NAME::Resources,
|
::$NAME::Resources,
|
||||||
) = $body;
|
) = $handler;
|
||||||
|
|
||||||
static mut LOCAL: $local = $local {
|
static mut LOCALS: $locals = $locals {
|
||||||
$($var: $expr,)+
|
$($var: unsafe { $crate::Static::new($expr) },)+
|
||||||
};
|
};
|
||||||
|
|
||||||
f(
|
f(
|
||||||
&mut $crate::Threshold::new(::$NAME::$NAME),
|
&mut $crate::Threshold::new(::$NAME::$NAME),
|
||||||
&mut LOCAL,
|
&mut LOCALS,
|
||||||
::$NAME::Resources::new(),
|
::$NAME::Resources::new(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue