diff options
| author | Jorge Aparicio <jorge@japaric.io> | 2018-11-03 17:02:41 +0100 |
|---|---|---|
| committer | Jorge Aparicio <jorge@japaric.io> | 2018-11-03 17:16:55 +0100 |
| commit | c631049efcadca8b07940c794cce2be58fa48444 (patch) | |
| tree | f6bd73e5c396fc06072557ee965cc59e9c8e3e9f /examples | |
| parent | 653338e7997a0cdc5deaed98b1bb5f60006717ed (diff) | |
v0.4.0
closes #32
closes #33
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/baseline.rs | 61 | ||||
| -rw-r--r-- | examples/capacity.rs | 57 | ||||
| -rw-r--r-- | examples/custom-type.rs | 49 | ||||
| -rw-r--r-- | examples/full-syntax.rs | 83 | ||||
| -rw-r--r-- | examples/generics.rs | 73 | ||||
| -rw-r--r-- | examples/idle.rs | 43 | ||||
| -rw-r--r-- | examples/init.rs | 44 | ||||
| -rw-r--r-- | examples/interrupt.rs | 61 | ||||
| -rw-r--r-- | examples/late-resources.rs | 86 | ||||
| -rw-r--r-- | examples/late.rs | 65 | ||||
| -rw-r--r-- | examples/lock.rs | 71 | ||||
| -rw-r--r-- | examples/message.rs | 61 | ||||
| -rw-r--r-- | examples/nested.rs | 128 | ||||
| -rw-r--r-- | examples/not-send.rs | 58 | ||||
| -rw-r--r-- | examples/not-sync.rs | 41 | ||||
| -rw-r--r-- | examples/one-task.rs | 96 | ||||
| -rw-r--r-- | examples/periodic.rs | 43 | ||||
| -rw-r--r-- | examples/preemption.rs | 67 | ||||
| -rw-r--r-- | examples/ramfunc.rs | 53 | ||||
| -rw-r--r-- | examples/resource.rs | 60 | ||||
| -rw-r--r-- | examples/safe-static-mut-ref.rs | 31 | ||||
| -rw-r--r-- | examples/schedule.rs | 51 | ||||
| -rw-r--r-- | examples/singleton.rs | 69 | ||||
| -rw-r--r-- | examples/smallest.rs | 17 | ||||
| -rw-r--r-- | examples/static.rs | 47 | ||||
| -rw-r--r-- | examples/task.rs | 61 | ||||
| -rw-r--r-- | examples/two-tasks.rs | 58 | ||||
| -rw-r--r-- | examples/types.rs | 54 | ||||
| -rw-r--r-- | examples/zero-tasks.rs | 43 |
29 files changed, 1017 insertions, 714 deletions
diff --git a/examples/baseline.rs b/examples/baseline.rs new file mode 100644 index 0000000..73ef4c9 --- /dev/null +++ b/examples/baseline.rs @@ -0,0 +1,61 @@ +//! examples/baseline.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use lm3s6965::Interrupt; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +// NOTE: does NOT properly work on QEMU +#[app(device = lm3s6965)] +const APP: () = { + #[init(spawn = [foo])] + fn init() { + println!("init(baseline = {:?})", start); + + // `foo` inherits the baseline of `init`: `Instant(0)` + spawn.foo().unwrap(); + } + + #[task(schedule = [foo])] + fn foo() { + static mut ONCE: bool = true; + + println!("foo(baseline = {:?})", scheduled); + + if *ONCE { + *ONCE = false; + + rtfm::pend(Interrupt::UART0); + } else { + debug::exit(debug::EXIT_SUCCESS); + } + } + + #[interrupt(spawn = [foo])] + fn UART0() { + println!("UART0(baseline = {:?})", start); + + // `foo` inherits the baseline of `UART0`: its `start` time + spawn.foo().unwrap(); + } + + extern "C" { + fn UART1(); + } +}; diff --git a/examples/capacity.rs b/examples/capacity.rs new file mode 100644 index 0000000..2dea2c3 --- /dev/null +++ b/examples/capacity.rs @@ -0,0 +1,57 @@ +//! examples/capacity.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use lm3s6965::Interrupt; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + #[init(spawn = [foo])] + fn init() { + rtfm::pend(Interrupt::UART0); + } + + #[interrupt(spawn = [foo, bar])] + fn UART0() { + spawn.foo(0).unwrap(); + spawn.foo(1).unwrap(); + spawn.foo(2).unwrap(); + spawn.foo(3).unwrap(); + + spawn.bar().unwrap(); + } + + #[task(capacity = 4)] + fn foo(x: u32) { + println!("foo({})", x); + } + + #[task] + fn bar() { + println!("bar"); + + debug::exit(debug::EXIT_SUCCESS); + } + + // Interrupt handlers used to dispatch software tasks + extern "C" { + fn UART1(); + } +}; diff --git a/examples/custom-type.rs b/examples/custom-type.rs deleted file mode 100644 index 826e9dd..0000000 --- a/examples/custom-type.rs +++ /dev/null @@ -1,49 +0,0 @@ -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_std] - -extern crate cortex_m_rtfm as rtfm; -extern crate stm32f103xx; - -use rtfm::{app, Threshold}; - -pub struct Foo; - -app! { - device: stm32f103xx, - - resources: { - static CO_OWNED: Foo = Foo; - static ON: Foo = Foo; - static OWNED: Foo = Foo; - static SHARED: Foo = Foo; - }, - - idle: { - resources: [OWNED, SHARED], - }, - - tasks: { - SYS_TICK: { - path: sys_tick, - resources: [CO_OWNED, ON, SHARED], - }, - - TIM2: { - enabled: false, - path: tim2, - priority: 1, - resources: [CO_OWNED], - }, - }, -} - -fn init(_p: ::init::Peripherals, _r: ::init::Resources) {} - -fn idle(_t: &mut Threshold, _r: ::idle::Resources) -> ! { - loop {} -} - -fn sys_tick(_t: &mut Threshold, _r: SYS_TICK::Resources) {} - -fn tim2(_t: &mut Threshold, _r: TIM2::Resources) {} diff --git a/examples/full-syntax.rs b/examples/full-syntax.rs deleted file mode 100644 index 9bdcd7b..0000000 --- a/examples/full-syntax.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! A showcase of the `app!` macro syntax -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_std] - -extern crate cortex_m_rtfm as rtfm; -extern crate stm32f103xx; - -use rtfm::{app, Threshold}; - -app! { - device: stm32f103xx, - - resources: { - static CO_OWNED: u32 = 0; - static ON: bool = false; - static OWNED: bool = false; - static SHARED: bool = false; - }, - - init: { - // This is the path to the `init` function - // - // `init` doesn't necessarily has to be in the root of the crate - path: main::init, - }, - - idle: { - // This is a path to the `idle` function - // - // `idle` doesn't necessarily has to be in the root of the crate - path: main::idle, - resources: [OWNED, SHARED], - }, - - tasks: { - SYS_TICK: { - path: sys_tick, - // If omitted priority is assumed to be 1 - // priority: 1, - resources: [CO_OWNED, ON, SHARED], - }, - - TIM2: { - // Tasks are enabled, between `init` and `idle`, by default but they - // can start disabled if `false` is specified here - enabled: false, - path: tim2, - priority: 1, - resources: [CO_OWNED], - }, - }, -} - -mod main { - use rtfm::{self, Resource, Threshold}; - - pub fn init(_p: ::init::Peripherals, _r: ::init::Resources) {} - - pub fn idle(t: &mut Threshold, mut r: ::idle::Resources) -> ! { - loop { - *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); - } - } - } -} - -fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) { - *r.ON = !*r.ON; - - *r.CO_OWNED += 1; -} - -fn tim2(_t: &mut Threshold, mut r: TIM2::Resources) { - *r.CO_OWNED += 1; -} diff --git a/examples/generics.rs b/examples/generics.rs deleted file mode 100644 index aceba1a..0000000 --- a/examples/generics.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! Working with resources in a generic fashion -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_std] - -extern crate cortex_m_rtfm as rtfm; -extern crate stm32f103xx; - -use rtfm::{app, Resource, Threshold}; -use stm32f103xx::{GPIOA, SPI1}; - -app! { - device: stm32f103xx, - - resources: { - static GPIOA: GPIOA; - static SPI1: SPI1; - }, - - tasks: { - EXTI0: { - path: exti0, - priority: 1, - resources: [GPIOA, SPI1], - }, - - EXTI1: { - path: exti1, - priority: 2, - resources: [GPIOA, SPI1], - }, - }, -} - -fn init(p: init::Peripherals) -> init::LateResources { - init::LateResources { - GPIOA: p.device.GPIOA, - SPI1: p.device.SPI1, - } -} - -fn idle() -> ! { - loop { - rtfm::wfi(); - } -} - -// A generic function that uses some resources -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 - }); -} - -// This task needs critical sections to access the resources -fn exti0(t: &mut Threshold, r: EXTI0::Resources) { - work(t, &r.GPIOA, &r.SPI1); -} - -// 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/idle.rs b/examples/idle.rs new file mode 100644 index 0000000..013ccce --- /dev/null +++ b/examples/idle.rs @@ -0,0 +1,43 @@ +//! examples/idle.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + #[init] + fn init() { + println!("init"); + } + + #[idle] + fn idle() -> ! { + static mut X: u32 = 0; + + // Safe access to local `static mut` variable + let _x: &'static mut u32 = X; + + println!("idle"); + + debug::exit(debug::EXIT_SUCCESS); + + loop {} + } +}; diff --git a/examples/init.rs b/examples/init.rs new file mode 100644 index 0000000..d6caa60 --- /dev/null +++ b/examples/init.rs @@ -0,0 +1,44 @@ +//! examples/init.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use rtfm::app; + +// NOTE: This convenience macro will appear in all the other examples and +// will always look the same +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + #[init] + fn init() { + static mut X: u32 = 0; + + // Cortex-M peripherals + let _core: rtfm::Peripherals = core; + + // Device specific peripherals + let _device: lm3s6965::Peripherals = device; + + // Safe access to local `static mut` variable + let _x: &'static mut u32 = X; + + println!("init"); + + debug::exit(debug::EXIT_SUCCESS); + } +}; diff --git a/examples/interrupt.rs b/examples/interrupt.rs new file mode 100644 index 0000000..19b1fed --- /dev/null +++ b/examples/interrupt.rs @@ -0,0 +1,61 @@ +//! examples/interrupt.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use lm3s6965::Interrupt; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + #[init] + fn init() { + // Pends the UART0 interrupt but its handler won't run until *after* + // `init` returns because interrupts are disabled + rtfm::pend(Interrupt::UART0); + + println!("init"); + } + + #[idle] + fn idle() -> ! { + // interrupts are enabled again; the `UART0` handler runs at this point + + println!("idle"); + + rtfm::pend(Interrupt::UART0); + + debug::exit(debug::EXIT_SUCCESS); + + loop {} + } + + #[interrupt] + fn UART0() { + static mut TIMES: u32 = 0; + + // Safe access to local `static mut` variable + *TIMES += 1; + + println!( + "UART0 called {} time{}", + *TIMES, + if *TIMES > 1 { "s" } else { "" } + ); + } +}; diff --git a/examples/late-resources.rs b/examples/late-resources.rs deleted file mode 100644 index 3bfc388..0000000 --- a/examples/late-resources.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! Demonstrates initialization of resources in `init`. -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_std] - -extern crate cortex_m_rtfm as rtfm; -extern crate stm32f103xx; - -use rtfm::{app, Threshold}; - -app! { - device: stm32f103xx, - - resources: { - // Usually, resources are initialized with a constant initializer: - static ON: bool = false; - - // However, there are cases where this is not possible or not desired. - // For example, there may not be a sensible value to use, or the type may - // not be constructible in a constant (like `Vec`). - // - // While it is possible to use an `Option` in some cases, that requires - // you to properly initialize it and `.unwrap()` it at every use. It - // also consumes more memory. - // - // To solve this, it is possible to defer initialization of resources to - // `init` by omitting the initializer. Doing that will require `init` to - // return the values of all "late" resources. - static IP_ADDRESS: u32; - - // PORT is used by 2 tasks, making it a shared resource. This just tests - // another internal code path and is not important for the example. - static PORT: u16; - }, - - idle: { - // Test that late resources can be used in idle - resources: [IP_ADDRESS], - }, - - tasks: { - SYS_TICK: { - priority: 1, - path: sys_tick, - resources: [IP_ADDRESS, PORT, ON], - }, - - EXTI0: { - priority: 2, - path: exti0, - resources: [PORT], - } - } -} - -// The signature of `init` is now required to have a specific return type. -fn init(_p: init::Peripherals, _r: init::Resources) -> init::LateResources { - // `init::Resources` does not contain `IP_ADDRESS`, since it is not yet - // initialized. - //_r.IP_ADDRESS; // doesn't compile - - // ...obtain value for IP_ADDRESS from EEPROM/DHCP... - let ip_address = 0x7f000001; - - init::LateResources { - // This struct will contain fields for all resources with omitted - // initializers. - IP_ADDRESS: ip_address, - PORT: 0, - } -} - -fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { - // Other tasks can access late resources like any other, since they are - // guaranteed to be initialized when tasks are run. - - r.IP_ADDRESS; -} - -fn exti0(_t: &mut Threshold, _r: EXTI0::Resources) {} - -fn idle(_t: &mut Threshold, _r: idle::Resources) -> ! { - loop { - rtfm::wfi(); - } -} diff --git a/examples/late.rs b/examples/late.rs new file mode 100644 index 0000000..6d76c58 --- /dev/null +++ b/examples/late.rs @@ -0,0 +1,65 @@ +//! examples/late.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use heapless::{ + consts::*, + spsc::{Consumer, Producer, Queue}, +}; +use lm3s6965::Interrupt; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + // Late resources + static mut P: Producer<'static, u32, U4> = (); + static mut C: Consumer<'static, u32, U4> = (); + + #[init] + fn init() { + // NOTE: we use `Option` here to work around the lack of + // a stable `const` constructor + static mut Q: Option<Queue<u32, U4>> = None; + + *Q = Some(Queue::new()); + let (p, c) = Q.as_mut().unwrap().split(); + + // Initialization of late resources + P = p; + C = c; + } + + #[idle(resources = [C])] + fn idle() -> ! { + loop { + if let Some(byte) = resources.C.dequeue() { + println!("received message: {}", byte); + + debug::exit(debug::EXIT_SUCCESS); + } else { + rtfm::pend(Interrupt::UART0); + } + } + } + + #[interrupt(resources = [P])] + fn UART0() { + resources.P.enqueue(42).unwrap(); + } +}; diff --git a/examples/lock.rs b/examples/lock.rs new file mode 100644 index 0000000..097bd5c --- /dev/null +++ b/examples/lock.rs @@ -0,0 +1,71 @@ +//! examples/lock.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use lm3s6965::Interrupt; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + static mut SHARED: u32 = 0; + + #[init] + fn init() { + rtfm::pend(Interrupt::GPIOA); + } + + // when omitted priority is assumed to be `1` + #[interrupt(resources = [SHARED])] + fn GPIOA() { + println!("A"); + + // the lower priority task requires a critical section to access the data + resources.SHARED.lock(|shared| { + // data can only be modified within this critical section (closure) + *shared += 1; + + // GPIOB will *not* run right now due to the critical section + rtfm::pend(Interrupt::GPIOB); + + println!("B - SHARED = {}", *shared); + + // GPIOC does not contend for `SHARED` so it's allowed to run now + rtfm::pend(Interrupt::GPIOC); + }); + + // critical section is over: GPIOB can now start + + println!("E"); + + debug::exit(debug::EXIT_SUCCESS); + } + + #[interrupt(priority = 2, resources = [SHARED])] + fn GPIOB() { + // the higher priority task does *not* need a critical section + *resources.SHARED += 1; + + println!("D - SHARED = {}", *resources.SHARED); + } + + #[interrupt(priority = 3)] + fn GPIOC() { + println!("C"); + } +}; diff --git a/examples/message.rs b/examples/message.rs new file mode 100644 index 0000000..1ff08b2 --- /dev/null +++ b/examples/message.rs @@ -0,0 +1,61 @@ +//! examples/message.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + #[init(spawn = [foo])] + fn init() { + spawn.foo(/* no message */).unwrap(); + } + + #[task(spawn = [bar])] + fn foo() { + static mut COUNT: u32 = 0; + + println!("foo"); + + spawn.bar(*COUNT).unwrap(); + *COUNT += 1; + } + + #[task(spawn = [baz])] + fn bar(x: u32) { + println!("bar({})", x); + + spawn.baz(x + 1, x + 2).unwrap(); + } + + #[task(spawn = [foo])] + fn baz(x: u32, y: u32) { + println!("baz({}, {})", x, y); + + if x + y > 4 { + debug::exit(debug::EXIT_SUCCESS); + } + + spawn.foo().unwrap(); + } + + extern "C" { + fn UART0(); + } +}; diff --git a/examples/nested.rs b/examples/nested.rs deleted file mode 100644 index 46c00b2..0000000 --- a/examples/nested.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! 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)] -#![deny(warnings)] -#![no_std] - -extern crate cortex_m_rtfm as rtfm; -extern crate stm32f103xx; - -use rtfm::{app, Resource, Threshold}; -use stm32f103xx::Interrupt; - -app! { - device: stm32f103xx, - - resources: { - static LOW: u64 = 0; - static HIGH: u64 = 0; - }, - - tasks: { - EXTI0: { - path: exti0, - priority: 1, - resources: [LOW, HIGH], - }, - - EXTI1: { - path: exti1, - priority: 2, - resources: [LOW], - }, - - EXTI2: { - path: exti2, - priority: 3, - resources: [HIGH], - }, - }, -} - -fn init(_p: init::Peripherals, _r: init::Resources) {} - -fn idle() -> ! { - // A - rtfm::bkpt(); - - // 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(); - } -} - -#[allow(non_snake_case)] -fn exti0( - t: &mut Threshold, - EXTI0::Resources { - LOW: mut low, - HIGH: mut high, - }: EXTI0::Resources, -) { - // Because this task has a priority of 1 the preemption threshold `t` also - // starts at 1 - - // B - 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 - low.claim_mut(t, |_low, t| { - // This claim increases the preemption threshold to 2 - // - // 2 is just high enough to not race with task `exti1` for access to the - // `LOW` resource - - // D - 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 - - // F - rtfm::bkpt(); - - // Claims can be nested - 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); - - // G - rtfm::bkpt(); - }); - - // Upon leaving the critical section the preemption threshold drops back - // to 2 and `exti2` immediately preempts this task - // ~> exti2 - }); - - // Once again the preemption threshold drops but this time to 1. Now the - // pending `exti1` task can preempt this task - // ~> exti1 -} - -fn exti1(_t: &mut Threshold, _r: EXTI1::Resources) { - // C, I - rtfm::bkpt(); -} - -fn exti2(_t: &mut Threshold, _r: EXTI2::Resources) { - // E, H - rtfm::bkpt(); -} diff --git a/examples/not-send.rs b/examples/not-send.rs new file mode 100644 index 0000000..be78c33 --- /dev/null +++ b/examples/not-send.rs @@ -0,0 +1,58 @@ +//! `examples/not-send.rs` + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_halt; + +use core::marker::PhantomData; + +use cortex_m_semihosting::debug; +use rtfm::app; + +pub struct NotSend { + _0: PhantomData<*const ()>, +} + +#[app(device = lm3s6965)] +const APP: () = { + static mut SHARED: Option<NotSend> = None; + + #[init(spawn = [baz, quux])] + fn init() { + spawn.baz().unwrap(); + spawn.quux().unwrap(); + } + + #[task(spawn = [bar])] + fn foo() { + // scenario 1: message passed to task that runs at the same priority + spawn.bar(NotSend { _0: PhantomData }).ok(); + } + + #[task] + fn bar(_x: NotSend) { + // scenario 1 + } + + #[task(priority = 2, resources = [SHARED])] + fn baz() { + // scenario 2: resource shared between tasks that run at the same priority + *resources.SHARED = Some(NotSend { _0: PhantomData }); + } + + #[task(priority = 2, resources = [SHARED])] + fn quux() { + // scenario 2 + let _not_send = resources.SHARED.take().unwrap(); + + debug::exit(debug::EXIT_SUCCESS); + } + + extern "C" { + fn UART0(); + fn UART1(); + } +}; diff --git a/examples/not-sync.rs b/examples/not-sync.rs new file mode 100644 index 0000000..d94e0a0 --- /dev/null +++ b/examples/not-sync.rs @@ -0,0 +1,41 @@ +//! `examples/not-sync.rs` + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_halt; + +use core::marker::PhantomData; + +use cortex_m_semihosting::debug; +use rtfm::app; + +pub struct NotSync { + _0: PhantomData<*const ()>, +} + +#[app(device = lm3s6965)] +const APP: () = { + static SHARED: NotSync = NotSync { _0: PhantomData }; + + #[init] + fn init() { + debug::exit(debug::EXIT_SUCCESS); + } + + #[task(resources = [SHARED])] + fn foo() { + let _: &NotSync = resources.SHARED; + } + + #[task(resources = [SHARED])] + fn bar() { + let _: &NotSync = resources.SHARED; + } + + extern "C" { + fn UART0(); + } +}; diff --git a/examples/one-task.rs b/examples/one-task.rs deleted file mode 100644 index dc2bfd2..0000000 --- a/examples/one-task.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! An application with one task -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_std] - -extern crate cortex_m; -extern crate cortex_m_rtfm as rtfm; -extern crate stm32f103xx; - -use cortex_m::peripheral::syst::SystClkSource; -use rtfm::{app, Threshold}; -use stm32f103xx::GPIOC; - -app! { - device: stm32f103xx, - - // Here data resources are declared - // - // Data resources are static variables that are safe to share across tasks - resources: { - // Declaration of resources looks exactly like declaration of static - // variables - static ON: bool = false; - }, - - // 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: { - // Path to the task handler - path: sys_tick, - - // These are the resources this task has access to. - // - // The resources listed here must also appear in `app.resources` - resources: [ON], - }, - } -} - -fn init(mut p: init::Peripherals, r: init::Resources) { - // `init` can modify all the `resources` declared in `app!` - r.ON; - - // power on GPIOC - p.device.RCC.apb2enr.modify(|_, w| w.iopcen().enabled()); - - // configure PC13 as output - p.device.GPIOC.bsrr.write(|w| w.bs13().set()); - p.device - .GPIOC - .crh - .modify(|_, w| w.mode13().output().cnf13().push()); - - // configure the system timer to generate one interrupt every second - p.core.SYST.set_clock_source(SystClkSource::Core); - p.core.SYST.set_reload(8_000_000); // 1s - p.core.SYST.enable_interrupt(); - p.core.SYST.enable_counter(); -} - -fn idle() -> ! { - loop { - rtfm::wfi(); - } -} - -// This is the task handler of the SYS_TICK exception -// -// `_t` is the preemption threshold token. We won't use it in this program. -// -// `r` is the set of resources this task has access to. `SYS_TICK::Resources` -// has one field per resource declared in `app!`. -#[allow(unsafe_code)] -fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) { - // toggle state - *r.ON = !*r.ON; - - if *r.ON { - // set the pin PC13 high - // NOTE(unsafe) atomic write to a stateless register - unsafe { - (*GPIOC::ptr()).bsrr.write(|w| w.bs13().set()); - } - } else { - // set the pin PC13 low - // NOTE(unsafe) atomic write to a stateless register - unsafe { - (*GPIOC::ptr()).bsrr.write(|w| w.br13().reset()); - } - } -} diff --git a/examples/periodic.rs b/examples/periodic.rs new file mode 100644 index 0000000..0fb8bdf --- /dev/null +++ b/examples/periodic.rs @@ -0,0 +1,43 @@ +//! examples/periodic.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use rtfm::{app, Instant}; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +const PERIOD: u32 = 8_000_000; + +// NOTE: does NOT work on QEMU! +#[app(device = lm3s6965)] +const APP: () = { + #[init(schedule = [foo])] + fn init() { + schedule.foo(Instant::now() + PERIOD.cycles()).unwrap(); + } + + #[task(schedule = [foo])] + fn foo() { + let now = Instant::now(); + println!("foo(scheduled = {:?}, now = {:?})", scheduled, now); + + schedule.foo(scheduled + PERIOD.cycles()).unwrap(); + } + + extern "C" { + fn UART0(); + } +}; diff --git a/examples/preemption.rs b/examples/preemption.rs deleted file mode 100644 index 340b976..0000000 --- a/examples/preemption.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! Two tasks running at *different* priorities with access to the same resource -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_std] - -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 `SYS_TICK` task has higher priority than `TIM2` - SYS_TICK: { - path: sys_tick, - priority: 2, - resources: [COUNTER], - }, - - TIM2: { - path: tim2, - priority: 1, - resources: [COUNTER], - }, - }, -} - -fn init(_p: init::Peripherals, _r: init::Resources) { - // .. -} - -fn idle() -> ! { - loop { - rtfm::wfi(); - } -} - -fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) { - // .. - - // This task can't be preempted by `tim2` so it has direct access to the - // resource data - *r.COUNTER += 1; - - // .. -} - -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 undefined behavior. - r.COUNTER.claim_mut(t, |counter, _t| { - // `claim_mut` creates a critical section - *counter += 1; - }); - - // .. -} diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs new file mode 100644 index 0000000..b7fe252 --- /dev/null +++ b/examples/ramfunc.rs @@ -0,0 +1,53 @@ +//! examples/ramfunc.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + #[init(spawn = [bar])] + fn init() { + spawn.bar().unwrap(); + } + + #[inline(never)] + #[task] + fn foo() { + println!("foo"); + + debug::exit(debug::EXIT_SUCCESS); + } + + // run this task from RAM + #[inline(never)] + #[link_section = ".data.bar"] + #[task(priority = 2, spawn = [foo])] + fn bar() { + spawn.foo().unwrap(); + } + + extern "C" { + fn UART0(); + + // run the task dispatcher from RAM + #[link_section = ".data.UART1"] + fn UART1(); + } +}; diff --git a/examples/resource.rs b/examples/resource.rs new file mode 100644 index 0000000..2777da1 --- /dev/null +++ b/examples/resource.rs @@ -0,0 +1,60 @@ +//! examples/resource.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use lm3s6965::Interrupt; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + // A resource + static mut SHARED: u32 = 0; + + #[init] + fn init() { + rtfm::pend(Interrupt::UART0); + rtfm::pend(Interrupt::UART1); + } + + #[idle] + fn idle() -> ! { + debug::exit(debug::EXIT_SUCCESS); + + // error: `SHARED` can't be accessed from this context + // SHARED += 1; + + loop {} + } + + // `SHARED` can be access from this context + #[interrupt(resources = [SHARED])] + fn UART0() { + *resources.SHARED += 1; + + println!("UART0: SHARED = {}", resources.SHARED); + } + + // `SHARED` can be access from this context + #[interrupt(resources = [SHARED])] + fn UART1() { + *resources.SHARED += 1; + + println!("UART1: SHARED = {}", resources.SHARED); + } +}; diff --git a/examples/safe-static-mut-ref.rs b/examples/safe-static-mut-ref.rs deleted file mode 100644 index 9579f52..0000000 --- a/examples/safe-static-mut-ref.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! Safe creation of `&'static mut` references -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_std] - -extern crate cortex_m_rtfm as rtfm; -extern crate stm32f103xx; - -use rtfm::app; - -app! { - device: stm32f103xx, - - resources: { - static BUFFER: [u8; 16] = [0; 16]; - }, - - init: { - resources: [BUFFER], - }, -} - -fn init(_p: init::Peripherals, r: init::Resources) { - let _buf: &'static mut [u8; 16] = r.BUFFER; -} - -fn idle() -> ! { - loop { - rtfm::wfi(); - } -} diff --git a/examples/schedule.rs b/examples/schedule.rs new file mode 100644 index 0000000..9fb2796 --- /dev/null +++ b/examples/schedule.rs @@ -0,0 +1,51 @@ +//! examples/schedule.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use rtfm::{app, Instant}; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +// NOTE: does NOT work on QEMU! +#[app(device = lm3s6965)] +const APP: () = { + #[init(schedule = [foo, bar])] + fn init() { + let now = Instant::now(); + + println!("init @ {:?}", now); + + // Schedule `foo` to run 8e6 cycles (clock cycles) in the future + schedule.foo(now + 8_000_000.cycles()).unwrap(); + + // Schedule `bar` to run 4e6 cycles in the future + schedule.bar(now + 4_000_000.cycles()).unwrap(); + } + + #[task] + fn foo() { + println!("foo @ {:?}", Instant::now()); + } + + #[task] + fn bar() { + println!("bar @ {:?}", Instant::now()); + } + + extern "C" { + fn UART0(); + } +}; diff --git a/examples/singleton.rs b/examples/singleton.rs new file mode 100644 index 0000000..888a5a6 --- /dev/null +++ b/examples/singleton.rs @@ -0,0 +1,69 @@ +//! examples/singleton.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use alloc_singleton::stable::pool::{Box, Pool}; +use cortex_m_semihosting::debug; +use lm3s6965::Interrupt; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + #[Singleton(Send)] + static mut M: [u32; 2] = [0; 2]; + + static mut P: Pool<M> = (); + + #[init(resources = [M])] + fn init() { + rtfm::pend(Interrupt::I2C0); + + P = Pool::new(resources.M); + } + + #[interrupt( + priority = 2, + resources = [P], + spawn = [foo, bar], + )] + fn I2C0() { + spawn.foo(resources.P.alloc(1).unwrap()).unwrap(); + spawn.bar(resources.P.alloc(2).unwrap()).unwrap(); + } + + #[task(resources = [P])] + fn foo(x: Box<M>) { + println!("foo({})", x); + + resources.P.lock(|p| p.dealloc(x)); + + debug::exit(debug::EXIT_SUCCESS); + } + + #[task(priority = 2, resources = [P])] + fn bar(x: Box<M>) { + println!("bar({})", x); + + resources.P.dealloc(x); + } + + extern "C" { + fn UART0(); + fn UART1(); + } +}; diff --git a/examples/smallest.rs b/examples/smallest.rs new file mode 100644 index 0000000..e4d86be --- /dev/null +++ b/examples/smallest.rs @@ -0,0 +1,17 @@ +//! examples/smallest.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +// panic-handler crate +extern crate panic_semihosting; + +use rtfm::app; + +#[app(device = lm3s6965)] +const APP: () = { + #[init] + fn init() {} +}; diff --git a/examples/static.rs b/examples/static.rs new file mode 100644 index 0000000..3dc0e89 --- /dev/null +++ b/examples/static.rs @@ -0,0 +1,47 @@ +//! examples/static.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use lm3s6965::Interrupt; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + static KEY: u32 = (); + + #[init] + fn init() { + rtfm::pend(Interrupt::UART0); + rtfm::pend(Interrupt::UART1); + + KEY = 0xdeadbeef; + } + + #[interrupt(resources = [KEY])] + fn UART0() { + println!("UART0(KEY = {:#x})", resources.KEY); + + debug::exit(debug::EXIT_SUCCESS); + } + + #[interrupt(priority = 2, resources = [KEY])] + fn UART1() { + println!("UART1(KEY = {:#x})", resources.KEY); + } +}; diff --git a/examples/task.rs b/examples/task.rs new file mode 100644 index 0000000..b1cd7ae --- /dev/null +++ b/examples/task.rs @@ -0,0 +1,61 @@ +//! examples/task.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use rtfm::app; + +macro_rules! println { + ($($tt:tt)*) => { + if let Ok(mut stdout) = cortex_m_semihosting::hio::hstdout() { + use core::fmt::Write; + + writeln!(stdout, $($tt)*).ok(); + } + }; +} + +#[app(device = lm3s6965)] +const APP: () = { + #[init(spawn = [foo])] + fn init() { + spawn.foo().unwrap(); + } + + #[task(spawn = [bar, baz])] + fn foo() { + println!("foo"); + + // spawns `bar` onto the task scheduler + // `foo` and `bar` have the same priority so `bar` will not run until + // after `foo` terminates + spawn.bar().unwrap(); + + // spawns `baz` onto the task scheduler + // `baz` has higher priority than `foo` so it immediately preempts `foo` + spawn.baz().unwrap(); + } + + #[task] + fn bar() { + println!("bar"); + + debug::exit(debug::EXIT_SUCCESS); + } + + #[task(priority = 2)] + fn baz() { + println!("baz"); + } + + // Interrupt handlers used to dispatch software tasks + extern "C" { + fn UART0(); + fn UART1(); + } +}; diff --git a/examples/two-tasks.rs b/examples/two-tasks.rs deleted file mode 100644 index 2348915..0000000 --- a/examples/two-tasks.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! Two tasks running at the *same* priority with access to the same resource -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_std] - -extern crate cortex_m_rtfm as rtfm; -extern crate stm32f103xx; - -use rtfm::{app, Threshold}; - -app! { - device: stm32f103xx, - - resources: { - static COUNTER: u64 = 0; - }, - - // Both SYS_TICK and TIM2 have access to the `COUNTER` data - tasks: { - SYS_TICK: { - path: sys_tick, - resources: [COUNTER], - }, - - TIM2: { - path: tim2, - resources: [COUNTER], - }, - }, -} - -fn init(_p: init::Peripherals, _r: init::Resources) { - // .. -} - -fn idle() -> ! { - loop { - rtfm::wfi(); - } -} - -// 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, mut r: SYS_TICK::Resources) { - // .. - - *r.COUNTER += 1; - - // .. -} - -fn tim2(_t: &mut Threshold, mut r: TIM2::Resources) { - // .. - - *r.COUNTER += 1; - - // .. -} diff --git a/examples/types.rs b/examples/types.rs new file mode 100644 index 0000000..6606208 --- /dev/null +++ b/examples/types.rs @@ -0,0 +1,54 @@ +//! examples/types.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::debug; +use rtfm::{app, Instant}; + +#[app(device = lm3s6965)] +const APP: () = { + static mut SHARED: u32 = 0; + + #[init(schedule = [foo], spawn = [foo])] + fn init() { + let _: Instant = start; + let _: rtfm::Peripherals = core; + let _: lm3s6965::Peripherals = device; + let _: init::Schedule = schedule; + let _: init::Spawn = spawn; + + debug::exit(debug::EXIT_SUCCESS); + } + + #[exception(schedule = [foo], spawn = [foo])] + fn SVCall() { + let _: Instant = start; + let _: SVCall::Schedule = schedule; + let _: SVCall::Spawn = spawn; + } + + #[interrupt(resources = [SHARED], schedule = [foo], spawn = [foo])] + fn UART0() { + let _: Instant = start; + let _: resources::SHARED = resources.SHARED; + let _: UART0::Schedule = schedule; + let _: UART0::Spawn = spawn; + } + + #[task(priority = 2, resources = [SHARED], schedule = [foo], spawn = [foo])] + fn foo() { + let _: Instant = scheduled; + let _: foo::Resources = resources; + let _: foo::Schedule = schedule; + let _: foo::Spawn = spawn; + } + + extern "C" { + fn UART1(); + } +}; diff --git a/examples/zero-tasks.rs b/examples/zero-tasks.rs deleted file mode 100644 index abd1c4c..0000000 --- a/examples/zero-tasks.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Minimal example with zero tasks -#![deny(unsafe_code)] -#![deny(warnings)] -#![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 the 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.core.SYST; - p.device.GPIOA; - p.device.RCC; - // .. -} - -// The idle loop. -// -// This runs after `init` 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() -> ! { - loop { - // This puts the processor to sleep until there's a task to service - rtfm::wfi(); - } -} |
