use embedded_hal::{ blocking::{delay::DelayUs, spi::Transfer}, digital::v2::OutputPin, }; use crate::{ consts::*, error::{BusError, BusErrorToOtherError, RfError, SpiErrorToOtherError}, 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, ) -> Result<(), BusError<Spi::Error>> where Spi::Error: Debug, { self.send_command::<_, 0>(spi, &mut [CHANGE_STATE, s.into()])?; Ok(()) } pub fn get_temp<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, ) -> Result<f32, BusError<Spi::Error>> { 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], ) -> Result<(), BusError<Spi::Error>> { 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(()) } fn clear_interrupts<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, ) -> Result<(), BusError<Spi::Error>> { self.send_command::<_, 8>(spi, &mut [GET_INT_STATUS])?; Ok(()) } fn enable_interrupts<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, ) -> Result<(), BusError<Spi::Error>> { 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, ) -> Result<(), BusError<Spi::Error>> { 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, ) -> Result<bool, BusError<Spi::Error>> { 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, ) -> Result<bool, BusError<Spi::Error>> { 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, ) -> Result<bool, BusError<Spi::Error>> { let ch_pend = self.send_command::<_, 7>(spi, &mut [GET_INT_STATUS, 0xFF, 0xFF, 0xFF ^ (1 << 5)])?[6]; Ok(ch_pend & (1 << 5) != 0) } pub fn tx_fifo_space<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, ) -> Result<u8, BusError<Spi::Error>> { let fifo = self.send_command::<_, 2>(spi, &mut [FIFO_INFO, 0])?; Ok(fifo[1]) } pub fn rx_fifo_len<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, ) -> Result<u8, BusError<Spi::Error>> { let fifo = self.send_command::<_, 2>(spi, &mut [FIFO_INFO, 0])?; Ok(fifo[0]) } pub fn clear_fifo<Spi: Transfer<u8>>( &mut self, spi: &mut Spi, ) -> Result<(), BusError<Spi::Error>> { 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, ) -> Result<u16, BusError<Spi::Error>> { 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], ) -> Result<[u8; NUM_BYTES_OUT], BusError<Spi::Error>> { self.with_cs(|_| { spi.transfer(command).be()?; 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; let mut attempts = 0; while attempts < MAX_ATTEMPTS { if let Some(out) = self.with_cs(|s| { // 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 { return Ok(None); } let mut out = [0xFF; NUM_BYTES_OUT]; spi.transfer(&mut out).be()?; Ok(Some(out)) })? { return Ok(out); } attempts += 1; } Err(BusError::Timeout) } 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 } }