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}