diff --git a/hal/src/async_hal/interrupts.rs b/hal/src/async_hal/interrupts.rs index a1d76625ed4..b9a6f55705a 100644 --- a/hal/src/async_hal/interrupts.rs +++ b/hal/src/async_hal/interrupts.rs @@ -1,7 +1,7 @@ //! # Async interrupts //! -//! This module provides APIs for working with interrupts, tailored towards -//! async peripherals. +//! This module provides APIs specific to working with interrupts in an async +//! peripheral context. //! //! Asynchronous programming relies on tasks that can be paused and resumed //! without blocking the entire program. When an async task is waiting for a @@ -34,14 +34,10 @@ //! [`InterruptExt`]: crate::async_hal::interrupts::InterruptExt //! [`pac`]: crate::pac +pub use crate::interrupt::*; + use crate::typelevel::Sealed; -use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; -use core::{ - mem, - sync::atomic::{compiler_fence, Ordering}, -}; -use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; -use critical_section::CriticalSection; +use atsamd_hal_macros::hal_cfg; /// Marker trait indicating that an interrupt source has one binding and /// one handler. @@ -355,170 +351,3 @@ pub trait Handler: Sealed { /// /// This allows drivers to check bindings at compile-time. pub unsafe trait Binding> {} - -/// An interrupt type that can be configured by the HAL to handle -/// interrupts. -/// -/// The PAC defined enum-level interrupts implement this trait. -pub trait InterruptExt: InterruptNumber + Copy { - /// Enable the interrupt. - /// - /// # Safety - /// - /// Do not enable any interrupt inside a critical section. - #[inline] - unsafe fn enable(self) { - compiler_fence(Ordering::SeqCst); - NVIC::unmask(self) - } - - /// Disable the interrupt. - #[inline] - fn disable(self) { - NVIC::mask(self); - compiler_fence(Ordering::SeqCst); - } - - /// Check if interrupt is enabled. - #[inline] - fn is_enabled(self) -> bool { - NVIC::is_enabled(self) - } - - /// Check if interrupt is pending. - #[inline] - fn is_pending(self) -> bool { - NVIC::is_pending(self) - } - - /// Set interrupt pending. - #[inline] - fn pend(self) { - NVIC::pend(self) - } - - /// Unset interrupt pending. - #[inline] - fn unpend(self) { - NVIC::unpend(self) - } - - /// Get the priority of the interrupt. - #[inline] - fn get_priority(self) -> Priority { - Priority::hw2logical(NVIC::get_priority(self)) - } - - /// Set the interrupt priority. - #[inline] - #[hal_macro_helper] - fn set_priority(self, prio: Priority) { - unsafe { - let mut nvic = steal_nvic(); - - // On thumbv6, set_priority must do a RMW to change 8bit in a 32bit reg. - #[hal_cfg(any("nvic-d11", "nvic-d21"))] - critical_section::with(|_| nvic.set_priority(self, prio.logical2hw())); - // On thumbv7+, set_priority does an atomic 8bit write, so no CS needed. - #[hal_cfg("nvic-d5x")] - nvic.set_priority(self, prio.logical2hw()); - } - } - - /// Set the interrupt priority with an already-acquired critical section. - /// - /// Equivalent to [`set_priority`](Self::set_priority), except you pass a - /// [`CriticalSection`] to prove you've already acquired a critical - /// section. This prevents acquiring another one, which saves code size. - #[inline] - fn set_priority_with_cs(self, _cs: CriticalSection, prio: Priority) { - unsafe { - let mut nvic = steal_nvic(); - nvic.set_priority(self, prio.logical2hw()); - } - } -} - -impl InterruptExt for T {} - -#[hal_cfg(any("nvic-d11", "nvic-d21"))] -const NVIC_PRIO_BITS: u8 = 2; -#[hal_cfg("nvic-d5x")] -const NVIC_PRIO_BITS: u8 = 3; - -/// Logical interrupt priority level. -/// -/// P4 is the most urgent, and P1 is the least urgent priority. -#[hal_cfg(any("nvic-d11", "nvic-d21"))] -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -#[allow(missing_docs)] -pub enum Priority { - P1 = 1, - P2 = 2, - P3 = 3, - P4 = 4, -} - -/// Logical interrupt priority level. -/// -/// P8 is the most urgent, and P1 is the least urgent priority. -#[hal_cfg("nvic-d5x")] -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -#[allow(missing_docs)] -pub enum Priority { - P1 = 1, - P2 = 2, - P3 = 3, - P4 = 4, - P5 = 5, - P6 = 6, - P7 = 7, - P8 = 8, -} - -impl Priority { - /// Convert a logical priority (where higher priority number = higher - /// priority level) to a hardware priority level (where lower priority - /// number = higher priority level). - /// - /// Taken from [`cortex-m-interrupt`] - /// - /// See LICENSE-MIT for the license. - /// - /// [`cortex-m-interrupt`]: https://github.com/datdenkikniet/cortex-m-interrupt - #[inline] - #[must_use] - pub const fn logical2hw(self) -> u8 { - ((1 << NVIC_PRIO_BITS) - self as u8) << (8 - NVIC_PRIO_BITS) - } - - /// Convert a hardware priority level (where lower priority number = higher - /// priority level) to a logical priority (where a higher priority number = - /// higher priority level). - /// - /// # Panics - /// - /// This method may only be used with allowed hardware priority levels. Ie, - /// * 0x00, - /// * 0x20, - /// * 0x40, - /// * and so on. - /// - /// Any other value will cause a panic. To save yourself some - /// trouble, use this method only with hardware priority values gotten - /// directly from the NVIC. - #[inline] - #[must_use] - pub const fn hw2logical(prio: u8) -> Self { - assert!(prio % 0x20 == 0); - unsafe { mem::transmute((1 << NVIC_PRIO_BITS) - (prio >> (8 - NVIC_PRIO_BITS))) } - } -} - -unsafe fn steal_nvic() -> NVIC { - cortex_m::peripheral::Peripherals::steal().NVIC -} diff --git a/hal/src/interrupt.rs b/hal/src/interrupt.rs new file mode 100644 index 00000000000..ec79802b0b3 --- /dev/null +++ b/hal/src/interrupt.rs @@ -0,0 +1,170 @@ +//! Primitives for manipulating interrupts + +use core::sync::atomic::{compiler_fence, Ordering}; + +use crate::pac::NVIC; +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; + +pub use crate::pac::NVIC_PRIO_BITS; + +/// Logical interrupt priority level. +/// +/// P4 is the most urgent, and P1 is the least urgent priority. +#[hal_cfg(any("nvic-d11", "nvic-d21"))] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +pub enum Priority { + P1 = 1, + P2 = 2, + P3 = 3, + P4 = 4, +} + +/// Logical interrupt priority level. +/// +/// P8 is the most urgent, and P1 is the least urgent priority. +#[hal_cfg("nvic-d5x")] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +#[allow(missing_docs)] +pub enum Priority { + P1 = 1, + P2 = 2, + P3 = 3, + P4 = 4, + P5 = 5, + P6 = 6, + P7 = 7, + P8 = 8, +} + +impl Priority { + /// Convert a logical priority (where higher priority number = higher + /// priority level) to a hardware priority level (where lower priority + /// number = higher priority level). + /// + /// Taken from [`cortex-m-interrupt`] + /// + /// See LICENSE-MIT for the license. + /// + /// [`cortex-m-interrupt`]: https://github.com/datdenkikniet/cortex-m-interrupt + #[inline] + #[must_use] + pub const fn logical2hw(self) -> u8 { + ((1 << NVIC_PRIO_BITS) - self as u8) << (8 - NVIC_PRIO_BITS) + } + + /// Convert a hardware priority level (where lower priority number = higher + /// priority level) to a logical priority (where a higher priority number = + /// higher priority level). + /// + /// # Panics + /// + /// This method may only be used with allowed hardware priority levels. Ie, + /// * 0x00, + /// * 0x20, + /// * 0x40, + /// * and so on. + /// + /// Any other value will cause a panic. To save yourself some + /// trouble, use this method only with hardware priority values gotten + /// directly from the NVIC. + #[inline] + #[must_use] + pub const fn hw2logical(prio: u8) -> Self { + assert!(prio % 0x20 == 0); + unsafe { core::mem::transmute((1 << NVIC_PRIO_BITS) - (prio >> (8 - NVIC_PRIO_BITS))) } + } +} + +/// An interrupt type that can be configured by the HAL to handle +/// interrupts. +/// +/// The PAC defined enum-level interrupts implement this trait. +pub trait InterruptExt: cortex_m::interrupt::InterruptNumber + Copy { + /// Enable the interrupt. + /// + /// # Safety + /// + /// Do not enable any interrupt inside a critical section. + #[inline] + unsafe fn enable(self) { + compiler_fence(Ordering::SeqCst); + NVIC::unmask(self) + } + + /// Disable the interrupt. + #[inline] + fn disable(self) { + NVIC::mask(self); + compiler_fence(Ordering::SeqCst); + } + + /// Check if interrupt is enabled. + #[inline] + fn is_enabled(self) -> bool { + NVIC::is_enabled(self) + } + + /// Check if interrupt is pending. + #[inline] + fn is_pending(self) -> bool { + NVIC::is_pending(self) + } + + /// Set interrupt pending. + #[inline] + fn pend(self) { + NVIC::pend(self) + } + + /// Unset interrupt pending. + #[inline] + fn unpend(self) { + NVIC::unpend(self) + } + + /// Get the priority of the interrupt. + #[inline] + fn get_priority(self) -> Priority { + Priority::hw2logical(NVIC::get_priority(self)) + } + + /// Set the interrupt priority. + #[inline] + #[hal_macro_helper] + fn set_priority(self, prio: Priority) { + unsafe { + let mut nvic = steal_nvic(); + + // On thumbv6, set_priority must do a RMW to change 8bit in a 32bit reg. + #[hal_cfg(any("nvic-d11", "nvic-d21"))] + critical_section::with(|_| nvic.set_priority(self, prio.logical2hw())); + // On thumbv7+, set_priority does an atomic 8bit write, so no CS needed. + #[hal_cfg("nvic-d5x")] + nvic.set_priority(self, prio.logical2hw()); + } + } + + /// Set the interrupt priority with an already-acquired critical section. + /// + /// Equivalent to [`set_priority`](Self::set_priority), except you pass a + /// [`CriticalSection`] to prove you've already acquired a critical + /// section. This prevents acquiring another one, which saves code size. + #[inline] + fn set_priority_with_cs(self, _cs: critical_section::CriticalSection, prio: Priority) { + unsafe { + let mut nvic = steal_nvic(); + nvic.set_priority(self, prio.logical2hw()); + } + } +} + +impl InterruptExt for T {} + +unsafe fn steal_nvic() -> NVIC { + cortex_m::peripheral::Peripherals::steal().NVIC +} diff --git a/hal/src/lib.rs b/hal/src/lib.rs index 91ad7ca0af7..5fde9260fce 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs @@ -76,6 +76,8 @@ pub mod delay; #[cfg(feature = "device")] pub mod gpio; #[cfg(feature = "device")] +pub mod interrupt; +#[cfg(feature = "device")] pub mod prelude; #[cfg(feature = "device")] pub mod rtc;