radio_at86rf23x/
lib.rs

1use core::fmt::Debug;
2use core::marker::PhantomData;
3
4use embedded_hal::delay::blocking::DelayUs;
5use log::{debug, warn};
6
7
8use radio::{BasicInfo, Channel as _, Interrupts, Registers as _, State as _};
9
10pub mod base;
11pub mod device;
12pub mod prelude;
13
14use base::Base;
15use device::*;
16
17pub use device::CHANNELS;
18
19/// AT86RF23x driver object
20pub struct At86Rf23x<B: Base> {
21    hal: B,
22    auto_crc: bool,
23}
24
25/// AT86RF23x device configuration
26#[derive(Clone, PartialEq, Debug)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29pub struct Config {
30    pub xtal_mode: XtalMode,
31    pub channel: Ch,
32    pub auto_crc: bool,
33}
34
35impl Default for Config {
36    fn default() -> Self {
37        Self {
38            xtal_mode: XtalMode::InternalOscillator,
39            channel: Ch {
40                bitrate: OqpskDataRate::D250kbps,
41                channel: CHANNELS[0],
42            },
43            auto_crc: true,
44        }
45    }
46}
47
48/// Error type for AT86RF23x
49#[derive(Debug, Clone, PartialEq)]
50#[cfg_attr(feature = "thiserror", derive(thiserror::Error))]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53pub enum Error<SpiErr: Debug, PinErr: Debug, DelayErr: Debug> {
54    #[error("SPI error: {0}")]
55    /// Communications (SPI or UART) error
56    Spi(SpiErr),
57
58    #[error("Pin error: {0}")]
59    /// Pin control error
60    Pin(PinErr),
61
62    #[error("Delay error: {0}")]
63    /// Delay error
64    Delay(DelayErr),
65
66    #[error("Unsupported state command")]
67    /// Unsupported state command
68    Unsupported,
69
70    #[error("No response from device")]
71    /// No response from device
72    NoResponse,
73
74    #[error("Unexpected register value (reg: 0x{0:02x} val: 0x:{1:02x}")]
75    /// No response from device
76    UnexpectedValue(u8, u8),
77
78    #[cfg(feature="common-modulation")]
79    #[error("Unsupported modulation configuration")]
80    /// Unsupported modulation
81    Modulation(radio::modulation::ModError),
82}
83
84#[cfg(feature="common-modulation")]
85impl <SpiErr: Debug, PinErr: Debug, DelayErr: Debug> From<radio::modulation::ModError> for Error<B::SpiErr, B::PinErr, B::DelayErr> {
86    fn from(m: radio::modulation::ModError) -> Self {
87        Error::Modulation(m)
88    }
89}
90
91#[derive(Copy, Clone, PartialEq, Debug, strum_macros::Display)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
93#[cfg_attr(feature = "defmt", derive(defmt::Format))]
94pub enum State {
95    Init,
96    Idle,
97    PllOn,
98    BusyTx,
99    RxOn,
100    BusyRx,
101    Sleep,
102    Busy,
103}
104
105impl radio::RadioState for State {
106    fn idle() -> Self {
107        State::Idle
108    }
109
110    fn sleep() -> Self {
111        State::Sleep
112    }
113}
114
115/// Part information
116#[derive(Debug, Clone, PartialEq)]
117pub struct Info {
118    pub part: Part,
119    pub ver: u8,
120    pub mfr: u16,
121}
122
123impl<B> At86Rf23x<B>
124where
125    B: Base,
126{
127    pub fn new(hal: B, config: Config) -> Result<Self, Error<B::SpiErr, B::PinErr, B::DelayErr>> {
128        let mut s = Self {
129            hal,
130            auto_crc: config.auto_crc,
131        };
132
133        debug!("Connecting to device");
134
135        // Deassert sleep
136        s.hal.slp_tr(false)?;
137
138        // Perform reset
139        s.hal.reset()?;
140
141        // TODO: enable AWAKE_END IRQ to detect move to TRX_OFF
142
143        // Write TRX_OFF to enter ready state
144        s.write_register::<TrxCmd>(TrxCmd::TrxOff)?;
145
146        // TODO: Wait for TRX_OFF state (PLL lock)
147
148        // Read info
149        let i = s.info()?;
150        if i.part == Part::None && i.ver == 0 && i.mfr == 0 {
151            warn!("Init failed, communication error");
152            return Err(Error::NoResponse);
153        }
154
155        // TODO: check voltages are okay
156
157        // TODO: configure xtal
158
159        // Set channel
160        s.set_channel(&config.channel)?;
161
162        // TODO: set CCA mode
163
164        // Enable promiscuous mode
165        s.update_register::<XahCtrl1, _>(|r| r.with_aack_prom_mode(true))?;
166
167        if s.auto_crc {
168            // Enable auto-crc in TX
169            s.update_register::<TrxCtrl1, _>(|r| r.with_tx_auto_crc_on(true))?;
170        }
171
172        // Enable dynamic frame buffer protection
173        s.update_register::<TrxCtrl2, _>(|r| r.with_rx_safe_mode(true))?;
174
175        debug!("Device info: {:02x?}", i);
176
177        Ok(s)
178    }
179
180    /// Read device information
181    pub fn info(&mut self) -> Result<Info, Error<B::SpiErr, B::PinErr, B::DelayErr>> {
182        let i = Info {
183            part: self.read_register::<PartNum>().map(|p| p.part())?,
184            ver: self.read_register::<VersionNum>()?.into(),
185            mfr: u16::from_le_bytes([
186                self.read_register::<ManId0>()?.into(),
187                self.read_register::<ManId1>()?.into(),
188            ]),
189        };
190        Ok(i)
191    }
192
193    /// Set 802.15.4 PAN ID
194    pub fn set_pan_id(&mut self, pan_id: u16) -> Result<(), Error<B::SpiErr, B::PinErr, B::DelayErr>> {
195        let b = pan_id.to_le_bytes();
196        self.write_register::<PanId0>(b[0].into())?;
197        self.write_register::<PanId1>(b[1].into())?;
198        Ok(())
199    }
200
201    /// Set 802.15.4 Short (16-bit) address
202    pub fn set_short_addr(&mut self, short_addr: u16) -> Result<(), Error<B::SpiErr, B::PinErr, B::DelayErr>> {
203        let b = short_addr.to_le_bytes();
204        self.write_register::<ShortAddr0>(b[0].into())?;
205        self.write_register::<ShortAddr1>(b[1].into())?;
206        Ok(())
207    }
208
209    /// Set 802.15.4 Long (64-bit) address
210    pub fn set_long_addr(&mut self, long_addr: u64) -> Result<(), Error<B::SpiErr, B::PinErr, B::DelayErr>> {
211        let b = long_addr.to_le_bytes();
212        self.write_register::<IeeeAddr0>(b[0].into())?;
213        self.write_register::<IeeeAddr1>(b[1].into())?;
214        self.write_register::<IeeeAddr2>(b[2].into())?;
215        self.write_register::<IeeeAddr3>(b[3].into())?;
216        self.write_register::<IeeeAddr0>(b[4].into())?;
217        self.write_register::<IeeeAddr1>(b[5].into())?;
218        self.write_register::<IeeeAddr2>(b[6].into())?;
219        self.write_register::<IeeeAddr3>(b[7].into())?;
220        Ok(())
221    }
222
223    /// Write to the device FIFO
224    pub fn fifo_write(&mut self, data: &[u8]) -> Result<(), Error<B::SpiErr, B::PinErr, B::DelayErr>> {
225        self.hal.spi_write(&[CommandFlags::BUFF_WR.bits()], data)
226    }
227
228    /// Read from the device FIFO
229    pub fn fifo_read(&mut self, data: &mut [u8]) -> Result<(), Error<B::SpiErr, B::PinErr, B::DelayErr>> {
230        self.hal.spi_read(&[CommandFlags::BUFF_RD.bits()], data)
231    }
232
233    /// Write to the device SRAM
234    pub fn sram_write(
235        &mut self,
236        offset: u8,
237        data: &[u8],
238    ) -> Result<(), Error<B::SpiErr, B::PinErr, B::DelayErr>> {
239        self.hal
240            .spi_write(&[CommandFlags::SRAM_WR.bits(), offset], data)
241    }
242
243    /// Read from the device SRAM
244    pub fn sram_read(
245        &mut self,
246        offset: u8,
247        data: &mut [u8],
248    ) -> Result<(), Error<B::SpiErr, B::PinErr, B::DelayErr>> {
249        self.hal
250            .spi_read(&[CommandFlags::SRAM_RD.bits(), offset], data)
251    }
252}
253
254impl<B> radio::Registers<u8> for At86Rf23x<B>
255where
256    B: Base,
257{
258    type Error = Error<B::SpiErr, B::PinErr, B::DelayErr>;
259
260    /// Read a register value
261    fn read_register<R: radio::Register<Word = u8>>(&mut self) -> Result<R, Self::Error> {
262        let mut d = [0u8];
263        self.hal
264            .spi_read(&[R::ADDRESS as u8 | CommandFlags::REG_RD.bits()], &mut d)?;
265
266        R::try_from(d[0]).map_err(|_e| Error::UnexpectedValue(R::ADDRESS, d[0]))
267    }
268
269    /// Write a register value
270    fn write_register<R: radio::Register<Word = u8>>(
271        &mut self,
272        value: R,
273    ) -> Result<(), Self::Error> {
274        self.hal.spi_write(
275            &[R::ADDRESS as u8 | CommandFlags::REG_WR.bits()],
276            &[value.into()],
277        )
278    }
279}
280
281impl<B> radio::Registers<u16> for At86Rf23x<B>
282where
283    B: Base,
284{
285    type Error = Error<B::SpiErr, B::PinErr, B::DelayErr>;
286
287    /// Read a register value
288    fn read_register<R: radio::Register<Word = u16>>(&mut self) -> Result<R, Self::Error> {
289        let mut r = [0u8, 0u8];
290        self.hal
291            .spi_read(&[R::ADDRESS as u8 | CommandFlags::REG_RD.bits()], &mut r)?;
292
293        let d = u16::from_le_bytes(r);
294
295        R::try_from(d).map_err(|_e| Error::UnexpectedValue(R::ADDRESS, r[0]))
296    }
297
298    /// Write a register value
299    fn write_register<R: radio::Register<Word = u16>>(
300        &mut self,
301        value: R,
302    ) -> Result<(), Self::Error> {
303        let v = u16::to_le_bytes(value.into());
304
305        self.hal
306            .spi_write(&[R::ADDRESS as u8 | CommandFlags::REG_WR.bits()], &v)
307    }
308}
309
310impl<B> radio::State for At86Rf23x<B>
311where
312    B: Base,
313{
314    type State = State;
315    type Error = Error<B::SpiErr, B::PinErr, B::DelayErr>;
316
317    fn set_state(&mut self, s: State) -> Result<(), Self::Error> {
318        // Convert requested state to commands
319        let v = match s {
320            State::Idle => TrxCmd::ForceTrxOff,
321            State::PllOn => TrxCmd::PllOn,
322            State::RxOn => TrxCmd::RxOn,
323            State::Sleep => todo!("Set slp_tr high to sleep"),
324            //State::DeepSleep => todo!("Call prep deep sleep, set slp_tr"),
325            _ => return Err(Error::Unsupported),
326        };
327
328        debug!("Set state cmd: {:?} (requested: {:?})", v, s);
329
330        // TODO: check state is not StateTransitionInProgress before applying
331
332        // Write command
333        self.write_register::<TrxCmd>(v)?;
334
335        Ok(())
336    }
337
338    fn get_state(&mut self) -> Result<Self::State, Self::Error> {
339        use TrxStatus::*;
340
341        // Read status register
342        let trx_status = self.read_register::<TrxStatus>()?;
343
344        // Convert to state enum
345        let s = match trx_status {
346            POn => State::Init,
347            BusyRx | BusyRxAack => State::BusyRx,
348            BusyTx | BusyTxAret => State::BusyTx,
349            RxOn => State::RxOn,
350            TrxOff => State::Idle,
351            PllOn => State::PllOn,
352            Sleep | PrepDeepSleep => State::Sleep,
353            RxAackOn => State::BusyRx,
354            TxAretOn => State::BusyTx,
355            StateTransition => State::Busy,
356        };
357
358        debug!("TRX status: {:?} state: {:?}", trx_status, s);
359
360        Ok(s)
361    }
362}
363
364impl<B> radio::Interrupts for At86Rf23x<B>
365where
366    B: Base,
367{
368    type Irq = Irqs;
369    type Error = Error<B::SpiErr, B::PinErr, B::DelayErr>;
370
371    /// Read interrupts, flags are always cleared on read
372    fn get_interrupts(&mut self, _: bool) -> Result<Self::Irq, Self::Error> {
373        let irqs = self.read_register::<Irqs>()?;
374
375        if !irqs.is_empty() {
376            debug!("IRQs: {:?}", irqs);
377        }
378
379        Ok(irqs)
380    }
381}
382
383/// AT86RF23x Channel Object
384#[derive(Copy, Clone, PartialEq, Debug)]
385#[cfg_attr(feature = "defmt", derive(defmt::Format))]
386pub struct Ch {
387    /// Channel bitrate
388    pub bitrate: OqpskDataRate,
389    /// Channel frequency
390    pub channel: Channel,
391}
392
393#[cfg(feature="common-modulation")]
394impl TryFrom<radio::modulation::gfsk::GfskChannel> for Ch {
395    type Error = radio::modulation::ModError;
396
397    fn try_from(ch: radio::modulation::gfsk::GfskChannel) -> Result<Self, Self::Error> {
398        use radio::modulation::ModError;
399
400        let bitrate = OqpskDataRate::try_from(ch.bitrate_bps)
401                .map_err(|_| ModError::UnsupportedBitrate)?;
402
403        let channel = Channel::try_from(ch.freq_khz)
404                .map_err(|_| ModError::UnsupportedFrequency)?;
405
406        if ch.bw_khz != 5 {
407            return Err(ModError::UnsupportedBandwidth);
408        }
409
410        Ok(Ch{ bitrate, channel })
411    }
412}
413
414impl<B> radio::Channel for At86Rf23x<B>
415where
416    B: Base,
417{
418    type Channel = Ch;
419    type Error = Error<B::SpiErr, B::PinErr, B::DelayErr>;
420
421    fn set_channel(&mut self, ch: &Ch) -> Result<(), Self::Error> {
422        // Ensure we're idle
423        self.set_state(State::PllOn)?;
424
425        // TODO: support alternate channel configs?
426
427        // Load new channel
428        self.update_register::<PhyCcCca, _>(|r| r.with_channel(ch.channel))?;
429
430        // Load new bitrate
431        self.update_register::<TrxCtrl2, _>(|r| r.with_oqpsk_data_rate(ch.bitrate))?;
432
433        Ok(())
434    }
435}
436
437impl<B> DelayUs for At86Rf23x<B>
438where
439    B: Base,
440{
441    type Error = Error<B::SpiErr, B::PinErr, B::DelayErr>;
442
443    fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> {
444        self.hal.delay_us(us)
445    }
446}
447
448impl<B> radio::Power for At86Rf23x<B>
449where
450    B: Base,
451{
452    type Error = Error<B::SpiErr, B::PinErr, B::DelayErr>;
453
454    fn set_power(&mut self, _power: i8) -> Result<(), Self::Error> {
455        // TODO: convert power from int to nearest power value
456        let p = Power::P4dBm;
457
458        // Update power register
459        self.update_register::<PhyTxPwr, _>(|r| r.with_tx_pwr(p))?;
460
461        Ok(())
462    }
463}
464
465impl<B> radio::Rssi for At86Rf23x<B>
466where
467    B: Base,
468{
469    type Error = Error<B::SpiErr, B::PinErr, B::DelayErr>;
470
471    fn poll_rssi(&mut self) -> Result<i16, Self::Error> {
472        let r = self.read_register::<PhyRssi>()?;
473        Ok(-94 + r.rssi() as i16 * 3)
474    }
475}
476impl<B> radio::Transmit for At86Rf23x<B>
477where
478    B: Base,
479{
480    type Error = Error<B::SpiErr, B::PinErr, B::DelayErr>;
481
482    fn start_transmit(&mut self, data: &[u8]) -> Result<(), Self::Error> {
483        // Ensure we're idle and the PLL is on
484        self.set_state(State::PllOn)?;
485
486        // Load data into FIFO
487        // TODO: check length is valid
488
489        debug!("TX data: {:02x?}", data);
490
491        // Calculate length
492        let mut len = data.len() as u8;
493        if self.auto_crc {
494            len += 2;
495        }
496
497        // First length
498        self.sram_write(0, &[len])?;
499
500        // Then data
501        self.sram_write(1, data)?;
502
503        // Add CRC padding if required
504        if self.auto_crc {
505            let crc = [0xFFu8; 2];
506            self.sram_write(1 + data.len() as u8, &crc)?;
507        }
508
509        // Setup IRQs
510        let irqs = Irqs::TRX_END | Irqs::TRX_UR | Irqs::AMI;
511        self.write_register::<IrqMask>(irqs.bits().into())?;
512        let _ = self.get_interrupts(true)?;
513
514        debug!("Entering TX state");
515
516        // Set to TX state
517        self.write_register::<TrxCmd>(TrxCmd::TxStart)?;
518
519        Ok(())
520    }
521
522    fn check_transmit(&mut self) -> Result<bool, Self::Error> {
523        // Poll on IRQ pin if available
524        // TODO: feature gate this like other impls?
525        if !self.hal.irq()? {
526            return Ok(false);
527        }
528
529        // Check for RX_START TRX_END RX_CRC_VALID AMI (if extended mode enabled) IRQs, BUSY_RX state
530        let irqs = self.read_register::<Irqs>()?;
531
532        if !irqs.is_empty() {
533            debug!("TX IRQs: {:?}", irqs);
534        }
535
536        // TRX_END signals receive completion
537        if irqs.contains(Irqs::TRX_END) {
538            debug!("TX complete");
539            Ok(true)
540        } else {
541            Ok(false)
542        }
543    }
544}
545
546impl<B> radio::Receive for At86Rf23x<B>
547where
548    B: Base,
549{
550    type Error = Error<B::SpiErr, B::PinErr, B::DelayErr>;
551
552    type Info = BasicInfo;
553
554    fn start_receive(&mut self) -> Result<(), Self::Error> {
555        // Ensure we're idle and the PLL is on
556        // TODO: skip this if not required? (ie. already in PLL_ON or RX_ON)
557        self.set_state(State::PllOn)?;
558
559        // Setup IRQs
560        let irqs = Irqs::RX_START | Irqs::TRX_END | Irqs::TRX_UR | Irqs::AMI;
561        self.write_register::<IrqMask>(irqs.bits().into())?;
562        let _ = self.get_interrupts(true)?;
563
564        debug!("Entering RX state");
565
566        // Set to receive state
567        self.write_register::<TrxCmd>(TrxCmd::RxOn)?;
568
569        Ok(())
570    }
571
572    /// Poll to check for receive completion
573    fn check_receive(&mut self, _restart: bool) -> Result<bool, Self::Error> {
574        // Poll on IRQ pin if available
575        // TODO: feature gate this like other impls?
576        if !self.hal.irq()? {
577            return Ok(false);
578        }
579
580        // Check for RX_START TRX_END RX_CRC_VALID AMI (if extended mode enabled) IRQs, BUSY_RX state
581        let irqs = self.read_register::<Irqs>()?;
582
583        if !irqs.is_empty() {
584            debug!("RX IRQs: {:?}", irqs);
585        }
586
587        // TRX_END signals receive completion
588        if irqs.contains(Irqs::TRX_END) {
589            debug!("RX complete");
590            Ok(true)
591
592        // TODO: RX_CRC_VALID, AMI IRQs for extended mode
593
594        // TRX_UR signifies FIFO underflow
595        } else if irqs.contains(Irqs::TRX_UR) {
596            todo!()
597
598        // Receiving in progress
599        } else if irqs.contains(Irqs::RX_START) {
600            debug!("RX start");
601            Ok(true)
602
603        // Nothing happening
604        } else {
605            Ok(false)
606        }
607    }
608
609    fn get_received(&mut self, buff: &mut [u8]) -> Result<(usize, Self::Info), Self::Error> {
610        // Read RSSI (using energy detect per datasheet recommendation)
611        let ed = self.read_register::<PhyEdLevel>()?;
612        let info = if ed.ed_level() < 0x54 {
613            BasicInfo::new(-94 + ed.ed_level() as i16, u16::MIN)
614        } else {
615            BasicInfo::default()
616        };
617
618        // TODO: apparently different 231 and 233 devices operate differently here...
619        // https://github.com/msolters/arduino-at86rf233/blob/master/at86rf2xx.cpp#L253
620
621        // Read first byte PHR to discern length
622        self.sram_read(0, &mut buff[..1])?;
623        let n = buff[0] as usize;
624
625        // Read following data
626        self.sram_read(1, &mut buff[1..][..n])?;
627
628        // TODO: if we have auto CRC enabled should we check here / remove from returned array?
629
630        // Return packet length
631        Ok((n, info))
632    }
633}
634#[cfg(test)]
635mod tests {
636    #[test]
637    fn it_works() {
638        let result = 2 + 2;
639        assert_eq!(result, 4);
640    }
641}