aboutsummaryrefslogtreecommitdiff
path: root/embassy-cortex-m/src/executor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-cortex-m/src/executor.rs')
-rw-r--r--embassy-cortex-m/src/executor.rs89
1 files changed, 89 insertions, 0 deletions
diff --git a/embassy-cortex-m/src/executor.rs b/embassy-cortex-m/src/executor.rs
new file mode 100644
index 000000000..63a1519cf
--- /dev/null
+++ b/embassy-cortex-m/src/executor.rs
@@ -0,0 +1,89 @@
1use core::marker::PhantomData;
2
3use crate::interrupt::{Interrupt, InterruptExt};
4use embassy::executor::{raw, SendSpawner};
5
6pub use embassy::executor::Executor;
7
8fn pend_by_number(n: u16) {
9 #[derive(Clone, Copy)]
10 struct N(u16);
11 unsafe impl cortex_m::interrupt::InterruptNumber for N {
12 fn number(self) -> u16 {
13 self.0
14 }
15 }
16 cortex_m::peripheral::NVIC::pend(N(n))
17}
18
19/// Interrupt mode executor.
20///
21/// This executor runs tasks in interrupt mode. The interrupt handler is set up
22/// to poll tasks, and when a task is woken the interrupt is pended from software.
23///
24/// This allows running async tasks at a priority higher than thread mode. One
25/// use case is to leave thread mode free for non-async tasks. Another use case is
26/// to run multiple executors: one in thread mode for low priority tasks and another in
27/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
28/// priority ones.
29///
30/// It is even possible to run multiple interrupt mode executors at different priorities,
31/// by assigning different priorities to the interrupts. For an example on how to do this,
32/// See the 'multiprio' example for 'embassy-nrf'.
33///
34/// To use it, you have to pick an interrupt that won't be used by the hardware.
35/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
36/// If this is not the case, you may use an interrupt from any unused peripheral.
37///
38/// It is somewhat more complex to use, it's recommended to use the thread-mode
39/// [`Executor`] instead, if it works for your use case.
40pub struct InterruptExecutor<I: Interrupt> {
41 irq: I,
42 inner: raw::Executor,
43 not_send: PhantomData<*mut ()>,
44}
45
46impl<I: Interrupt> InterruptExecutor<I> {
47 /// Create a new Executor.
48 pub fn new(irq: I) -> Self {
49 let ctx = irq.number() as *mut ();
50 Self {
51 irq,
52 inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx),
53 not_send: PhantomData,
54 }
55 }
56
57 /// Start the executor.
58 ///
59 /// This initializes the executor, configures and enables the interrupt, and returns.
60 /// The executor keeps running in the background through the interrupt.
61 ///
62 /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
63 /// is returned instead of a [`Spawner`] because the executor effectively runs in a
64 /// different "thread" (the interrupt), so spawning tasks on it is effectively
65 /// sending them.
66 ///
67 /// To obtain a [`Spawner`] for this executor, use [`Spawner::for_current_executor`] from
68 /// a task running in it.
69 ///
70 /// This function requires `&'static mut self`. This means you have to store the
71 /// Executor instance in a place where it'll live forever and grants you mutable
72 /// access. There's a few ways to do this:
73 ///
74 /// - a [Forever](crate::util::Forever) (safe)
75 /// - a `static mut` (unsafe)
76 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
77 pub fn start(&'static mut self) -> SendSpawner {
78 self.irq.disable();
79
80 self.irq.set_handler(|ctx| unsafe {
81 let executor = &*(ctx as *const raw::Executor);
82 executor.poll();
83 });
84 self.irq.set_handler_context(&self.inner as *const _ as _);
85 self.irq.enable();
86
87 self.inner.spawner().make_send()
88 }
89}