rf4463 0.1.0

A simple, no-std library for interfacing with the SI4463 transceiver IC
Documentation
#![no_std]

pub mod config;
mod consts;
pub mod error;
mod internal_radio;
mod internal_state;
pub(crate) mod state;

use core::{convert::Infallible, fmt::Debug};
use embedded_hal::{
    blocking::{delay::DelayUs, spi::Transfer},
    digital::v2::OutputPin,
};

use consts::*;
use error::{RfError, SpiErrorToOtherError, TransferError};
use internal_state::InternalState;
use state::State;

use crate::internal_radio::InternalRadio;

pub struct Rf4463<const PACKET_LEN: usize, Spi, SdnPin, CsPin, Delay> {
    radio: InternalRadio<Spi, SdnPin, CsPin, Delay>,
    state: InternalState<PACKET_LEN>,
    rx_forever: bool,
}

impl<
        const PACKET_LEN: usize,
        Spi: Transfer<u8>,
        SdnPin: OutputPin<Error = Infallible>,
        CsPin: OutputPin<Error = Infallible>,
        Delay: DelayUs<u16>,
    > Rf4463<PACKET_LEN, Spi, SdnPin, CsPin, Delay>
where
    Spi::Error: Debug,
{
    pub fn new(
        spi: Spi,
        sdn: SdnPin,
        mut cs: CsPin,
        delay: Delay,
        config: &mut [u8],
    ) -> Result<Self, RfError<Spi::Error>> {
        assert!(
            PACKET_LEN < 8192,
            "Packet length cannot be above 8191 bytes"
        );

        cs.set_high().unwrap();

        Ok(Self {
            radio: InternalRadio::new(spi, sdn, cs, delay, config)?,
            state: InternalState::Idle,
            rx_forever: false,
        })
    }

    /// Do not use this in the middle of an RX event
    /// Otherwise weird things will happen! I think this is a limitation
    /// of the si4463, but it's not documented anywhere.
    /// TX may or may not have a similar issue - I haven't tested it
    pub fn get_temp(&mut self) -> Result<f32, Spi::Error> {
        self.radio.get_temp()
    }

    pub fn get_rssi(&mut self) -> Result<f64, Spi::Error> {
        self.radio.get_rssi()
    }

    pub fn is_idle(&mut self) -> bool {
        matches!(self.state, InternalState::Idle)
    }

    pub fn start_rx(&mut self, rx_forever: bool) -> Result<(), Spi::Error> {
        self.radio.clear_fifo()?;
        self.radio.clear_ph_and_modem_interrupts()?;

        self.state = InternalState::Rx {
            data: [0; PACKET_LEN],
            i: 0,
            received: false,
            rssi: None,
        };

        self.radio.send_command::<0>(&mut [
            START_RX,
            0,
            0,
            (PACKET_LEN >> 8).try_into().unwrap(),
            (PACKET_LEN & 0xff).try_into().unwrap(),
            State::Rx.into(),
            if rx_forever {
                State::Rx.into()
            } else {
                State::Sleep.into()
            },
            State::Rx.into(),
        ])?;

        self.rx_forever = rx_forever;

        Ok(())
    }

    pub fn finish_rx(&mut self) -> Result<Option<([u8; PACKET_LEN], f64)>, Spi::Error> {
        let pkt = match self.state {
            InternalState::Rx {
                data,
                received,
                i,
                rssi,
            } if i == PACKET_LEN && received => {
                let rssi = match rssi {
                    Some(x) => x,
                    None => self.get_rssi()?,
                };

                let ret = Some((data, rssi));

                if self.rx_forever {
                    self.radio.clear_ph_and_modem_interrupts()?;

                    self.state = InternalState::Rx {
                        data: [0; PACKET_LEN],
                        i: 0,
                        received: false,
                        rssi: None,
                    };
                } else {
                    self.state = InternalState::Idle;
                }

                ret
            }

            _ => None,
        };

        Ok(pkt)
    }

    pub fn start_tx(&mut self, mut data: [u8; PACKET_LEN]) -> Result<(), Spi::Error> {
        self.radio.clear_fifo()?;
        self.radio.clear_ph_and_modem_interrupts()?;

        // 128-byte TX buffer
        let i = data.len().min(128);

        self.radio.with_cs(|s| {
            s.spi_transfer_byte(WRITE_TX_FIFO)?;
            s.spi.transfer(&mut data[0..i])?;

            Ok(())
        })?;

        self.radio.send_command::<0>(&mut [
            START_TX,
            0,
            u8::from(State::Sleep) << 4,
            (PACKET_LEN >> 8).try_into().unwrap(),
            (PACKET_LEN & 0xff).try_into().unwrap(),
            0,
            0,
        ])?;

        self.state = InternalState::Tx { data, i };

        Ok(())
    }

    pub fn interrupt(&mut self) -> Result<(), TransferError<Spi::Error>> {
        if self.radio.fifo_underflow_pending().te()? {
            return Err(TransferError::FifoOverflow);
        }

        match &mut self.state {
            InternalState::Idle => {
                // we were not expecting an interrupt
                // ignore
            }

            InternalState::Rx { received, .. } => {
                if self.radio.packet_received_pending().te()? {
                    *received = true;
                }

                if let Err(e) = self.rx_step() {
                    // something went wrong. Go back to idle
                    self.state = InternalState::Idle;
                    let _ = self.radio.sleep();

                    return Err(e);
                }
            }

            InternalState::Tx { .. } => {
                if self.radio.packet_sent_pending().te()? {
                    self.state = InternalState::Idle;
                }

                if let Err(e) = self.tx_step() {
                    // something went wrong. Go back to idle
                    self.state = InternalState::Idle;
                    let _ = self.radio.sleep();

                    return Err(e);
                }
            }
        }

        Ok(())
    }

    fn rx_step(&mut self) -> Result<(), TransferError<Spi::Error>> {
        let (data, i, rssi) = match &mut self.state {
            InternalState::Rx { data, i, rssi, .. } => (data, i, rssi),
            _ => return Ok(()),
        };

        let len: usize = self.radio.rx_fifo_len().te()?.into();
        if len == 0 {
            return Ok(());
        }

        if len + *i > PACKET_LEN {
            return Err(TransferError::TooMuchData);
        }

        if self.radio.fifo_underflow_pending().te()? {
            return Err(TransferError::FifoOverflow);
        }

        let data = &mut data[*i..(*i + len)];
        self.radio
            .with_cs(|s| {
                s.spi_transfer_byte(READ_RX_FIFO)?;
                s.spi.transfer(data)?;

                Ok(())
            })
            .te()?;

        if rssi.is_none() {
            *rssi = Some(self.radio.get_rssi().te()?)
        }

        *i += len;

        Ok(())
    }

    fn tx_step(&mut self) -> Result<(), TransferError<Spi::Error>> {
        let (data, i) = match &mut self.state {
            InternalState::Tx { data, i, .. } => (data, i),
            _ => return Ok(()),
        };

        // only look at the slice we haven't sent yet
        let data = &mut data[*i..];

        let len: usize = self.radio.tx_fifo_space().te()?.into();
        let len = len.min(data.len());
        if len == 0 {
            return Ok(());
        }

        if self.radio.fifo_underflow_pending().te()? {
            return Err(TransferError::FifoOverflow);
        }

        self.radio.with_cs(|s| {
            s.spi_transfer_byte(WRITE_TX_FIFO).te()?;
            s.spi.transfer(&mut data[0..len]).te()?;

            Ok(())
        })?;

        *i += len;

        Ok(())
    }
}