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