Skip to content
Snippets Groups Projects
internal_radio.rs 7.78 KiB
Newer Older
Stephen D's avatar
Stephen D committed
use embedded_hal::{
    blocking::{delay::DelayUs, spi::Transfer},
    digital::v2::OutputPin,
};

use crate::{
    consts::*,
Stephen D's avatar
Stephen D committed
    error::{BusError, BusErrorToOtherError, RfError, SpiErrorToOtherError},
Stephen D's avatar
Stephen D committed
    state::State,
};
use core::{convert::Infallible, fmt::Debug};

pub(crate) struct InternalRadio<SdnPin, CsPin, Delay> {
    sdn: SdnPin,
    cs: CsPin,
    delay: Delay,
}

impl<
        SdnPin: OutputPin<Error = Infallible>,
        CsPin: OutputPin<Error = Infallible>,
        Delay: DelayUs<u16>,
    > InternalRadio<SdnPin, CsPin, Delay>
{
    pub fn new<Spi: Transfer<u8>>(
        spi: &mut Spi,
        sdn: SdnPin,
        mut cs: CsPin,
        delay: Delay,
        config: &mut [u8],
    ) -> Result<Self, RfError<Spi::Error>>
    where
        Spi::Error: Debug,
    {
        cs.set_high().unwrap();

        let mut s = Self { sdn, cs, delay };

        s.reset();
        s.configure(spi, config).re()?;
        s.clear_interrupts(spi).re()?;
        s.enable_interrupts(spi).re()?;
        s.sleep(spi)?;

        Ok(s)
    }

    fn reset(&mut self) {
        self.sdn.set_high().unwrap();
        self.delay.delay_us(50_000);
        self.sdn.set_low().unwrap();
        self.delay.delay_us(50_000);
    }

    pub fn sleep<Spi: Transfer<u8>>(&mut self, spi: &mut Spi) -> Result<(), RfError<Spi::Error>>
    where
        Spi::Error: Debug,
    {
        if self.get_state(spi)? != State::Tx {
            self.set_state(spi, State::Sleep).re()?;
        }

        Ok(())
    }

    pub fn get_state<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
    ) -> Result<State, RfError<Spi::Error>>
    where
        Spi::Error: Debug,
    {
        let resp = self.send_command::<_, 1>(spi, &mut [READ_FRR_B]).re()?[0];

        Ok(resp.try_into()?)
    }

    pub fn set_state<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
        s: State,
Stephen D's avatar
Stephen D committed
    ) -> Result<(), BusError<Spi::Error>>
    where
        Spi::Error: Debug,
    {
Stephen D's avatar
Stephen D committed
        self.send_command::<_, 0>(spi, &mut [CHANGE_STATE, s.into()])?;

        Ok(())
    }

Stephen D's avatar
Stephen D committed
    pub fn get_temp<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
    ) -> Result<f32, BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        let result: f32 = self.get_adc(spi, ADC_CONV_TEMP, ADC_SPEED << 4, 4)?.into();

        Ok(899.0 * result / 4096.0 - 293.0)
    }

    pub fn get_rssi<Spi: Transfer<u8>>(&mut self, spi: &mut Spi) -> Result<f64, Spi::Error> {
        let frr = self.read_frr(spi)?;

        Ok((frr[0] as f64) / 2.0 - 136.0)
    }

    fn configure<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
        config: &mut [u8],
Stephen D's avatar
Stephen D committed
    ) -> Result<(), BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        let mut i = 0;
        while i < config.len() {
            let l: usize = config[i].into();
            self.send_command::<_, 0>(spi, &mut config[(i + 1)..(i + 1 + l)])?;

            i += l + 1;
        }

        Ok(())
    }

Stephen D's avatar
Stephen D committed
    fn clear_interrupts<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
    ) -> Result<(), BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        self.send_command::<_, 8>(spi, &mut [GET_INT_STATUS])?;

        Ok(())
    }

Stephen D's avatar
Stephen D committed
    fn enable_interrupts<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
    ) -> Result<(), BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        self.send_command::<_, 0>(
            spi,
            &mut [SET_PROPERTY, 0x01, 2, 0x00, 0b00000001, 0b00110011],
        )?;

        Ok(())
    }

    pub fn clear_ph_and_modem_interrupts<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
Stephen D's avatar
Stephen D committed
    ) -> Result<(), BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        self.send_command::<_, 0>(spi, &mut [GET_INT_STATUS, 0, 0, 0xFF])?;

        Ok(())
    }

    // also clears the interrupt
    pub fn packet_received_pending<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
Stephen D's avatar
Stephen D committed
    ) -> Result<bool, BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        let ph_pend =
            self.send_command::<_, 3>(spi, &mut [GET_INT_STATUS, 0xFF ^ (1 << 4), 0xFF, 0xFF])?[2];

        Ok((ph_pend & (1 << 4)) != 0)
    }

    // also clears the interrupt
    pub fn packet_sent_pending<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
