rf24/
types.rs

1//! This module defines types used by various traits.
2//! These types are meant to be agnostic of the trait implementation.
3
4use core::{
5    fmt::{Display, Formatter, Result},
6    write,
7};
8
9use bitfield_struct::bitfield;
10
11/// Power Amplifier level. The units dBm (decibel-milliwatts or dB<sub>mW</sub>)
12/// represents a logarithmic signal loss.
13#[derive(Clone, Copy, Debug, PartialEq)]
14pub enum PaLevel {
15    /// | nRF24L01 | Si24R1 with<br>LNA Enabled | Si24R1 with<br>LNA Disabled |
16    /// | :-------:|:--------------------------:|:---------------------------:|
17    /// | -18 dBm | -6 dBm | -12 dBm |
18    Min,
19    /// | nRF24L01 | Si24R1 with<br>LNA Enabled | Si24R1 with<br>LNA Disabled |
20    /// | :-------:|:--------------------------:|:---------------------------:|
21    /// | -12 dBm | 0 dBm | -4 dBm |
22    Low,
23    /// | nRF24L01 | Si24R1 with<br>LNA Enabled | Si24R1 with<br>LNA Disabled |
24    /// | :-------:|:--------------------------:|:---------------------------:|
25    /// | -6 dBm | 3 dBm | 1 dBm |
26    High,
27    /// | nRF24L01 | Si24R1 with<br>LNA Enabled | Si24R1 with<br>LNA Disabled |
28    /// | :-------:|:--------------------------:|:---------------------------:|
29    /// | 0 dBm | 7 dBm | 4 dBm |
30    Max,
31}
32
33#[cfg(feature = "defmt")]
34#[cfg(target_os = "none")]
35impl defmt::Format for PaLevel {
36    fn format(&self, fmt: defmt::Formatter) {
37        match self {
38            PaLevel::Min => defmt::write!(fmt, "Min"),
39            PaLevel::Low => defmt::write!(fmt, "Low"),
40            PaLevel::High => defmt::write!(fmt, "High"),
41            PaLevel::Max => defmt::write!(fmt, "Max"),
42        }
43    }
44}
45
46impl PaLevel {
47    pub(crate) const fn into_bits(self) -> u8 {
48        match self {
49            PaLevel::Min => 0,
50            PaLevel::Low => 2,
51            PaLevel::High => 4,
52            PaLevel::Max => 6,
53        }
54    }
55    pub(crate) const fn from_bits(value: u8) -> Self {
56        match value {
57            0 => PaLevel::Min,
58            2 => PaLevel::Low,
59            4 => PaLevel::High,
60            _ => PaLevel::Max,
61        }
62    }
63}
64
65impl Display for PaLevel {
66    fn fmt(&self, f: &mut Formatter) -> Result {
67        match self {
68            PaLevel::Min => write!(f, "Min"),
69            PaLevel::Low => write!(f, "Low"),
70            PaLevel::High => write!(f, "High"),
71            PaLevel::Max => write!(f, "Max"),
72        }
73    }
74}
75
76/// How fast data moves through the air. Units are in bits per second (bps).
77#[derive(Clone, Copy, Debug, PartialEq)]
78pub enum DataRate {
79    /// represents 1 Mbps
80    Mbps1,
81    /// represents 2 Mbps
82    Mbps2,
83    /// represents 250 Kbps
84    Kbps250,
85}
86
87impl DataRate {
88    pub(crate) const fn into_bits(self) -> u8 {
89        match self {
90            DataRate::Mbps1 => 0,
91            DataRate::Mbps2 => 0x8,
92            DataRate::Kbps250 => 0x20,
93        }
94    }
95    pub(crate) const fn from_bits(value: u8) -> Self {
96        match value {
97            0x8 => DataRate::Mbps2,
98            0x20 => DataRate::Kbps250,
99            _ => DataRate::Mbps1,
100        }
101    }
102}
103
104#[cfg(feature = "defmt")]
105#[cfg(target_os = "none")]
106impl defmt::Format for DataRate {
107    fn format(&self, fmt: defmt::Formatter) {
108        match self {
109            DataRate::Mbps1 => defmt::write!(fmt, "1 Mbps"),
110            DataRate::Mbps2 => defmt::write!(fmt, "2 Mbps"),
111            DataRate::Kbps250 => defmt::write!(fmt, "250 Kbps"),
112        }
113    }
114}
115
116impl Display for DataRate {
117    fn fmt(&self, f: &mut Formatter) -> Result {
118        match self {
119            DataRate::Mbps1 => write!(f, "1 Mbps"),
120            DataRate::Mbps2 => write!(f, "2 Mbps"),
121            DataRate::Kbps250 => write!(f, "250 Kbps"),
122        }
123    }
124}
125
126/// The length of a CRC checksum that is used (if any).
127///
128/// Cyclical Redundancy Checking (CRC) is commonly used to ensure data integrity.
129#[derive(Clone, Copy, Debug, PartialEq)]
130pub enum CrcLength {
131    /// represents no CRC checksum is used
132    Disabled,
133    /// represents CRC 8 bit checksum is used
134    Bit8,
135    /// represents CRC 16 bit checksum is used
136    Bit16,
137}
138
139impl CrcLength {
140    pub(crate) const fn into_bits(self) -> u8 {
141        match self {
142            CrcLength::Disabled => 0,
143            CrcLength::Bit8 => 8,
144            CrcLength::Bit16 => 12,
145        }
146    }
147    pub(crate) const fn from_bits(value: u8) -> Self {
148        match value {
149            0 => CrcLength::Disabled,
150            8 => CrcLength::Bit8,
151            _ => CrcLength::Bit16,
152        }
153    }
154}
155
156#[cfg(feature = "defmt")]
157#[cfg(target_os = "none")]
158impl defmt::Format for CrcLength {
159    fn format(&self, fmt: defmt::Formatter) {
160        match self {
161            CrcLength::Disabled => defmt::write!(fmt, "disabled"),
162            CrcLength::Bit8 => defmt::write!(fmt, "8 bit"),
163            CrcLength::Bit16 => defmt::write!(fmt, "16 bit"),
164        }
165    }
166}
167
168impl Display for CrcLength {
169    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
170        match self {
171            CrcLength::Disabled => write!(f, "disabled"),
172            CrcLength::Bit8 => write!(f, "8 bit"),
173            CrcLength::Bit16 => write!(f, "16 bit"),
174        }
175    }
176}
177
178/// The possible states of a FIFO.
179#[derive(Clone, Copy, Debug, PartialEq)]
180pub enum FifoState {
181    /// Represent the state of a FIFO when it is full.
182    Full,
183    /// Represent the state of a FIFO when it is empty.
184    Empty,
185    /// Represent the state of a FIFO when it is not full but not empty either.
186    Occupied,
187}
188
189#[cfg(feature = "defmt")]
190#[cfg(target_os = "none")]
191impl defmt::Format for FifoState {
192    #[cfg(feature = "defmt")]
193    fn format(&self, fmt: defmt::Formatter) {
194        match self {
195            FifoState::Empty => defmt::write!(fmt, "Empty"),
196            FifoState::Full => defmt::write!(fmt, "Full"),
197            FifoState::Occupied => defmt::write!(fmt, "Occupied"),
198        }
199    }
200}
201
202impl Display for FifoState {
203    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
204        match self {
205            FifoState::Empty => write!(f, "Empty"),
206            FifoState::Full => write!(f, "Full"),
207            FifoState::Occupied => write!(f, "Occupied"),
208        }
209    }
210}
211
212/// A struct used to describe the different interrupt events.
213///
214/// To instantiate an object with flags that have different values:
215/// ```
216/// let flags = StatusFlags::default() // all flags are false
217///     .with_rx_dr(true); // assert only `rx_dr` flags
218/// ```
219/// Use [`StatusFlags::default`] to instantiate all flags set to false.
220/// Use [`StatusFlags::new`] to instantiate all flags set to true.
221#[bitfield(u8, new = false, order = Msb)]
222pub struct StatusFlags {
223    #[bits(1)]
224    _padding: u8,
225
226    /// A flag to describe if RX Data Ready to read.
227    #[bits(1, access = RO)]
228    pub rx_dr: bool,
229
230    /// A flag to describe if TX Data Sent.
231    #[bits(1, access = RO)]
232    pub tx_ds: bool,
233
234    /// A flag to describe if TX Data Failed.
235    #[bits(1, access = RO)]
236    pub tx_df: bool,
237
238    #[bits(3, access = RO)]
239    pub(crate) rx_pipe: u8,
240
241    #[bits(1, access = RO)]
242    pub(crate) tx_full: bool,
243}
244
245#[cfg(feature = "defmt")]
246impl defmt::Format for StatusFlags {
247    fn format(&self, fmt: defmt::Formatter) {
248        defmt::write!(
249            fmt,
250            "StatusFlags rx_dr: {}, tx_ds: {}, tx_df: {}",
251            self.rx_dr,
252            self.tx_ds,
253            self.tx_df
254        )
255    }
256}
257
258impl StatusFlags {
259    /// A mask to isolate only the IRQ flags. Useful for STATUS and CONFIG registers.
260    pub(crate) const IRQ_MASK: u8 = 0x70;
261
262    /// A convenience constructor similar to [`StatusFlags::default`] except
263    /// all fields are set to `true`.
264    pub fn new() -> Self {
265        Self::from_bits(0x70)
266    }
267
268    /// A flag to describe if RX Data Ready to read.
269    pub fn with_rx_dr(self, flag: bool) -> Self {
270        let new_val = self.into_bits() & !(1 << Self::RX_DR_OFFSET);
271        if flag {
272            Self::from_bits(new_val | (1 << Self::RX_DR_OFFSET))
273        } else {
274            Self::from_bits(new_val)
275        }
276    }
277
278    /// A flag to describe if TX Data Sent.
279    pub fn with_tx_ds(self, flag: bool) -> Self {
280        let new_val = self.into_bits() & !(1 << Self::TX_DS_OFFSET);
281        if flag {
282            Self::from_bits(new_val | (1 << Self::TX_DS_OFFSET))
283        } else {
284            Self::from_bits(new_val)
285        }
286    }
287
288    /// A flag to describe if TX Data Failed.
289    pub fn with_tx_df(self, flag: bool) -> Self {
290        let new_val = self.into_bits() & !(1 << Self::TX_DF_OFFSET);
291        if flag {
292            Self::from_bits(new_val | (1 << Self::TX_DF_OFFSET))
293        } else {
294            Self::from_bits(new_val)
295        }
296    }
297}
298
299impl Display for StatusFlags {
300    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
301        write!(
302            f,
303            "StatusFlags rx_dr: {}, tx_ds: {}, tx_df: {}",
304            self.rx_dr(),
305            self.tx_ds(),
306            self.tx_df()
307        )
308    }
309}
310
311#[cfg(test)]
312mod test {
313    use crate::StatusFlags;
314
315    use super::{CrcLength, DataRate, FifoState, PaLevel};
316    extern crate std;
317    use std::{format, string::String};
318
319    fn display_crc(param: CrcLength, expected: String) -> bool {
320        format!("{param}") == expected
321    }
322
323    #[test]
324    fn crc_8bit() {
325        assert!(display_crc(CrcLength::Bit8, String::from("8 bit")));
326    }
327
328    #[test]
329    fn crc_16bit() {
330        assert!(display_crc(CrcLength::Bit16, String::from("16 bit")));
331    }
332
333    #[test]
334    fn crc_disable() {
335        assert!(display_crc(CrcLength::Disabled, String::from("disabled")));
336    }
337
338    fn display_fifo_state(param: FifoState, expected: String) -> bool {
339        format!("{param}") == expected
340    }
341
342    #[test]
343    fn fifo_state_empty() {
344        assert!(display_fifo_state(FifoState::Empty, String::from("Empty")));
345    }
346
347    #[test]
348    fn fifo_state_full() {
349        assert!(display_fifo_state(FifoState::Full, String::from("Full")));
350    }
351
352    #[test]
353    fn fifo_state_occupied() {
354        assert!(display_fifo_state(
355            FifoState::Occupied,
356            String::from("Occupied")
357        ));
358    }
359
360    fn display_data_rate(param: DataRate, expected: String) -> bool {
361        format!("{param}") == expected
362    }
363
364    #[test]
365    fn data_rate_1mbps() {
366        assert!(display_data_rate(DataRate::Mbps1, String::from("1 Mbps")));
367    }
368
369    #[test]
370    fn data_rate_2mbps() {
371        assert!(display_data_rate(DataRate::Mbps2, String::from("2 Mbps")));
372    }
373
374    #[test]
375    fn data_rate_250kbps() {
376        assert!(display_data_rate(
377            DataRate::Kbps250,
378            String::from("250 Kbps")
379        ));
380    }
381
382    fn display_pa_level(param: PaLevel, expected: String) -> bool {
383        format!("{param}") == expected
384    }
385
386    #[test]
387    fn pa_level_min() {
388        assert!(display_pa_level(PaLevel::Min, String::from("Min")));
389    }
390
391    #[test]
392    fn pa_level_low() {
393        assert!(display_pa_level(PaLevel::Low, String::from("Low")));
394    }
395
396    #[test]
397    fn pa_level_high() {
398        assert!(display_pa_level(PaLevel::High, String::from("High")));
399    }
400
401    #[test]
402    fn pa_level_max() {
403        assert!(display_pa_level(PaLevel::Max, String::from("Max")));
404    }
405
406    #[test]
407    fn display_flags() {
408        assert_eq!(
409            format!("{}", StatusFlags::default()),
410            String::from("StatusFlags rx_dr: false, tx_ds: false, tx_df: false")
411        );
412    }
413
414    fn set_flags(rx_dr: bool, tx_ds: bool, tx_df: bool) {
415        let flags = StatusFlags::default()
416            .with_rx_dr(rx_dr)
417            .with_tx_ds(tx_ds)
418            .with_tx_df(tx_df);
419        assert_eq!(flags.rx_dr(), rx_dr);
420        assert_eq!(flags.tx_ds(), tx_ds);
421        assert_eq!(flags.tx_df(), tx_df);
422    }
423
424    #[test]
425    fn flags_0x50() {
426        set_flags(true, false, true);
427    }
428
429    #[test]
430    fn flags_0x20() {
431        set_flags(false, true, false);
432    }
433}