Skip to main content

pamoja_radios/sx127x/
irq.rs

1//! The SX127x LoRa interrupt flags.
2//!
3//! In LoRa mode the chip raises its interrupts in RegIrqFlags and masks them in
4//! RegIrqFlagsMask, both laid out as the SX1276/77/78/79 datasheet (Rev 7) describes them.
5//! A raised flag stays set until it is written back as a 1.
6
7use core::ops::{BitAnd, BitOr, BitOrAssign};
8
9/// A set of SX127x LoRa interrupts, in the layout of RegIrqFlags.
10///
11/// # Examples
12///
13/// ```
14/// use pamoja_radios::sx127x::irq::IrqFlags;
15///
16/// // RegIrqFlags read 0x70: a packet arrived with a valid header but a bad payload CRC.
17/// let raised = IrqFlags::from_bits(0x70);
18/// assert!(raised.contains(IrqFlags::RX_DONE | IrqFlags::PAYLOAD_CRC_ERROR));
19/// assert!(!raised.contains(IrqFlags::TX_DONE));
20/// ```
21#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
22pub struct IrqFlags(u8);
23
24impl IrqFlags {
25    /// No interrupt.
26    pub const NONE: IrqFlags = IrqFlags(0);
27    /// Bit 0: channel activity detection heard a LoRa signal.
28    pub const CAD_DETECTED: IrqFlags = IrqFlags(1 << 0);
29    /// Bit 1: frequency hopping moved to the next channel.
30    pub const FHSS_CHANGE_CHANNEL: IrqFlags = IrqFlags(1 << 1);
31    /// Bit 2: channel activity detection finished.
32    pub const CAD_DONE: IrqFlags = IrqFlags(1 << 2);
33    /// Bit 3: the payload in the data buffer has been transmitted.
34    pub const TX_DONE: IrqFlags = IrqFlags(1 << 3);
35    /// Bit 4: a valid header was received.
36    pub const VALID_HEADER: IrqFlags = IrqFlags(1 << 4);
37    /// Bit 5: the payload failed its CRC.
38    pub const PAYLOAD_CRC_ERROR: IrqFlags = IrqFlags(1 << 5);
39    /// Bit 6: a packet has been received.
40    pub const RX_DONE: IrqFlags = IrqFlags(1 << 6);
41    /// Bit 7: a single reception timed out before a preamble arrived.
42    pub const RX_TIMEOUT: IrqFlags = IrqFlags(1 << 7);
43    /// Every interrupt, which is what writing 0xFF clears.
44    pub const ALL: IrqFlags = IrqFlags(0xFF);
45
46    /// Creates a set from the register's bits.
47    ///
48    /// # Arguments
49    ///
50    /// * `bits` - the RegIrqFlags value.
51    ///
52    /// # Returns
53    ///
54    /// The set.
55    pub const fn from_bits(bits: u8) -> IrqFlags {
56        IrqFlags(bits)
57    }
58
59    /// Returns the register's bits.
60    ///
61    /// # Returns
62    ///
63    /// The RegIrqFlags value, which written back clears exactly these interrupts.
64    pub const fn bits(self) -> u8 {
65        self.0
66    }
67
68    /// Reports whether every interrupt of another set is in this one.
69    ///
70    /// # Arguments
71    ///
72    /// * `other` - the interrupts to look for.
73    ///
74    /// # Returns
75    ///
76    /// `true` when all of them are set.
77    pub const fn contains(self, other: IrqFlags) -> bool {
78        self.0 & other.0 == other.0
79    }
80
81    /// Reports whether any interrupt of another set is in this one.
82    ///
83    /// # Arguments
84    ///
85    /// * `other` - the interrupts to look for.
86    ///
87    /// # Returns
88    ///
89    /// `true` when at least one of them is set.
90    pub const fn intersects(self, other: IrqFlags) -> bool {
91        self.0 & other.0 != 0
92    }
93
94    /// Reports whether the set holds no interrupt.
95    ///
96    /// # Returns
97    ///
98    /// `true` when no bit is set.
99    pub const fn is_empty(self) -> bool {
100        self.0 == 0
101    }
102}
103
104impl BitOr for IrqFlags {
105    type Output = IrqFlags;
106
107    fn bitor(self, rhs: IrqFlags) -> IrqFlags {
108        IrqFlags(self.0 | rhs.0)
109    }
110}
111
112impl BitOrAssign for IrqFlags {
113    fn bitor_assign(&mut self, rhs: IrqFlags) {
114        self.0 |= rhs.0;
115    }
116}
117
118impl BitAnd for IrqFlags {
119    type Output = IrqFlags;
120
121    fn bitand(self, rhs: IrqFlags) -> IrqFlags {
122        IrqFlags(self.0 & rhs.0)
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn each_flag_sits_on_the_bit_the_register_description_gives() {
132        assert_eq!(IrqFlags::RX_TIMEOUT.bits(), 0x80);
133        assert_eq!(IrqFlags::RX_DONE.bits(), 0x40);
134        assert_eq!(IrqFlags::PAYLOAD_CRC_ERROR.bits(), 0x20);
135        assert_eq!(IrqFlags::VALID_HEADER.bits(), 0x10);
136        assert_eq!(IrqFlags::TX_DONE.bits(), 0x08);
137        assert_eq!(IrqFlags::CAD_DONE.bits(), 0x04);
138        assert_eq!(IrqFlags::FHSS_CHANGE_CHANNEL.bits(), 0x02);
139        assert_eq!(IrqFlags::CAD_DETECTED.bits(), 0x01);
140    }
141
142    #[test]
143    fn the_flags_together_are_the_whole_register() {
144        let every = IrqFlags::RX_TIMEOUT
145            | IrqFlags::RX_DONE
146            | IrqFlags::PAYLOAD_CRC_ERROR
147            | IrqFlags::VALID_HEADER
148            | IrqFlags::TX_DONE
149            | IrqFlags::CAD_DONE
150            | IrqFlags::FHSS_CHANGE_CHANNEL
151            | IrqFlags::CAD_DETECTED;
152        assert_eq!(every, IrqFlags::ALL);
153    }
154
155    #[test]
156    fn intersects_finds_any_and_contains_needs_all() {
157        let raised = IrqFlags::RX_DONE | IrqFlags::VALID_HEADER;
158        assert!(raised.intersects(IrqFlags::RX_DONE | IrqFlags::RX_TIMEOUT));
159        assert!(!raised.contains(IrqFlags::RX_DONE | IrqFlags::RX_TIMEOUT));
160        assert!(IrqFlags::NONE.is_empty());
161        assert_eq!(raised & IrqFlags::RX_DONE, IrqFlags::RX_DONE);
162    }
163}