hal_mik32/epic.rs
1//! Управление внешним контроллером прерываний EPIC.
2//!
3//! "Штатный" контроллер прерываний в ядре отключен. Управление
4//! статусом (вкл, выкл) осуществляется через регистр mie (см. модуль interrupts).
5//!
6//! Все прерывания обрабатываются единым обработчиком trap_handler.
7//! // TODO: вынести listen для level в отдельный, чтобы можно было писать сразу несколько линий,
8//! а не по одной, так как при записи в регистр mask_level_set перезаписывается вся маска целиком
9use core::option::Option;
10use core::u32;
11use mik32_pac::{Epic, Peripherals};
12
13/// Тип срабатывания прерывания
14///
15/// # Variants
16///
17/// - `Edge` - по фронту
18/// - `Level` - по уровню
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum Trigger {
21 Edge, // Срабатывание по фронту
22 Level, // Срабатывание по уровню
23}
24
25/// Линии прерывания
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27#[repr(u32)]
28pub enum InterruptLine {
29 Timer32_0 = 1 << 0, // Таймер 32_0 - линия 0
30 USART0 = 1 << 1, // USART0 - линия 1
31 USART1 = 1 << 2, // USART1 - линия 2
32 SPI0 = 1 << 3, // SPI0 - линия 3
33 SPI1 = 1 << 4, // SPI1 - линия 4
34 GPIO = 1 << 5, // GPIO - линия 5
35 I2C0 = 1 << 6, // I2C0 - линия 6
36 I2C1 = 1 << 7, // I2C1 - линия 7
37 WDT = 1 << 8, // WDT - линия 8
38 Timer16_0 = 1 << 9, // Таймер 16_0 - линия 9
39 Timer16_1 = 1 << 10, // Таймер 16_1 - линия 10
40 Timer16_2 = 1 << 11, // Таймер 16_2 - линия 11
41 Timer32_1 = 1 << 12, // Таймер 32_1 - линия 12
42 Timer32_2 = 1 << 13, // Таймер 32_2 - линия 13
43 SPIFI = 1 << 14, // SPIFI - линия 14
44 RTC = 1 << 15, // RTC - линия 15
45 EEPROM = 1 << 16, // EEPROM - линия 16
46 WdtDom3 = 1 << 17, // WDT домен 3 - линия 17
47 WdtSpifi = 1 << 18, // WDT SPIFI - линия 18
48 WdtEeprom = 1 << 19, // WDT EEPROM - линия 19
49 DMA = 1 << 20, // DMA - линия 20
50 FrequencyMonitor = 1 << 21, // Монитор частоты - линия 21
51 AVCCOver = 1 << 22, // AVCC выше порога - линия 22
52 AVCCUnder = 1 << 23, // AVCC ниже порога - линия 23
53 VCCOver = 1 << 24, // VCC выше порога - линия 24
54 VCCUnder = 1 << 25, // VCC ниже порога - линия 25
55 LowBattery = 1 << 26, // Низкий заряд батареи - линия 26
56 BrownOut = 1 << 27, // Brown Out - линия 27
57 TSENS = 1 << 28, // Датчик температуры - линия 28
58 ADC = 1 << 29, // ADC - линия 29
59 DAC0 = 1 << 30, // DAC0 - линия 30
60 DAC1 = 1 << 31, // DAC1 - линия 31
61}
62
63impl InterruptLine {
64 #[inline(always)]
65 pub const fn mask(self) -> u32 {
66 self as u32
67 }
68}
69
70#[derive(Debug)]
71pub enum Error {
72 LineLevelSet,
73 LineEdgeSet,
74 LineNeverSet,
75}
76
77/// Контроллер прерываний
78pub struct EPIC {
79 dp: Epic,
80 line_mask: u32,
81}
82
83impl EPIC {
84 /// Конструктор
85 ///
86 /// # Arguments
87 ///
88 /// - `dp` (`Epic`) - контроллер прерываний из PAC
89 ///
90 /// # Returns
91 ///
92 /// - `Self` - экземпляр контроллера
93 ///
94 /// # Examples
95 ///
96 /// ```
97 /// use crate::epic::EPIC;
98 ///
99 /// let dp = Peripherals::take().unwrap();
100 /// let mut epic = EPIC::new(dp.epic);
101 /// ```
102 pub fn new(dp: Epic) -> Self {
103 // EPIC sits on the APB_M clock domain. The C HAL enables this clock
104 // before accessing EPIC registers; do the same here so the driver is
105 // ready to use right after construction.
106 unsafe {
107 Peripherals::steal()
108 .pm
109 .clk_apb_m_set()
110 .write(|w| w.epic().enable());
111 }
112
113 Self {
114 dp: dp,
115 line_mask: 0u32,
116 }
117 }
118
119 /// Включает линию прерывания
120 ///
121 /// Если на линию уже было включено прерывание, но другого типа
122 /// (включаете "по фронту", а уже было включено "по уровеню"), то вернётся ошибка
123 ///
124 /// # Arguments
125 ///
126 /// - `line` (`InterruptLine`) - линия прерывания
127 /// - `trigger` (`Trigger`) - тип срабатывания прерывания (по фронту или по уровню)
128 ///
129 /// # Returns
130 ///
131 /// - `Result<(), Error>` - успех включения прерывания
132 ///
133 /// # Errors
134 ///
135 /// На линию уже было включено прерывание другого типа
136 ///
137 /// # Examples
138 ///
139 /// ```
140 /// use crate::epic::EPIC;
141 ///
142 /// let dp = Peripherals::take().unwrap();
143 /// let mut epic = EPIC::new(dp.epic);
144 /// epic.listen(InterruptLine::Timer32_1); // Включим прерывание по линии таймера
145 /// ```
146 pub fn listen(&mut self, line: InterruptLine, trigger: Trigger) -> Result<(), Error> {
147 let line_mask = line.mask();
148
149 match trigger {
150 Trigger::Edge => {
151 if self.line_mask & line_mask != 0 {
152 return Err(Error::LineLevelSet);
153 }
154
155 self.dp
156 .mask_edge_set()
157 .write(|w| unsafe { w.bits(line_mask) });
158 }
159 Trigger::Level => {
160 if self.dp.mask_edge_set().read().bits() & line_mask != 0 {
161 return Err(Error::LineEdgeSet);
162 }
163
164 self.line_mask |= line_mask;
165 self.dp
166 .mask_level_set()
167 .write(|w| unsafe { w.bits(self.line_mask) });
168 }
169 }
170 Ok(())
171 }
172
173 /// Выключает прерывания по конкретной линии
174 /// Отключаются все прерввания, по фронту и по уровню.
175 ///
176 /// # Arguments
177 ///
178 /// - `line` (`InterruptLine`) - какую линию прервываний отключить, если `None`, то отключить все линии
179 ///
180 /// # Examples
181 ///
182 /// ```
183 /// use crate::epic::EPIC;
184 /// use mik32_pac::Peripherals;
185 ///
186 /// let dp = Peripherals::take().unwrap();
187 /// let mut epic = EPIC::new(dp.epic);
188 /// epic.unlisten(InterruptLine::Timer32_1); // Выключим прерывание по линии таймера
189 /// ```
190 pub fn unlisten(&mut self, line: Option<InterruptLine>) {
191 if let Some(line) = line {
192 let line_mask = line.mask();
193
194 // Выключаем "по фронту"
195 self.dp
196 .mask_edge_clear()
197 .write(|w| unsafe { w.bits(line_mask) });
198
199 // Выключаем "по уровню"
200 self.line_mask &= !line_mask;
201 self.dp
202 .mask_level_clear()
203 .write(|w| unsafe { w.bits(line_mask) });
204 } else {
205 // Выключаем все "по фронту"
206 self.dp
207 .mask_edge_clear()
208 .write(|w| unsafe { w.bits(u32::MAX) });
209
210 // Выключаем все "по уровню"
211 self.line_mask = 0u32;
212 self.dp
213 .mask_level_clear()
214 .write(|w| unsafe { w.bits(u32::MAX) });
215 }
216 }
217
218 /// Произошло ли событие?
219 ///
220 /// # Arguments
221 /// - `event` (`InterruptEvent`) - проверяемое событие
222 ///
223 /// # Returns
224 ///
225 /// - `bool` - случилось ли событие
226 ///
227 /// # Examples
228 ///
229 /// ```
230 /// use crate::epic::EPIC;
231 /// use mik32_pac::Peripherals;
232 ///
233 /// let dp = Peripherals::take().unwrap();
234 /// let mut epic = EPIC::new(dp.epic);
235 /// epic.listen(InterruptLine::Timer32_1); // Включим прерывание по линии таймера
236 /// let is_timer_event_happend: bool = epic.event(InterruptLine::Timer32_1);
237 /// ```
238 pub fn event(&self, line: InterruptLine) -> bool {
239 self.raw_pending(line)
240 }
241
242 /// Returns true when a configured, unmasked interrupt is pending.
243 ///
244 /// Reads EPIC `STATUS`, which is the right register for dispatching from a
245 /// trap handler because it takes EPIC masks into account.
246 pub fn pending(&self, line: InterruptLine) -> bool {
247 self.pending_mask() & line.mask() != 0
248 }
249
250 /// Returns the full EPIC `STATUS` register.
251 pub fn pending_mask(&self) -> u32 {
252 self.dp.status().read().bits()
253 }
254
255 /// Returns true when the interrupt line is asserted regardless of masks.
256 ///
257 /// Reads EPIC `RAW_STATUS`.
258 pub fn raw_pending(&self, line: InterruptLine) -> bool {
259 self.raw_pending_mask() & line.mask() != 0
260 }
261
262 /// Returns the full EPIC `RAW_STATUS` register.
263 pub fn raw_pending_mask(&self) -> u32 {
264 self.dp.raw_status().read().bits()
265 }
266
267 /// Clears the pending flag for one interrupt line.
268 pub fn clear_line(&mut self, line: InterruptLine) {
269 self.clear_mask(line.mask());
270 }
271
272 /// Clears pending flags selected by `mask`.
273 pub fn clear_mask(&mut self, mask: u32) {
274 self.dp.clear().write(|w| unsafe { w.bits(mask) });
275 }
276
277 /// Clears pending flags for all interrupt lines.
278 pub fn clear_all(&mut self) {
279 self.clear_mask(u32::MAX);
280 }
281
282 /// Очищает флаги всех прерываний
283 ///
284 /// # Examples
285 ///
286 /// ```
287 /// use crate::epic::EPIC;
288 /// use mik32_pac::Peripherals;
289 /// let dp = Peripherals::take().unwrap();
290 /// let mut epic = EPIC::new(dp.epic);
291 /// epic.unlisten(InterruptLine::Timer32_1); // Выключим прерывание по линии таймера
292 /// ```
293 pub fn clear(&mut self) {
294 self.clear_all();
295 }
296}