#![no_std] pub mod config; mod consts; pub mod error; mod internal_radio; mod internal_state; pub mod rx; 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, TxError}; use internal_state::InternalState; use rx::RxPacket; use state::State; use crate::internal_radio::InternalRadio; const VCXO_FREQ: u32 = 30_000_000; /// All the rx_buf methods MUST be passed the same rx_buf /// All the tx_buf methods MUST be passed the same tx_buf /// We do this because we don't keep that data locally for space reasons /// And we don't keep references to it for lifetime reasons! pub struct Rf4463<SdnPin, CsPin, Delay> { radio: InternalRadio<SdnPin, CsPin, Delay>, state: InternalState, rx_forever: bool, channel: u8, } impl< SdnPin: OutputPin<Error = Infallible>, CsPin: OutputPin<Error = Infallible>, Delay: DelayUs<u16>, > Rf4463<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(); Ok(Self { radio: InternalRadio::new(spi, sdn, cs, delay, config)?, state: InternalState::Idle, rx_forever: false, channel: 0, }) } /// 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<Spi: Transfer<u8>>(&mut self, spi: &mut Spi) -> Result<f32, Spi::Error> { self.radio.get_temp(spi) } pub fn get_rssi<Spi: Transfer<u8>>(&mut self, spi: &mut Spi) -> Result<f64, Spi::Error> { self.radio.get_rssi(spi) } pub fn sleep<Spi: Transfer<u8>>(&mut self, spi: &mut Spi) -> Result<(), RfError<Spi::Error>> where Spi::Error: Debug, { self.state = InternalState::Idle; self.radio.sleep(spi) } pub fn set_channel(&mut self, channel: u8) { self.channel = channel; } /// Frequency given in Hz pub fn set_frequency<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, freq: u32, ) -> Result<(), Spi::Error> { let outdiv; let band; match freq { x if x < 177_000_000 => { outdiv = 24; band = 5; } x if x < 239_000_000 => { outdiv = 16; band = 4; } x if x < 353_000_000 => { outdiv = 12; band = 3; } x if x < 525_000_000 => { outdiv = 8; band = 2; } x if x < 705_000_000 => { outdiv = 6; band = 1; } _ => { outdiv = 4; band = 0; } } let f_pfd = 2 * VCXO_FREQ / outdiv; let n: u8 = (freq / f_pfd - 1).try_into().unwrap(); let ratio = freq as f32 / f_pfd as f32; let rest = ratio - n as f32; let m = (rest * 524_288.0) as u32; // set band parameter self.radio .send_command::<_, 0>(spi, &mut [SET_PROPERTY, 0x20, 0x01, 0x51, 8 + band])?; // set pll parameters self.radio.send_command::<_, 0>( spi, &mut [ SET_PROPERTY, 0x40, 0x04, 0x00, n, ((m >> 16) & 0xFF).try_into().unwrap(), ((m >> 8) & 0xFF).try_into().unwrap(), (m & 0xFF).try_into().unwrap(), ], )?; Ok(()) } pub fn is_idle(&mut self) -> bool { matches!(self.state, InternalState::Idle) } // in other words, we're actively receiving a packet (not just waiting for one) pub fn is_busy_rxing<Spi: Transfer<u8>>(&mut self, spi: &mut Spi) -> Result<bool, Spi::Error> { if let InternalState::Rx { i, .. } = self.state { return Ok(i > 0 || self.radio.rx_fifo_len(spi)? > 0); } Ok(false) } // Len is none when using a variable-length packet pub fn start_rx<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, len: Option<usize>, rx_forever: bool, ) -> Result<(), Spi::Error> { self.radio.clear_fifo(spi)?; self.radio.clear_ph_and_modem_interrupts(spi)?; let len = len.unwrap_or(0); self.state = InternalState::Rx { i: 0, received: false, rssi: None, }; self.radio.send_command::<_, 0>( spi, &mut [ START_RX, self.channel, 0, (len >> 8).try_into().unwrap(), (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<'a, Spi: Transfer<u8>>( &mut self, spi: &mut Spi, rx_buf: &'a mut [u8], ) -> Result<Option<RxPacket<'a>>, Spi::Error> { let pkt = match self.state { InternalState::Rx { received, i, rssi } if received => { let rssi = match rssi { Some(x) => x, None => self.get_rssi(spi)?, }; let ret = Some(RxPacket::new(rx_buf, i, rssi)); if self.rx_forever { self.radio.clear_ph_and_modem_interrupts(spi)?; self.state = InternalState::Rx { i: 0, received: false, rssi: None, }; } else { self.state = InternalState::Idle; } ret } _ => None, }; Ok(pkt) } /// Panics if data length is > 8191 pub fn start_tx<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, tx_buf: &[u8], ) -> Result<(), TxError<Spi::Error>> where Spi::Error: Debug, { let len = tx_buf.len(); assert!(len < 8192, "Packet length cannot be above 8191 bytes"); if matches!(self.state, InternalState::Rx { .. }) { // if we're in the middle of rxing, we need to cancel that first // otherwise it gets confused self.radio.set_state(spi, State::Sleep).txe()?; } self.radio.clear_fifo(spi).txe()?; self.radio.clear_ph_and_modem_interrupts(spi).txe()?; // 128-byte TX buffer let i = tx_buf.len().min(128); let mut segment = [0; 128]; segment[..i].copy_from_slice(&tx_buf[..i]); self.radio.with_cs(|s| { s.spi_transfer_byte(spi, WRITE_TX_FIFO).txe()?; spi.transfer(&mut segment[..i]).txe()?; Ok(()) })?; self.radio .send_command::<_, 0>( spi, &mut [ START_TX, self.channel, u8::from(State::Sleep) << 4, (len >> 8).try_into().unwrap(), (len & 0xff).try_into().unwrap(), 0, 0, ], ) .txe()?; self.state = InternalState::Tx { i, len }; Ok(()) } /// You only *need* to pass in rx_buf or tx_buf depending on what we're doing, /// But you're free to pass in both without any issues pub fn interrupt<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, rx_buf: Option<&mut [u8]>, tx_buf: Option<&[u8]>, ) -> Result<(), TransferError<Spi::Error>> where Spi::Error: Debug, { if self.radio.fifo_underflow_pending(spi).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(spi).te()? { *received = true; } if let Err(e) = self.rx_step(spi, rx_buf.expect("We're receiving but there's no rx buf")) { // something went wrong. Go back to idle self.state = InternalState::Idle; let _ = self.radio.sleep(spi); return Err(e); } } InternalState::Tx { .. } => { if self.radio.packet_sent_pending(spi).te()? { self.state = InternalState::Idle; } if let Err(e) = self.tx_step( spi, tx_buf.expect("We're transmitting but there's no tx buf"), ) { // something went wrong. Go back to idle self.state = InternalState::Idle; let _ = self.radio.sleep(spi); return Err(e); } } } Ok(()) } fn rx_step<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, rx_buf: &mut [u8], ) -> Result<(), TransferError<Spi::Error>> where Spi::Error: Debug, { let (i, rssi) = match &mut self.state { InternalState::Rx { i, rssi, .. } => (i, rssi), _ => return Ok(()), }; let fifo_len: usize = self.radio.rx_fifo_len(spi).te()?.into(); if fifo_len == 0 { return Ok(()); } if fifo_len + *i > rx_buf.len() { return Err(TransferError::TooMuchData); } if self.radio.fifo_underflow_pending(spi).te()? { return Err(TransferError::FifoOverflow); } let data = &mut rx_buf[*i..(*i + fifo_len)]; self.radio .with_cs(|s| { s.spi_transfer_byte(spi, READ_RX_FIFO)?; spi.transfer(data)?; Ok(()) }) .te()?; if rssi.is_none() { *rssi = Some(self.radio.get_rssi(spi).te()?) } *i += fifo_len; Ok(()) } fn tx_step<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, tx_buf: &[u8], ) -> Result<(), TransferError<Spi::Error>> where Spi::Error: Debug, { let (data_len, i) = match &mut self.state { InternalState::Tx { i, len } => (*len, i), _ => return Ok(()), }; // only look at the slice we haven't sent yet let data = &tx_buf[*i..data_len]; let len: usize = self.radio.tx_fifo_space(spi).te()?.into(); let len = len.min(data.len()); if len == 0 { return Ok(()); } // we need a mutable reference on data for our transfer function // so we copy that data into a temporary buffer const FIFO_BUFFER_LEN: usize = 129; let mut buf = [0; FIFO_BUFFER_LEN]; buf[..len].copy_from_slice(&data[..len]); if self.radio.fifo_underflow_pending(spi).te()? { return Err(TransferError::FifoOverflow); } self.radio.with_cs(|s| { s.spi_transfer_byte(spi, WRITE_TX_FIFO).te()?; spi.transfer(&mut buf[0..len]).te()?; Ok(()) })?; *i += len; Ok(()) } }