pamoja_radios/sx126x/irq.rs
1//! The SX126x interrupt bits.
2//!
3//! The chip logs each interrupt source in a 16-bit IRQ register, laid out in Table 13-29
4//! of the SX1261/2 datasheet. The same layout masks which interrupts are enabled and
5//! routes each one to DIO1, DIO2, or DIO3 through SetDioIrqParams, and clears them through
6//! ClearIrqStatus.
7
8use core::ops::{BitAnd, BitOr, BitOrAssign};
9
10/// A set of SX126x interrupts, in the layout of the IRQ register.
11///
12/// # Examples
13///
14/// ```
15/// use pamoja_radios::sx126x::irq::Irq;
16///
17/// // GetIrqStatus answered 0x0202: a timeout and a received packet.
18/// let pending = Irq::from_bytes([0x02, 0x02]);
19/// assert!(pending.contains(Irq::RX_DONE | Irq::TIMEOUT));
20/// assert!(!pending.contains(Irq::CRC_ERROR));
21/// ```
22#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
23pub struct Irq(u16);
24
25impl Irq {
26 /// No interrupt.
27 pub const NONE: Irq = Irq(0);
28 /// Bit 0: the packet transmission completed.
29 pub const TX_DONE: Irq = Irq(1 << 0);
30 /// Bit 1: a packet was received.
31 pub const RX_DONE: Irq = Irq(1 << 1);
32 /// Bit 2: a preamble was detected.
33 pub const PREAMBLE_DETECTED: Irq = Irq(1 << 2);
34 /// Bit 3: a valid sync word was detected, in FSK.
35 pub const SYNC_WORD_VALID: Irq = Irq(1 << 3);
36 /// Bit 4: a valid LoRa header was received.
37 pub const HEADER_VALID: Irq = Irq(1 << 4);
38 /// Bit 5: a LoRa header failed its CRC.
39 pub const HEADER_ERROR: Irq = Irq(1 << 5);
40 /// Bit 6: a packet failed its CRC.
41 pub const CRC_ERROR: Irq = Irq(1 << 6);
42 /// Bit 7: channel activity detection finished, in LoRa.
43 pub const CAD_DONE: Irq = Irq(1 << 7);
44 /// Bit 8: channel activity was detected, in LoRa.
45 pub const CAD_DETECTED: Irq = Irq(1 << 8);
46 /// Bit 9: a receive or transmit timeout.
47 pub const TIMEOUT: Irq = Irq(1 << 9);
48 /// Bit 14: a hop in LR-FHSS, after the power amplifier ramped up again.
49 pub const LR_FHSS_HOP: Irq = Irq(1 << 14);
50 /// Every interrupt the datasheet defines: bits 0 to 9 and bit 14.
51 pub const ALL: Irq = Irq(0x43FF);
52
53 /// Creates a set from the register's bits.
54 ///
55 /// # Arguments
56 ///
57 /// * `bits` - the 16-bit register value.
58 ///
59 /// # Returns
60 ///
61 /// The set, keeping reserved bits as they came.
62 pub const fn from_bits(bits: u16) -> Irq {
63 Irq(bits)
64 }
65
66 /// Creates a set from the two bytes the chip answers with, most significant first.
67 ///
68 /// # Arguments
69 ///
70 /// * `bytes` - the IrqStatus bytes of a GetIrqStatus answer.
71 ///
72 /// # Returns
73 ///
74 /// The set.
75 pub const fn from_bytes(bytes: [u8; 2]) -> Irq {
76 Irq(u16::from_be_bytes(bytes))
77 }
78
79 /// Returns the register's bits.
80 ///
81 /// # Returns
82 ///
83 /// The 16-bit register value.
84 pub const fn bits(self) -> u16 {
85 self.0
86 }
87
88 /// Returns the two bytes a command carries for this set, most significant first.
89 ///
90 /// # Returns
91 ///
92 /// The mask as it goes on the wire.
93 pub const fn to_bytes(self) -> [u8; 2] {
94 self.0.to_be_bytes()
95 }
96
97 /// Reports whether every interrupt of another set is in this one.
98 ///
99 /// # Arguments
100 ///
101 /// * `other` - the interrupts to look for.
102 ///
103 /// # Returns
104 ///
105 /// `true` when all of them are set.
106 pub const fn contains(self, other: Irq) -> bool {
107 self.0 & other.0 == other.0
108 }
109
110 /// Reports whether any interrupt of another set is in this one.
111 ///
112 /// # Arguments
113 ///
114 /// * `other` - the interrupts to look for.
115 ///
116 /// # Returns
117 ///
118 /// `true` when at least one of them is set.
119 pub const fn intersects(self, other: Irq) -> bool {
120 self.0 & other.0 != 0
121 }
122
123 /// Reports whether the set holds no interrupt.
124 ///
125 /// # Returns
126 ///
127 /// `true` when no bit is set.
128 pub const fn is_empty(self) -> bool {
129 self.0 == 0
130 }
131}
132
133impl BitOr for Irq {
134 type Output = Irq;
135
136 fn bitor(self, rhs: Irq) -> Irq {
137 Irq(self.0 | rhs.0)
138 }
139}
140
141impl BitOrAssign for Irq {
142 fn bitor_assign(&mut self, rhs: Irq) {
143 self.0 |= rhs.0;
144 }
145}
146
147impl BitAnd for Irq {
148 type Output = Irq;
149
150 fn bitand(self, rhs: Irq) -> Irq {
151 Irq(self.0 & rhs.0)
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn the_bits_follow_table_13_29() {
161 let table = [
162 (Irq::TX_DONE, 0),
163 (Irq::RX_DONE, 1),
164 (Irq::PREAMBLE_DETECTED, 2),
165 (Irq::SYNC_WORD_VALID, 3),
166 (Irq::HEADER_VALID, 4),
167 (Irq::HEADER_ERROR, 5),
168 (Irq::CRC_ERROR, 6),
169 (Irq::CAD_DONE, 7),
170 (Irq::CAD_DETECTED, 8),
171 (Irq::TIMEOUT, 9),
172 (Irq::LR_FHSS_HOP, 14),
173 ];
174 let mut all = Irq::NONE;
175 for (irq, bit) in table {
176 assert_eq!(irq.bits(), 1 << bit);
177 all |= irq;
178 }
179 assert_eq!(all, Irq::ALL);
180 }
181
182 #[test]
183 fn the_register_travels_most_significant_byte_first() {
184 let irq = Irq::TIMEOUT | Irq::TX_DONE;
185 assert_eq!(irq.to_bytes(), [0x02, 0x01]);
186 assert_eq!(Irq::from_bytes([0x02, 0x01]), irq);
187 }
188
189 #[test]
190 fn a_set_answers_for_its_members() {
191 let pending = Irq::RX_DONE | Irq::CRC_ERROR;
192 assert!(pending.contains(Irq::RX_DONE));
193 assert!(!pending.contains(Irq::RX_DONE | Irq::TIMEOUT));
194 assert!(pending.intersects(Irq::CRC_ERROR | Irq::HEADER_ERROR));
195 assert_eq!(pending & Irq::CRC_ERROR, Irq::CRC_ERROR);
196 assert!(Irq::NONE.is_empty());
197 }
198}