Skip to main content

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}