Stephen D's avatar
Stephen D committed
    ) -> Result<bool, BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        let ph_pend =
            self.send_command::<_, 3>(spi, &mut [GET_INT_STATUS, 0xFF ^ (1 << 5), 0xFF, 0xFF])?[2];

        Ok((ph_pend & (1 << 5)) != 0)
    }

    // also clears the interrupt
    pub fn fifo_underflow_pending<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
Stephen D's avatar
Stephen D committed
    ) -> Result<bool, BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        let ch_pend =
            self.send_command::<_, 7>(spi, &mut [GET_INT_STATUS, 0xFF, 0xFF, 0xFF ^ (1 << 5)])?[6];

        Ok(ch_pend & (1 << 5) != 0)
    }

Stephen D's avatar
Stephen D committed
    pub fn tx_fifo_space<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
    ) -> Result<u8, BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        let fifo = self.send_command::<_, 2>(spi, &mut [FIFO_INFO, 0])?;

        Ok(fifo[1])
    }

Stephen D's avatar
Stephen D committed
    pub fn rx_fifo_len<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
    ) -> Result<u8, BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        let fifo = self.send_command::<_, 2>(spi, &mut [FIFO_INFO, 0])?;

        Ok(fifo[0])
    }

Stephen D's avatar
Stephen D committed
    pub fn clear_fifo<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
    ) -> Result<(), BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        self.send_command::<_, 0>(spi, &mut [FIFO_INFO, FIFO_CLEAR_RX | FIFO_CLEAR_TX])?;

        Ok(())
    }

    fn get_adc<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
        en: u8,
        cfg: u8,
        part: usize,
Stephen D's avatar
Stephen D committed
    ) -> Result<u16, BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        let data: [_; 6] = self.send_command(spi, &mut [GET_ADC_READING, en, cfg])?;

        Ok((u16::from(data[part]) << 8) | u16::from(data[part + 1]))
    }

    pub fn send_command<Spi: Transfer<u8>, const NUM_BYTES_OUT: usize>(
        &mut self,
        spi: &mut Spi,
        command: &mut [u8],
Stephen D's avatar
Stephen D committed
    ) -> Result<[u8; NUM_BYTES_OUT], BusError<Spi::Error>> {
Stephen D's avatar
Stephen D committed
        self.with_cs(|_| {
Stephen D's avatar
Stephen D committed
            spi.transfer(command).be()?;
Stephen D's avatar
Stephen D committed

            Ok(())
        })?;

        // Keep trying to read 1 byte until we get 0xFF
        // At that point, the SI4463 is ready to dump the bytes we care about
        // If we get back something other than 0xFF, toggle the CS line
        // and try again
        const MAX_ATTEMPTS: i32 = 100_000;
Stephen D's avatar
Stephen D committed
        let mut attempts = 0;
        while attempts < MAX_ATTEMPTS {
Stephen D's avatar
Stephen D committed
            if let Some(out) = self.with_cs(|s| {
Stephen D's avatar
Stephen D committed
                // TODO I don't think we're supposed to send this command on every check?
                // Should be leaving the cs line high and continuously polling for when we get 0xFF back
                // but it seems to work
                s.spi_transfer_byte(spi, READ_CMD_BUF).be()?;
                if s.spi_transfer_byte(spi, 0xFF).be()? != 0xFF {
Stephen D's avatar
Stephen D committed
                    return Ok(None);
                }

                let mut out = [0xFF; NUM_BYTES_OUT];
Stephen D's avatar
Stephen D committed
                spi.transfer(&mut out).be()?;
Stephen D's avatar
Stephen D committed

                Ok(Some(out))
            })? {
                return Ok(out);
            }
Stephen D's avatar
Stephen D committed

            attempts += 1;
Stephen D's avatar
Stephen D committed
        }
Stephen D's avatar
Stephen D committed

        Err(BusError::Timeout)
Stephen D's avatar
Stephen D committed
    }

    fn read_frr<Spi: Transfer<u8>>(&mut self, spi: &mut Spi) -> Result<[u8; 4], Spi::Error> {
        self.with_cs(|s| {
            s.spi_transfer_byte(spi, FRR_A_READ)?;

            let mut out = [0xFF; 4];
            spi.transfer(&mut out)?;

            Ok(out)
        })
    }

    pub fn spi_transfer_byte<Spi: Transfer<u8>>(
        &mut self,
        spi: &mut Spi,
        byte: u8,
    ) -> Result<u8, Spi::Error> {
        let mut buf = [byte];

        let buf = spi.transfer(&mut buf)?;

        Ok(buf[0])
    }

    // set the CS line low. Run the closure. Set it high
    pub fn with_cs<T, F: FnMut(&mut Self) -> T>(&mut self, mut f: F) -> T {
        self.cs.set_low().unwrap();
        self.delay.delay_us(1);

        let ret = f(self);

        self.cs.set_high().unwrap();
        self.delay.delay_us(1);

        ret
    }
}