diff options
Diffstat (limited to 'embassy-cortex-m/src/interrupt.rs')
| -rw-r--r-- | embassy-cortex-m/src/interrupt.rs | 214 |
1 files changed, 151 insertions, 63 deletions
diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-cortex-m/src/interrupt.rs index 0e790eaaf..e9fa43e87 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-cortex-m/src/interrupt.rs | |||
| @@ -2,120 +2,208 @@ | |||
| 2 | use core::mem; | 2 | use core::mem; |
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | 3 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 4 | 4 | ||
| 5 | use cortex_m::interrupt::InterruptNumber; | ||
| 5 | use cortex_m::peripheral::NVIC; | 6 | use cortex_m::peripheral::NVIC; |
| 6 | 7 | ||
| 7 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. | 8 | /// Generate a standard `mod interrupt` for a HAL. |
| 8 | #[doc(hidden)] | 9 | #[macro_export] |
| 9 | pub mod _export { | 10 | macro_rules! interrupt_mod { |
| 10 | pub use atomic_polyfill as atomic; | 11 | ($($irqs:ident),* $(,)?) => { |
| 11 | pub use embassy_macros::{cortex_m_interrupt as interrupt, cortex_m_interrupt_declare as declare}; | 12 | #[cfg(feature = "rt")] |
| 12 | } | 13 | pub use cortex_m_rt::interrupt; |
| 13 | 14 | ||
| 14 | /// Interrupt handler trait. | 15 | /// Interrupt definitions. |
| 15 | /// | 16 | pub mod interrupt { |
| 16 | /// Drivers that need to handle interrupts implement this trait. | 17 | pub use embassy_cortex_m::interrupt::{InterruptExt, Priority}; |
| 17 | /// The user must ensure `on_interrupt()` is called every time the interrupt fires. | 18 | pub use crate::pac::Interrupt::*; |
| 18 | /// Drivers must use use [`Binding`] to assert at compile time that the user has done so. | 19 | pub use crate::pac::Interrupt; |
| 19 | pub trait Handler<I: Interrupt> { | ||
| 20 | /// Interrupt handler function. | ||
| 21 | /// | ||
| 22 | /// Must be called every time the `I` interrupt fires, synchronously from | ||
| 23 | /// the interrupt handler context. | ||
| 24 | /// | ||
| 25 | /// # Safety | ||
| 26 | /// | ||
| 27 | /// This function must ONLY be called from the interrupt handler for `I`. | ||
| 28 | unsafe fn on_interrupt(); | ||
| 29 | } | ||
| 30 | 20 | ||
| 31 | /// Compile-time assertion that an interrupt has been bound to a handler. | 21 | /// Type-level interrupt infrastructure. |
| 32 | /// | 22 | /// |
| 33 | /// For the vast majority of cases, you should use the `bind_interrupts!` | 23 | /// This module contains one *type* per interrupt. This is used for checking at compile time that |
| 34 | /// macro instead of writing `unsafe impl`s of this trait. | 24 | /// the interrupts are correctly bound to HAL drivers. |
| 35 | /// | 25 | /// |
| 36 | /// # Safety | 26 | /// As an end user, you shouldn't need to use this module directly. Use the [`crate::bind_interrupts!`] macro |
| 37 | /// | 27 | /// to bind interrupts, and the [`crate::interrupt`] module to manually register interrupt handlers and manipulate |
| 38 | /// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()` | 28 | /// interrupts directly (pending/unpending, enabling/disabling, setting the priority, etc...) |
| 39 | /// to be called every time the `I` interrupt fires. | 29 | pub mod typelevel { |
| 40 | /// | 30 | use super::InterruptExt; |
| 41 | /// This allows drivers to check bindings at compile-time. | 31 | |
| 42 | pub unsafe trait Binding<I: Interrupt, H: Handler<I>> {} | 32 | mod sealed { |
| 43 | 33 | pub trait Interrupt {} | |
| 44 | #[derive(Clone, Copy)] | 34 | } |
| 45 | pub(crate) struct NrWrap(pub(crate) u16); | 35 | |
| 46 | unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap { | 36 | /// Type-level interrupt. |
| 47 | fn number(self) -> u16 { | 37 | /// |
| 48 | self.0 | 38 | /// This trait is implemented for all typelevel interrupt types in this module. |
| 49 | } | 39 | pub trait Interrupt: sealed::Interrupt { |
| 40 | |||
| 41 | /// Interrupt enum variant. | ||
| 42 | /// | ||
| 43 | /// This allows going from typelevel interrupts (one type per interrupt) to | ||
| 44 | /// non-typelevel interrupts (a single `Interrupt` enum type, with one variant per interrupt). | ||
| 45 | const IRQ: super::Interrupt; | ||
| 46 | |||
| 47 | /// Enable the interrupt. | ||
| 48 | #[inline] | ||
| 49 | unsafe fn enable() { | ||
| 50 | Self::IRQ.enable() | ||
| 51 | } | ||
| 52 | |||
| 53 | /// Disable the interrupt. | ||
| 54 | #[inline] | ||
| 55 | fn disable() { | ||
| 56 | Self::IRQ.disable() | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Check if interrupt is enabled. | ||
| 60 | #[inline] | ||
| 61 | fn is_enabled() -> bool { | ||
| 62 | Self::IRQ.is_enabled() | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Check if interrupt is pending. | ||
| 66 | #[inline] | ||
| 67 | fn is_pending() -> bool { | ||
| 68 | Self::IRQ.is_pending() | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Set interrupt pending. | ||
| 72 | #[inline] | ||
| 73 | fn pend() { | ||
| 74 | Self::IRQ.pend() | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Unset interrupt pending. | ||
| 78 | #[inline] | ||
| 79 | fn unpend() { | ||
| 80 | Self::IRQ.unpend() | ||
| 81 | } | ||
| 82 | |||
| 83 | /// Get the priority of the interrupt. | ||
| 84 | #[inline] | ||
| 85 | fn get_priority() -> crate::interrupt::Priority { | ||
| 86 | Self::IRQ.get_priority() | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Set the interrupt priority. | ||
| 90 | #[inline] | ||
| 91 | fn set_priority(prio: crate::interrupt::Priority) { | ||
| 92 | Self::IRQ.set_priority(prio) | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | $( | ||
| 97 | #[allow(non_camel_case_types)] | ||
| 98 | #[doc=stringify!($irqs)] | ||
| 99 | #[doc=" typelevel interrupt."] | ||
| 100 | pub enum $irqs {} | ||
| 101 | impl sealed::Interrupt for $irqs{} | ||
| 102 | impl Interrupt for $irqs { | ||
| 103 | const IRQ: super::Interrupt = super::Interrupt::$irqs; | ||
| 104 | } | ||
| 105 | )* | ||
| 106 | |||
| 107 | /// Interrupt handler trait. | ||
| 108 | /// | ||
| 109 | /// Drivers that need to handle interrupts implement this trait. | ||
| 110 | /// The user must ensure `on_interrupt()` is called every time the interrupt fires. | ||
| 111 | /// Drivers must use use [`Binding`] to assert at compile time that the user has done so. | ||
| 112 | pub trait Handler<I: Interrupt> { | ||
| 113 | /// Interrupt handler function. | ||
| 114 | /// | ||
| 115 | /// Must be called every time the `I` interrupt fires, synchronously from | ||
| 116 | /// the interrupt handler context. | ||
| 117 | /// | ||
| 118 | /// # Safety | ||
| 119 | /// | ||
| 120 | /// This function must ONLY be called from the interrupt handler for `I`. | ||
| 121 | unsafe fn on_interrupt(); | ||
| 122 | } | ||
| 123 | |||
| 124 | /// Compile-time assertion that an interrupt has been bound to a handler. | ||
| 125 | /// | ||
| 126 | /// For the vast majority of cases, you should use the `bind_interrupts!` | ||
| 127 | /// macro instead of writing `unsafe impl`s of this trait. | ||
| 128 | /// | ||
| 129 | /// # Safety | ||
| 130 | /// | ||
| 131 | /// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()` | ||
| 132 | /// to be called every time the `I` interrupt fires. | ||
| 133 | /// | ||
| 134 | /// This allows drivers to check bindings at compile-time. | ||
| 135 | pub unsafe trait Binding<I: Interrupt, H: Handler<I>> {} | ||
| 136 | } | ||
| 137 | } | ||
| 138 | }; | ||
| 50 | } | 139 | } |
| 51 | 140 | ||
| 52 | /// Represents an interrupt type that can be configured by embassy to handle | 141 | /// Represents an interrupt type that can be configured by embassy to handle |
| 53 | /// interrupts. | 142 | /// interrupts. |
| 54 | pub unsafe trait Interrupt { | 143 | pub unsafe trait InterruptExt: InterruptNumber + Copy { |
| 55 | /// Return the NVIC interrupt number for this interrupt. | ||
| 56 | fn number() -> u16; | ||
| 57 | |||
| 58 | /// Enable the interrupt. | 144 | /// Enable the interrupt. |
| 59 | #[inline] | 145 | #[inline] |
| 60 | unsafe fn enable() { | 146 | unsafe fn enable(self) { |
| 61 | compiler_fence(Ordering::SeqCst); | 147 | compiler_fence(Ordering::SeqCst); |
| 62 | NVIC::unmask(NrWrap(Self::number())) | 148 | NVIC::unmask(self) |
| 63 | } | 149 | } |
| 64 | 150 | ||
| 65 | /// Disable the interrupt. | 151 | /// Disable the interrupt. |
| 66 | #[inline] | 152 | #[inline] |
| 67 | fn disable() { | 153 | fn disable(self) { |
| 68 | NVIC::mask(NrWrap(Self::number())); | 154 | NVIC::mask(self); |
| 69 | compiler_fence(Ordering::SeqCst); | 155 | compiler_fence(Ordering::SeqCst); |
| 70 | } | 156 | } |
| 71 | 157 | ||
| 72 | /// Check if interrupt is being handled. | 158 | /// Check if interrupt is being handled. |
| 73 | #[inline] | 159 | #[inline] |
| 74 | #[cfg(not(armv6m))] | 160 | #[cfg(not(armv6m))] |
| 75 | fn is_active() -> bool { | 161 | fn is_active(self) -> bool { |
| 76 | NVIC::is_active(NrWrap(Self::number())) | 162 | NVIC::is_active(self) |
| 77 | } | 163 | } |
| 78 | 164 | ||
| 79 | /// Check if interrupt is enabled. | 165 | /// Check if interrupt is enabled. |
| 80 | #[inline] | 166 | #[inline] |
| 81 | fn is_enabled() -> bool { | 167 | fn is_enabled(self) -> bool { |
| 82 | NVIC::is_enabled(NrWrap(Self::number())) | 168 | NVIC::is_enabled(self) |
| 83 | } | 169 | } |
| 84 | 170 | ||
| 85 | /// Check if interrupt is pending. | 171 | /// Check if interrupt is pending. |
| 86 | #[inline] | 172 | #[inline] |
| 87 | fn is_pending() -> bool { | 173 | fn is_pending(self) -> bool { |
| 88 | NVIC::is_pending(NrWrap(Self::number())) | 174 | NVIC::is_pending(self) |
| 89 | } | 175 | } |
| 90 | 176 | ||
| 91 | /// Set interrupt pending. | 177 | /// Set interrupt pending. |
| 92 | #[inline] | 178 | #[inline] |
| 93 | fn pend() { | 179 | fn pend(self) { |
| 94 | NVIC::pend(NrWrap(Self::number())) | 180 | NVIC::pend(self) |
| 95 | } | 181 | } |
| 96 | 182 | ||
| 97 | /// Unset interrupt pending. | 183 | /// Unset interrupt pending. |
| 98 | #[inline] | 184 | #[inline] |
| 99 | fn unpend() { | 185 | fn unpend(self) { |
| 100 | NVIC::unpend(NrWrap(Self::number())) | 186 | NVIC::unpend(self) |
| 101 | } | 187 | } |
| 102 | 188 | ||
| 103 | /// Get the priority of the interrupt. | 189 | /// Get the priority of the interrupt. |
| 104 | #[inline] | 190 | #[inline] |
| 105 | fn get_priority() -> Priority { | 191 | fn get_priority(self) -> Priority { |
| 106 | Priority::from(NVIC::get_priority(NrWrap(Self::number()))) | 192 | Priority::from(NVIC::get_priority(self)) |
| 107 | } | 193 | } |
| 108 | 194 | ||
| 109 | /// Set the interrupt priority. | 195 | /// Set the interrupt priority. |
| 110 | #[inline] | 196 | #[inline] |
| 111 | fn set_priority(prio: Priority) { | 197 | fn set_priority(self, prio: Priority) { |
| 112 | critical_section::with(|_| unsafe { | 198 | critical_section::with(|_| unsafe { |
| 113 | let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(()); | 199 | let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(()); |
| 114 | nvic.set_priority(NrWrap(Self::number()), prio.into()) | 200 | nvic.set_priority(self, prio.into()) |
| 115 | }) | 201 | }) |
| 116 | } | 202 | } |
| 117 | } | 203 | } |
| 118 | 204 | ||
| 205 | unsafe impl<T: InterruptNumber + Copy> InterruptExt for T {} | ||
| 206 | |||
| 119 | impl From<u8> for Priority { | 207 | impl From<u8> for Priority { |
| 120 | fn from(priority: u8) -> Self { | 208 | fn from(priority: u8) -> Self { |
| 121 | unsafe { mem::transmute(priority & PRIO_MASK) } | 209 | unsafe { mem::transmute(priority & PRIO_MASK) } |
