atsamd_hal/
interrupt.rs

1//! Primitives for manipulating interrupts
2
3use core::sync::atomic::{compiler_fence, Ordering};
4
5use crate::pac::NVIC;
6use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
7
8pub use crate::pac::NVIC_PRIO_BITS;
9
10/// Logical interrupt priority level.
11///
12/// P4 is the most urgent, and P1 is the least urgent priority.
13#[hal_cfg(any("nvic-d11", "nvic-d21"))]
14#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16#[repr(u8)]
17#[allow(missing_docs)]
18pub enum Priority {
19    P1 = 1,
20    P2 = 2,
21    P3 = 3,
22    P4 = 4,
23}
24
25/// Logical interrupt priority level.
26///
27/// P8 is the most urgent, and P1 is the least urgent priority.
28#[hal_cfg("nvic-d5x")]
29#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31#[repr(u8)]
32#[allow(missing_docs)]
33pub enum Priority {
34    P1 = 1,
35    P2 = 2,
36    P3 = 3,
37    P4 = 4,
38    P5 = 5,
39    P6 = 6,
40    P7 = 7,
41    P8 = 8,
42}
43
44impl Priority {
45    /// Creates the `Priority` from a numeric priority if possible.
46    pub const fn from_numeric(prio: u8) -> Option<Self> {
47        if prio >= 1 && prio <= 8 {
48            Some(unsafe { core::mem::transmute::<u8, Self>(prio) })
49        } else {
50            None
51        }
52    }
53
54    /// Convert a logical priority (where higher priority number = higher
55    /// priority level) to a hardware priority level (where lower priority
56    /// number = higher priority level).
57    ///
58    /// Taken from [`cortex-m-interrupt`]
59    ///
60    /// See LICENSE-MIT for the license.
61    ///
62    /// [`cortex-m-interrupt`]: https://github.com/datdenkikniet/cortex-m-interrupt
63    #[inline]
64    #[must_use]
65    pub const fn logical2hw(self) -> u8 {
66        ((1 << NVIC_PRIO_BITS) - self as u8) << (8 - NVIC_PRIO_BITS)
67    }
68
69    /// Convert a hardware priority level (where lower priority number = higher
70    /// priority level) to a logical priority (where a higher priority number =
71    /// higher priority level).
72    ///
73    /// # Panics
74    ///
75    /// This method may only be used with allowed hardware priority levels. Ie,
76    /// * 0x00,
77    /// * 0x20,
78    /// * 0x40,
79    /// * and so on.
80    ///
81    /// Any other value will cause a panic. To save yourself some
82    /// trouble, use this method only with hardware priority values gotten
83    /// directly from the NVIC.
84    #[inline]
85    #[must_use]
86    pub const fn hw2logical(prio: u8) -> Self {
87        assert!(prio % 0x20 == 0);
88        unsafe { core::mem::transmute((1 << NVIC_PRIO_BITS) - (prio >> (8 - NVIC_PRIO_BITS))) }
89    }
90}
91
92/// An interrupt type that can be configured by the HAL to handle
93/// interrupts.
94///
95/// The PAC defined enum-level interrupts implement this trait.
96pub trait InterruptExt: cortex_m::interrupt::InterruptNumber + Copy {
97    /// Enable the interrupt.
98    ///
99    /// # Safety
100    ///
101    /// Do not enable any interrupt inside a critical section.
102    #[inline]
103    unsafe fn enable(self) {
104        compiler_fence(Ordering::SeqCst);
105        NVIC::unmask(self)
106    }
107
108    /// Disable the interrupt.
109    #[inline]
110    fn disable(self) {
111        NVIC::mask(self);
112        compiler_fence(Ordering::SeqCst);
113    }
114
115    /// Check if interrupt is enabled.
116    #[inline]
117    fn is_enabled(self) -> bool {
118        NVIC::is_enabled(self)
119    }
120
121    /// Check if interrupt is pending.
122    #[inline]
123    fn is_pending(self) -> bool {
124        NVIC::is_pending(self)
125    }
126
127    /// Set interrupt pending.
128    #[inline]
129    fn pend(self) {
130        NVIC::pend(self)
131    }
132
133    /// Unset interrupt pending.
134    #[inline]
135    fn unpend(self) {
136        NVIC::unpend(self)
137    }
138
139    /// Get the priority of the interrupt.
140    #[inline]
141    fn get_priority(self) -> Priority {
142        Priority::hw2logical(NVIC::get_priority(self))
143    }
144
145    /// Set the interrupt priority.
146    #[inline]
147    #[hal_macro_helper]
148    fn set_priority(self, prio: Priority) {
149        unsafe {
150            let mut nvic = steal_nvic();
151
152            // On thumbv6, set_priority must do a RMW to change 8bit in a 32bit reg.
153            #[hal_cfg(any("nvic-d11", "nvic-d21"))]
154            critical_section::with(|_| nvic.set_priority(self, prio.logical2hw()));
155            // On thumbv7+, set_priority does an atomic 8bit write, so no CS needed.
156            #[hal_cfg("nvic-d5x")]
157            nvic.set_priority(self, prio.logical2hw());
158        }
159    }
160
161    /// Set the interrupt priority with an already-acquired critical section.
162    ///
163    /// Equivalent to [`set_priority`](Self::set_priority), except you pass a
164    /// [`CriticalSection`] to prove you've already acquired a critical
165    /// section. This prevents acquiring another one, which saves code size.
166    ///
167    /// [`CriticalSection`]: critical_section::CriticalSection
168    #[inline]
169    fn set_priority_with_cs(self, _cs: critical_section::CriticalSection, prio: Priority) {
170        unsafe {
171            let mut nvic = steal_nvic();
172            nvic.set_priority(self, prio.logical2hw());
173        }
174    }
175}
176
177impl<T: cortex_m::interrupt::InterruptNumber + Copy> InterruptExt for T {}
178
179unsafe fn steal_nvic() -> NVIC {
180    cortex_m::peripheral::Peripherals::steal().NVIC
181}