Skip to main content

pic32_hal/
int.rs

1//! Interrupt controller
2//!
3//! Enable/disable and set priorities of interrupts in Multi-vectored mode
4
5use crate::pac::INT;
6use crate::pac_crate::{Reg, RegisterSpec};
7use core::marker::PhantomData;
8use core::ptr::{read_volatile, write_volatile};
9
10/// Interrupt source (from PAC)
11///
12/// Multiple interrupt sources can share a single interrupt vector.
13pub use crate::pac::interrupt::InterruptSource;
14
15/// Interrupt vector (from PAC)
16///
17/// One interrupt vector may assigned more than one interrupt source.
18pub use crate::pac::interrupt::Interrupt;
19
20/// Invalid numerical priority level
21#[derive(Debug, Copy, Clone)]
22pub struct PriorityConvertError;
23
24/// Interrupt priority level assignable to an interrupt vector
25#[derive(Copy, Clone, Debug)]
26pub struct Ipl(u8);
27
28impl TryFrom<u8> for Ipl {
29    type Error = PriorityConvertError;
30
31    fn try_from(ipl: u8) -> Result<Self, Self::Error> {
32        if ipl <= 7 {
33            Ok(Ipl(ipl))
34        } else {
35            Err(PriorityConvertError)
36        }
37    }
38}
39
40impl From<Ipl> for u8 {
41    fn from(ipl: Ipl) -> Self {
42        ipl.0
43    }
44}
45
46/// Interrupt priority level 1 (lowest interrupt priority; interrupts disabled)
47pub const IPL0: Ipl = Ipl(0);
48/// Interrupt priority level 1
49pub const IPL1: Ipl = Ipl(1);
50/// Interrupt priority level 2
51pub const IPL2: Ipl = Ipl(2);
52/// Interrupt priority level 3
53pub const IPL3: Ipl = Ipl(3);
54/// Interrupt priority level 4
55pub const IPL4: Ipl = Ipl(4);
56/// Interrupt priority level 5
57pub const IPL5: Ipl = Ipl(5);
58/// Interrupt priority level 6
59pub const IPL6: Ipl = Ipl(6);
60/// Interrupt priority level 6 (highest interrupt priority)
61pub const IPL7: Ipl = Ipl(7);
62
63/// Interrupt sub priority levels
64#[derive(Copy, Clone, Debug)]
65pub struct Isl(u8);
66
67impl TryFrom<u8> for Isl {
68    type Error = PriorityConvertError;
69
70    fn try_from(isl: u8) -> Result<Self, Self::Error> {
71        if isl <= 3 {
72            Ok(Isl(isl))
73        } else {
74            Err(PriorityConvertError)
75        }
76    }
77}
78
79impl From<Isl> for u8 {
80    fn from(isl: Isl) -> Self {
81        isl.0
82    }
83}
84
85/// Interrupt sub priority level 0 (lowest interrupt sub priority)
86pub const ISL0: Isl = Isl(0);
87/// Interrupt sub priority level 1
88pub const ISL1: Isl = Isl(1);
89/// Interrupt sub priority level 2
90pub const ISL2: Isl = Isl(2);
91/// Interrupt sub priority level 3 (highest interrupt sub priority)
92pub const ISL3: Isl = Isl(3);
93
94/// Access to interrupt controller.
95pub struct Int {
96    _int: PhantomData<INT>,
97}
98
99impl Int {
100    /// Create a new instance. Configures the interrupt controller to work in
101    /// the Multi-vectored mode. The Cause register of the MIPS core must be
102    /// already configured for use with External Interrupt Controller (EIC);
103    /// done by startup code.
104    pub fn new(int: INT) -> Int {
105        int.intconset.write(|w| w.mvec().bit(true));
106        Int { _int: PhantomData }
107    }
108
109    fn bitaddr<REG: RegisterSpec>(s: InterruptSource, breg: &Reg<REG>) -> (*mut u32, u32) {
110        let regndx = (s as usize) / 32;
111        let mask = 1 << ((s as usize) % 32);
112        let base = breg as *const _ as usize;
113        let reg = (base + regndx * 0x10) as *mut u32;
114        (reg, mask)
115    }
116
117    /// Enable interrupts for a specific source
118    pub fn ei(&self, s: InterruptSource) {
119        let (reg, mask) = Self::bitaddr(s, unsafe { &(*INT::ptr()).iec0set });
120        unsafe { write_volatile(reg, mask) };
121    }
122
123    /// Disable interrupts for a specific source
124    pub fn di(&self, s: InterruptSource) {
125        let (reg, mask) = Self::bitaddr(s, unsafe { &(*INT::ptr()).iec0clr });
126        unsafe { write_volatile(reg, mask) };
127    }
128
129    /// Check if interrupts for a specific source are enabled
130    pub fn is_ie(&self, s: InterruptSource) -> bool {
131        let (reg, mask) = Self::bitaddr(s, unsafe { &(*INT::ptr()).iec0 });
132        unsafe { read_volatile(reg) & mask != 0 }
133    }
134
135    /// Read the interrupt flag of a specific interrupt source
136    pub fn get_if(&self, s: InterruptSource) -> bool {
137        let (reg, mask) = Self::bitaddr(s, unsafe { &(*INT::ptr()).ifs0 });
138        unsafe { read_volatile(reg) & mask != 0 }
139    }
140
141    /// Clear the interrupt flag of a specific interrupt source
142    /// To be called before terminating an ISR.
143    pub fn clear_if(&self, s: InterruptSource) {
144        let (reg, mask) = Self::bitaddr(s, unsafe { &(*INT::ptr()).ifs0clr });
145        unsafe { write_volatile(reg, mask) };
146    }
147
148    /// Set the interrupt flag of a specific interrupt source
149    pub fn set_if(&self, s: InterruptSource) {
150        let (reg, mask) = Self::bitaddr(s, unsafe { &(*INT::ptr()).ifs0set });
151        unsafe { write_volatile(reg, mask) };
152    }
153
154    fn byteaddr<REG: RegisterSpec>(iv: Interrupt, breg: &Reg<REG>) -> (*mut u32, usize) {
155        let regndx = (iv as usize) / 4;
156        let bytepos = ((iv as usize) % 4) * 8;
157        let base = breg as *const _ as usize;
158        let reg = (base + regndx * 0x10) as *mut u32;
159        (reg, bytepos)
160    }
161
162    /// Set the interrupt priority level of a specific interrupt vector
163    pub fn set_ipl(&self, iv: Interrupt, ipl: Ipl) {
164        let (reg, bytepos) = Self::byteaddr(iv, unsafe { &(*INT::ptr()).ipc0 });
165        let bitpos = bytepos + 2;
166        let mask = 0x07 << bitpos;
167        unsafe { write_volatile(reg, read_volatile(reg) & !mask | ((ipl.0 as u32) << bitpos)) };
168    }
169
170    /// Get the interrupt priority level of a specific interrupt vector
171    pub fn ipl(&self, iv: Interrupt) -> Ipl {
172        let (reg, bytepos) = Self::byteaddr(iv, unsafe { &(*INT::ptr()).ipc0 });
173        let bitpos = bytepos + 2;
174        unsafe { Ipl((read_volatile(reg) >> bitpos) as u8 & 0x07) }
175    }
176
177    /// Set the interrupt sub priority level of a specific interrupt vector
178    pub fn set_isl(&self, iv: Interrupt, isl: Isl) {
179        let (reg, bitpos) = Self::byteaddr(iv, unsafe { &(*INT::ptr()).ipc0 });
180        let mask = 0x03 << bitpos;
181        unsafe { write_volatile(reg, read_volatile(reg) & !mask | ((isl.0 as u32) << bitpos)) };
182    }
183
184    /// Get the interrupt sub priority level of a specific interrupt vector
185    pub fn isl(&self, iv: Interrupt) -> Isl {
186        let (reg, bitpos) = Self::byteaddr(iv, unsafe { &(*INT::ptr()).ipc0 });
187        unsafe { Isl((read_volatile(reg) >> bitpos) as u8 & 0x03) }
188    }
189}