Initial add for sx126x
This commit is contained in:
		
							
								
								
									
										171
									
								
								embassy-lora/src/sx126x/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								embassy-lora/src/sx126x/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| use core::future::Future; | ||||
|  | ||||
| use defmt::Format; | ||||
| use embedded_hal::digital::v2::OutputPin; | ||||
| use embedded_hal_async::digital::Wait; | ||||
| use embedded_hal_async::spi::*; | ||||
| use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig}; | ||||
| use lorawan_device::async_device::Timings; | ||||
|  | ||||
| mod sx126x_lora; | ||||
| use sx126x_lora::LoRa; | ||||
|  | ||||
| use self::sx126x_lora::mod_params::RadioError; | ||||
|  | ||||
| /// Semtech Sx126x LoRa peripheral | ||||
| pub struct Sx126xRadio<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> | ||||
| where | ||||
|     SPI: SpiBus<u8, Error = BUS> + 'static, | ||||
|     CS: OutputPin + 'static, | ||||
|     RESET: OutputPin + 'static, | ||||
|     ANTRX: OutputPin + 'static, | ||||
|     ANTTX: OutputPin + 'static, | ||||
|     WAIT: Wait + 'static, | ||||
|     BUS: Error + Format + 'static, | ||||
| { | ||||
|     pub lora: LoRa<SPI, CS, RESET, ANTRX, ANTTX, WAIT>, | ||||
| } | ||||
|  | ||||
| impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> Sx126xRadio<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> | ||||
| where | ||||
|     SPI: SpiBus<u8, Error = BUS> + 'static, | ||||
|     CS: OutputPin + 'static, | ||||
|     RESET: OutputPin + 'static, | ||||
|     ANTRX: OutputPin + 'static, | ||||
|     ANTTX: OutputPin + 'static, | ||||
|     WAIT: Wait + 'static, | ||||
|     BUS: Error + Format + 'static, | ||||
| { | ||||
|     pub async fn new( | ||||
|         spi: SPI, | ||||
|         cs: CS, | ||||
|         reset: RESET, | ||||
|         antenna_rx: ANTRX, | ||||
|         antenna_tx: ANTTX, | ||||
|         dio1: WAIT, | ||||
|         busy: WAIT, | ||||
|         enable_public_network: bool, | ||||
|     ) -> Result<Self, RadioError<BUS>> { | ||||
|         let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy); | ||||
|         lora.init().await?; | ||||
|         lora.set_lora_modem(enable_public_network).await?; | ||||
|         Ok(Self { lora }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> Timings for Sx126xRadio<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> | ||||
| where | ||||
|     SPI: SpiBus<u8, Error = BUS> + 'static, | ||||
|     CS: OutputPin + 'static, | ||||
|     RESET: OutputPin + 'static, | ||||
|     ANTRX: OutputPin + 'static, | ||||
|     ANTTX: OutputPin + 'static, | ||||
|     WAIT: Wait + 'static, | ||||
|     BUS: Error + Format + 'static, | ||||
| { | ||||
|     fn get_rx_window_offset_ms(&self) -> i32 { | ||||
|         -500 | ||||
|     } | ||||
|     fn get_rx_window_duration_ms(&self) -> u32 { | ||||
|         800 | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> PhyRxTx for Sx126xRadio<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> | ||||
| where | ||||
|     SPI: SpiBus<u8, Error = BUS> + 'static, | ||||
|     CS: OutputPin + 'static, | ||||
|     RESET: OutputPin + 'static, | ||||
|     ANTRX: OutputPin + 'static, | ||||
|     ANTTX: OutputPin + 'static, | ||||
|     WAIT: Wait + 'static, | ||||
|     BUS: Error + Format + 'static, | ||||
| { | ||||
|     type PhyError = RadioError<BUS>; | ||||
|  | ||||
|     type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm | ||||
|     where | ||||
|         SPI: 'm, | ||||
|         CS: 'm, | ||||
|         RESET: 'm, | ||||
|         ANTRX: 'm, | ||||
|         ANTTX: 'm, | ||||
|         WAIT: 'm, | ||||
|         BUS: 'm; | ||||
|  | ||||
|     fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> { | ||||
|         trace!("TX START"); | ||||
|         async move { | ||||
|             self.lora | ||||
|                 .set_tx_config( | ||||
|                     config.pw, | ||||
|                     config.rf.spreading_factor.into(), | ||||
|                     config.rf.bandwidth.into(), | ||||
|                     config.rf.coding_rate.into(), | ||||
|                     4, | ||||
|                     false, | ||||
|                     true, | ||||
|                     false, | ||||
|                     0, | ||||
|                     false, | ||||
|                 ) | ||||
|                 .await?; | ||||
|             self.lora.set_max_payload_length(buffer.len() as u8).await?; | ||||
|             self.lora.set_channel(config.rf.frequency).await?; | ||||
|             self.lora.send(buffer, 0xffffff).await?; | ||||
|             self.lora.process_irq(None, None, None).await?; | ||||
|             trace!("TX DONE"); | ||||
|             return Ok(0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm | ||||
|     where | ||||
|         SPI: 'm, | ||||
|         CS: 'm, | ||||
|         RESET: 'm, | ||||
|         ANTRX: 'm, | ||||
|         ANTTX: 'm, | ||||
|         WAIT: 'm, | ||||
|         BUS: 'm; | ||||
|  | ||||
|     fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> { | ||||
|         trace!("RX START"); | ||||
|         async move { | ||||
|             self.lora | ||||
|                 .set_rx_config( | ||||
|                     config.spreading_factor.into(), | ||||
|                     config.bandwidth.into(), | ||||
|                     config.coding_rate.into(), | ||||
|                     4, | ||||
|                     4, | ||||
|                     false, | ||||
|                     0u8, | ||||
|                     true, | ||||
|                     false, | ||||
|                     0, | ||||
|                     false, | ||||
|                     true, | ||||
|                 ) | ||||
|                 .await?; | ||||
|             self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; | ||||
|             self.lora.set_channel(config.frequency).await?; | ||||
|             self.lora.rx(90 * 1000).await?; | ||||
|             let mut received_len = 0u8; | ||||
|             self.lora | ||||
|                 .process_irq(Some(receiving_buffer), Some(&mut received_len), None) | ||||
|                 .await?; | ||||
|             trace!("RX DONE"); | ||||
|  | ||||
|             let packet_status = self.lora.get_latest_packet_status(); | ||||
|             let mut rssi = 0i16; | ||||
|             let mut snr = 0i8; | ||||
|             if packet_status.is_some() { | ||||
|                 rssi = packet_status.unwrap().rssi as i16; | ||||
|                 snr = packet_status.unwrap().snr; | ||||
|             } | ||||
|  | ||||
|             Ok((received_len as usize, RxQuality::new(rssi, snr))) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										251
									
								
								embassy-lora/src/sx126x/sx126x_lora/board_specific.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								embassy-lora/src/sx126x/sx126x_lora/board_specific.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | ||||
| use embassy_time::{Duration, Timer}; | ||||
| use embedded_hal::digital::v2::OutputPin; | ||||
| use embedded_hal_async::digital::Wait; | ||||
| use embedded_hal_async::spi::SpiBus; | ||||
|  | ||||
| use super::mod_params::RadioError::*; | ||||
| use super::mod_params::*; | ||||
| use super::LoRa; | ||||
|  | ||||
| // Defines the time required for the TCXO to wakeup [ms]. | ||||
| const BRD_TCXO_WAKEUP_TIME: u32 = 10; | ||||
|  | ||||
| // Provides board-specific functionality for Semtech SX126x-based boards.  Use #[cfg(feature = "board_type")] to specify unique board functionality. | ||||
| // The base implementation supports the RAK4631 board. | ||||
|  | ||||
| impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> LoRa<SPI, CS, RESET, ANTRX, ANTTX, WAIT> | ||||
| where | ||||
|     SPI: SpiBus<u8, Error = BUS>, | ||||
|     CS: OutputPin, | ||||
|     RESET: OutputPin, | ||||
|     ANTRX: OutputPin, | ||||
|     ANTTX: OutputPin, | ||||
|     WAIT: Wait, | ||||
| { | ||||
|     // De-initialize the radio I/Os pins interface.  Useful when going into MCU low power modes. | ||||
|     pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         Ok(()) // no operation currently | ||||
|     } | ||||
|  | ||||
|     // Initialize the TCXO power pin | ||||
|     pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         let timeout = self.brd_get_board_tcxo_wakeup_time() << 6; | ||||
|         self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout) | ||||
|             .await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Initialize RF switch control pins | ||||
|     pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_dio2_as_rf_switch_ctrl(true).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Initialize the radio debug pins | ||||
|     pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         Ok(()) // no operation currently | ||||
|     } | ||||
|  | ||||
|     // Hardware reset of the radio | ||||
|     pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         Timer::after(Duration::from_millis(10)).await; | ||||
|         self.reset.set_low().map_err(|_| Reset)?; | ||||
|         Timer::after(Duration::from_millis(20)).await; | ||||
|         self.reset.set_high().map_err(|_| Reset)?; | ||||
|         Timer::after(Duration::from_millis(10)).await; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Wait while the busy pin is high | ||||
|     pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         self.busy.wait_for_low().await.map_err(|_| Busy)?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Wake up the radio | ||||
|     pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         self.cs.set_low().map_err(|_| CS)?; | ||||
|         self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?; | ||||
|         self.spi.write(&[0x00]).await.map_err(SPI)?; | ||||
|         self.cs.set_high().map_err(|_| CS)?; | ||||
|  | ||||
|         self.brd_wait_on_busy().await?; | ||||
|         self.brd_set_operating_mode(RadioMode::StandbyRC); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Send a command that writes data to the radio | ||||
|     pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_check_device_ready().await?; | ||||
|  | ||||
|         self.cs.set_low().map_err(|_| CS)?; | ||||
|         self.spi.write(&[op_code.value()]).await.map_err(SPI)?; | ||||
|         self.spi.write(buffer).await.map_err(SPI)?; | ||||
|         self.cs.set_high().map_err(|_| CS)?; | ||||
|  | ||||
|         if op_code != OpCode::SetSleep { | ||||
|             self.brd_wait_on_busy().await?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Send a command that reads data from the radio, filling the provided buffer and returning a status | ||||
|     pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> { | ||||
|         let mut status = [0u8]; | ||||
|         let mut input = [0u8]; | ||||
|  | ||||
|         self.sub_check_device_ready().await?; | ||||
|  | ||||
|         self.cs.set_low().map_err(|_| CS)?; | ||||
|         self.spi.write(&[op_code.value()]).await.map_err(SPI)?; | ||||
|         self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?; | ||||
|         for i in 0..buffer.len() { | ||||
|             self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; | ||||
|             buffer[i] = input[0]; | ||||
|         } | ||||
|         self.cs.set_high().map_err(|_| CS)?; | ||||
|  | ||||
|         self.brd_wait_on_busy().await?; | ||||
|  | ||||
|         Ok(status[0]) | ||||
|     } | ||||
|  | ||||
|     // Write one or more bytes of data to the radio memory | ||||
|     pub(super) async fn brd_write_registers( | ||||
|         &mut self, | ||||
|         start_register: Register, | ||||
|         buffer: &[u8], | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_check_device_ready().await?; | ||||
|  | ||||
|         self.cs.set_low().map_err(|_| CS)?; | ||||
|         self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?; | ||||
|         self.spi | ||||
|             .write(&[ | ||||
|                 ((start_register.addr() & 0xFF00) >> 8) as u8, | ||||
|                 (start_register.addr() & 0x00FF) as u8, | ||||
|             ]) | ||||
|             .await | ||||
|             .map_err(SPI)?; | ||||
|         self.spi.write(buffer).await.map_err(SPI)?; | ||||
|         self.cs.set_high().map_err(|_| CS)?; | ||||
|  | ||||
|         self.brd_wait_on_busy().await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Read one or more bytes of data from the radio memory | ||||
|     pub(super) async fn brd_read_registers( | ||||
|         &mut self, | ||||
|         start_register: Register, | ||||
|         buffer: &mut [u8], | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         let mut input = [0u8]; | ||||
|  | ||||
|         self.sub_check_device_ready().await?; | ||||
|  | ||||
|         self.cs.set_low().map_err(|_| CS)?; | ||||
|         self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?; | ||||
|         self.spi | ||||
|             .write(&[ | ||||
|                 ((start_register.addr() & 0xFF00) >> 8) as u8, | ||||
|                 (start_register.addr() & 0x00FF) as u8, | ||||
|                 0x00u8, | ||||
|             ]) | ||||
|             .await | ||||
|             .map_err(SPI)?; | ||||
|         for i in 0..buffer.len() { | ||||
|             self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; | ||||
|             buffer[i] = input[0]; | ||||
|         } | ||||
|         self.cs.set_high().map_err(|_| CS)?; | ||||
|  | ||||
|         self.brd_wait_on_busy().await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Write data to the buffer holding the payload in the radio | ||||
|     pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_check_device_ready().await?; | ||||
|  | ||||
|         self.cs.set_low().map_err(|_| CS)?; | ||||
|         self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?; | ||||
|         self.spi.write(&[offset]).await.map_err(SPI)?; | ||||
|         self.spi.write(buffer).await.map_err(SPI)?; | ||||
|         self.cs.set_high().map_err(|_| CS)?; | ||||
|  | ||||
|         self.brd_wait_on_busy().await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Read data from the buffer holding the payload in the radio | ||||
|     pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError<BUS>> { | ||||
|         let mut input = [0u8]; | ||||
|  | ||||
|         self.sub_check_device_ready().await?; | ||||
|  | ||||
|         self.cs.set_low().map_err(|_| CS)?; | ||||
|         self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?; | ||||
|         self.spi.write(&[offset]).await.map_err(SPI)?; | ||||
|         self.spi.write(&[0x00]).await.map_err(SPI)?; | ||||
|         for i in 0..buffer.len() { | ||||
|             self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; | ||||
|             buffer[i] = input[0]; | ||||
|         } | ||||
|         self.cs.set_high().map_err(|_| CS)?; | ||||
|  | ||||
|         self.brd_wait_on_busy().await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the radio output power | ||||
|     pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_tx_params(power, RampTime::Ramp40Us).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Get the radio type | ||||
|     pub(super) fn brd_get_radio_type(&mut self) -> RadioType { | ||||
|         #[cfg(feature = "rak4631")] | ||||
|         RadioType::SX1262 | ||||
|     } | ||||
|  | ||||
|     // Initialize the RF switch I/O pins interface | ||||
|     pub(super) async fn brd_ant_sw_on(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         Ok(()) // no operation currently | ||||
|     } | ||||
|  | ||||
|     // De-initialize the RF switch I/O pins interface for MCU low power modes | ||||
|     pub(super) async fn brd_ant_sw_off(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         Ok(()) // no operation currently | ||||
|     } | ||||
|  | ||||
|     // Check if the given RF frequency is supported by the hardware | ||||
|     pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result<bool, RadioError<BUS>> { | ||||
|         #[cfg(feature = "rak4631")] | ||||
|         Ok(true) // all frequencies currently supported for the SX1262 within a rak4631 | ||||
|     } | ||||
|  | ||||
|     // Get the duration required for the TCXO to wakeup [ms]. | ||||
|     pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 { | ||||
|         BRD_TCXO_WAKEUP_TIME | ||||
|     } | ||||
|  | ||||
|     /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process | ||||
|     pub(super) async fn brd_get_dio1_pin_state( | ||||
|         &mut self, | ||||
|     ) -> Result<u32, RadioError<BUS>> { | ||||
|         Ok(0) | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     // Get the current radio operatiing mode | ||||
|     pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode { | ||||
|         self.operating_mode | ||||
|     } | ||||
|  | ||||
|     // Set/Update the current radio operating mode  This function is only required to reflect the current radio operating mode when processing interrupts. | ||||
|     pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) { | ||||
|         self.operating_mode = mode; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										735
									
								
								embassy-lora/src/sx126x/sx126x_lora/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										735
									
								
								embassy-lora/src/sx126x/sx126x_lora/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,735 @@ | ||||
| #![allow(dead_code)] | ||||
|  | ||||
| use embassy_time::{Duration, Timer}; | ||||
| use embedded_hal::digital::v2::OutputPin; | ||||
| use embedded_hal_async::digital::Wait; | ||||
| use embedded_hal_async::spi::SpiBus; | ||||
|  | ||||
| mod board_specific; | ||||
| pub mod mod_params; | ||||
| mod subroutine; | ||||
|  | ||||
| use mod_params::RadioError::*; | ||||
| use mod_params::*; | ||||
|  | ||||
| // Syncwords for public and private networks | ||||
| const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444; | ||||
| const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424; | ||||
|  | ||||
| // Maximum number of registers that can be added to the retention list | ||||
| const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4; | ||||
|  | ||||
| // Possible LoRa bandwidths | ||||
| const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz]; | ||||
|  | ||||
| // Radio complete wakeup time with margin for temperature compensation [ms] | ||||
| const RADIO_WAKEUP_TIME: u32 = 3; | ||||
|  | ||||
| /// Provides high-level access to Semtech SX126x-based boards | ||||
| pub struct LoRa<SPI, CS, RESET, ANTRX, ANTTX, WAIT> { | ||||
|     spi: SPI, | ||||
|     cs: CS, | ||||
|     reset: RESET, | ||||
|     antenna_rx: ANTRX, | ||||
|     antenna_tx: ANTTX, | ||||
|     dio1: WAIT, | ||||
|     busy: WAIT, | ||||
|     operating_mode: RadioMode, | ||||
|     rx_continuous: bool, | ||||
|     max_payload_length: u8, | ||||
|     modulation_params: Option<ModulationParams>, | ||||
|     packet_type: PacketType, | ||||
|     packet_params: Option<PacketParams>, | ||||
|     packet_status: Option<PacketStatus>, | ||||
|     image_calibrated: bool, | ||||
|     frequency_error: u32, | ||||
| } | ||||
|  | ||||
| impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> LoRa<SPI, CS, RESET, ANTRX, ANTTX, WAIT> | ||||
| where | ||||
|     SPI: SpiBus<u8, Error = BUS>, | ||||
|     CS: OutputPin, | ||||
|     RESET: OutputPin, | ||||
|     ANTRX: OutputPin, | ||||
|     ANTTX: OutputPin, | ||||
|     WAIT: Wait, | ||||
| { | ||||
|     /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time () | ||||
|     pub fn new(spi: SPI, cs: CS, reset: RESET, antenna_rx: ANTRX, antenna_tx: ANTTX, dio1: WAIT, busy: WAIT) -> Self { | ||||
|         Self { | ||||
|             spi, | ||||
|             cs, | ||||
|             reset, | ||||
|             antenna_rx, | ||||
|             antenna_tx, | ||||
|             dio1, | ||||
|             busy, | ||||
|             operating_mode: RadioMode::Sleep, | ||||
|             rx_continuous: false, | ||||
|             max_payload_length: 0xFFu8, | ||||
|             modulation_params: None, | ||||
|             packet_type: PacketType::LoRa, | ||||
|             packet_params: None, | ||||
|             packet_status: None, | ||||
|             image_calibrated: false, | ||||
|             frequency_error: 0u32, // where is volatile FrequencyError modified ??? | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Initialize the radio | ||||
|     pub async fn init(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_init().await?; | ||||
|         self.sub_set_standby(StandbyMode::RC).await?; | ||||
|         self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?; | ||||
|         self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?; | ||||
|         self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?; | ||||
|         self.sub_set_dio_irq_params( | ||||
|             IrqMask::All.value(), | ||||
|             IrqMask::All.value(), | ||||
|             IrqMask::None.value(), | ||||
|             IrqMask::None.value(), | ||||
|         ) | ||||
|         .await?; | ||||
|         self.add_register_to_retention_list(Register::RxGain.addr()).await?; | ||||
|         self.add_register_to_retention_list(Register::TxModulation.addr()) | ||||
|             .await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Return current radio state | ||||
|     pub fn get_status(&mut self) -> RadioState { | ||||
|         match self.brd_get_operating_mode() { | ||||
|             RadioMode::Transmit => RadioState::TxRunning, | ||||
|             RadioMode::Receive => RadioState::RxRunning, | ||||
|             RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting, | ||||
|             _ => RadioState::Idle, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired) | ||||
|     pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_packet_type(PacketType::LoRa).await?; | ||||
|         if enable_public_network { | ||||
|             self.brd_write_registers( | ||||
|                 Register::LoRaSyncword, | ||||
|                 &[ | ||||
|                     ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8, | ||||
|                     (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8, | ||||
|                 ], | ||||
|             ) | ||||
|             .await?; | ||||
|         } else { | ||||
|             self.brd_write_registers( | ||||
|                 Register::LoRaSyncword, | ||||
|                 &[ | ||||
|                     ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8, | ||||
|                     (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8, | ||||
|                 ], | ||||
|             ) | ||||
|             .await?; | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Sets the channel frequency | ||||
|     pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_rf_frequency(frequency).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /* Checks if the channel is free for the given time.  This is currently not implemented until a substitute | ||||
|         for switching to the FSK modem is found. | ||||
|  | ||||
|     pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool; | ||||
|     */ | ||||
|  | ||||
|     /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts.   Ensure set_lora_modem() is called befrorehand. | ||||
|     /// After calling this function either set_rx_config() or set_tx_config() must be called. | ||||
|     pub async fn get_random_value(&mut self) -> Result<u32, RadioError<BUS>> { | ||||
|         self.sub_set_dio_irq_params( | ||||
|             IrqMask::None.value(), | ||||
|             IrqMask::None.value(), | ||||
|             IrqMask::None.value(), | ||||
|             IrqMask::None.value(), | ||||
|         ) | ||||
|         .await?; | ||||
|  | ||||
|         let result = self.sub_get_random().await?; | ||||
|         Ok(result) | ||||
|     } | ||||
|  | ||||
|     /// Set the reception parameters for the LoRa modem (only).  Ensure set_lora_modem() is called befrorehand. | ||||
|     ///   spreading_factor     [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] | ||||
|     ///   bandwidth            [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] | ||||
|     ///   coding_rate          [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] | ||||
|     ///   preamble_length      length in symbols (the hardware adds 4 more symbols) | ||||
|     ///   symb_timeout         RxSingle timeout value in symbols | ||||
|     ///   fixed_len            fixed length packets [0: variable, 1: fixed] | ||||
|     ///   payload_len          payload length when fixed length is used | ||||
|     ///   crc_on               [0: OFF, 1: ON] | ||||
|     ///   freq_hop_on          intra-packet frequency hopping [0: OFF, 1: ON] | ||||
|     ///   hop_period           number of symbols between each hop | ||||
|     ///   iq_inverted          invert IQ signals [0: not inverted, 1: inverted] | ||||
|     ///   rx_continuous        reception mode [false: single mode, true: continuous mode] | ||||
|     pub async fn set_rx_config( | ||||
|         &mut self, | ||||
|         spreading_factor: SpreadingFactor, | ||||
|         bandwidth: Bandwidth, | ||||
|         coding_rate: CodingRate, | ||||
|         preamble_length: u16, | ||||
|         symb_timeout: u16, | ||||
|         fixed_len: bool, | ||||
|         payload_len: u8, | ||||
|         crc_on: bool, | ||||
|         _freq_hop_on: bool, | ||||
|         _hop_period: u8, | ||||
|         iq_inverted: bool, | ||||
|         rx_continuous: bool, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         let mut symb_timeout_final = symb_timeout; | ||||
|  | ||||
|         self.rx_continuous = rx_continuous; | ||||
|         if self.rx_continuous { | ||||
|             symb_timeout_final = 0; | ||||
|         } | ||||
|         if fixed_len { | ||||
|             self.max_payload_length = payload_len; | ||||
|         } else { | ||||
|             self.max_payload_length = 0xFFu8; | ||||
|         } | ||||
|  | ||||
|         self.sub_set_stop_rx_timer_on_preamble_detect(false).await?; | ||||
|  | ||||
|         let mut low_data_rate_optimize = 0x00u8; | ||||
|         if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) | ||||
|             && (bandwidth == Bandwidth::_125KHz)) | ||||
|             || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) | ||||
|         { | ||||
|             low_data_rate_optimize = 0x01u8; | ||||
|         } | ||||
|  | ||||
|         let modulation_params = ModulationParams { | ||||
|             spreading_factor: spreading_factor, | ||||
|             bandwidth: bandwidth, | ||||
|             coding_rate: coding_rate, | ||||
|             low_data_rate_optimize: low_data_rate_optimize, | ||||
|         }; | ||||
|  | ||||
|         let mut preamble_length_final = preamble_length; | ||||
|         if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) | ||||
|             && (preamble_length < 12) | ||||
|         { | ||||
|             preamble_length_final = 12; | ||||
|         } | ||||
|  | ||||
|         let packet_params = PacketParams { | ||||
|             preamble_length: preamble_length_final, | ||||
|             implicit_header: fixed_len, | ||||
|             payload_length: self.max_payload_length, | ||||
|             crc_on: crc_on, | ||||
|             iq_inverted: iq_inverted, | ||||
|         }; | ||||
|  | ||||
|         self.modulation_params = Some(modulation_params); | ||||
|         self.packet_params = Some(packet_params); | ||||
|  | ||||
|         self.standby().await?; | ||||
|         self.sub_set_modulation_params().await?; | ||||
|         self.sub_set_packet_params().await?; | ||||
|         self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?; | ||||
|  | ||||
|         // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) | ||||
|         let mut iq_polarity = [0x00u8]; | ||||
|         self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?; | ||||
|         if iq_inverted { | ||||
|             self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))]) | ||||
|                 .await?; | ||||
|         } else { | ||||
|             self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)]) | ||||
|                 .await?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Set the transmission parameters for the LoRa modem (only). | ||||
|     ///   power                output power [dBm] | ||||
|     ///   spreading_factor     [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] | ||||
|     ///   bandwidth            [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] | ||||
|     ///   coding_rate          [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] | ||||
|     ///   preamble_length      length in symbols (the hardware adds 4 more symbols) | ||||
|     ///   fixed_len            fixed length packets [0: variable, 1: fixed] | ||||
|     ///   crc_on               [0: OFF, 1: ON] | ||||
|     ///   freq_hop_on          intra-packet frequency hopping [0: OFF, 1: ON] | ||||
|     ///   hop_period           number of symbols between each hop | ||||
|     ///   iq_inverted          invert IQ signals [0: not inverted, 1: inverted] | ||||
|     pub async fn set_tx_config( | ||||
|         &mut self, | ||||
|         power: i8, | ||||
|         spreading_factor: SpreadingFactor, | ||||
|         bandwidth: Bandwidth, | ||||
|         coding_rate: CodingRate, | ||||
|         preamble_length: u16, | ||||
|         fixed_len: bool, | ||||
|         crc_on: bool, | ||||
|         _freq_hop_on: bool, | ||||
|         _hop_period: u8, | ||||
|         iq_inverted: bool, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         let mut low_data_rate_optimize = 0x00u8; | ||||
|         if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) | ||||
|             && (bandwidth == Bandwidth::_125KHz)) | ||||
|             || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) | ||||
|         { | ||||
|             low_data_rate_optimize = 0x01u8; | ||||
|         } | ||||
|  | ||||
|         let modulation_params = ModulationParams { | ||||
|             spreading_factor: spreading_factor, | ||||
|             bandwidth: bandwidth, | ||||
|             coding_rate: coding_rate, | ||||
|             low_data_rate_optimize: low_data_rate_optimize, | ||||
|         }; | ||||
|  | ||||
|         let mut preamble_length_final = preamble_length; | ||||
|         if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) | ||||
|             && (preamble_length < 12) | ||||
|         { | ||||
|             preamble_length_final = 12; | ||||
|         } | ||||
|  | ||||
|         let packet_params = PacketParams { | ||||
|             preamble_length: preamble_length_final, | ||||
|             implicit_header: fixed_len, | ||||
|             payload_length: self.max_payload_length, | ||||
|             crc_on: crc_on, | ||||
|             iq_inverted: iq_inverted, | ||||
|         }; | ||||
|  | ||||
|         self.modulation_params = Some(modulation_params); | ||||
|         self.packet_params = Some(packet_params); | ||||
|  | ||||
|         self.standby().await?; | ||||
|         self.sub_set_modulation_params().await?; | ||||
|         self.sub_set_packet_params().await?; | ||||
|  | ||||
|         // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) | ||||
|  | ||||
|         let mut tx_modulation = [0x00u8]; | ||||
|         self.brd_read_registers(Register::TxModulation, &mut tx_modulation) | ||||
|             .await?; | ||||
|         if bandwidth == Bandwidth::_500KHz { | ||||
|             self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))]) | ||||
|                 .await?; | ||||
|         } else { | ||||
|             self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)]) | ||||
|                 .await?; | ||||
|         } | ||||
|  | ||||
|         self.brd_set_rf_tx_power(power).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported] | ||||
|     pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result<bool, RadioError<BUS>> { | ||||
|         Ok(self.brd_check_rf_frequency(frequency).await?) | ||||
|     } | ||||
|  | ||||
|     /// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called) | ||||
|     ///   spreading_factor     [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] | ||||
|     ///   bandwidth            [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] | ||||
|     ///   coding_rate          [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] | ||||
|     ///   preamble_length      length in symbols (the hardware adds 4 more symbols) | ||||
|     ///   fixed_len            fixed length packets [0: variable, 1: fixed] | ||||
|     ///   payload_len          sets payload length when fixed length is used | ||||
|     ///   crc_on               [0: OFF, 1: ON] | ||||
|     pub fn get_time_on_air( | ||||
|         &mut self, | ||||
|         spreading_factor: SpreadingFactor, | ||||
|         bandwidth: Bandwidth, | ||||
|         coding_rate: CodingRate, | ||||
|         preamble_length: u16, | ||||
|         fixed_len: bool, | ||||
|         payload_len: u8, | ||||
|         crc_on: bool, | ||||
|     ) -> Result<u32, RadioError<BUS>> { | ||||
|         let numerator = 1000 | ||||
|             * Self::get_lora_time_on_air_numerator( | ||||
|                 spreading_factor, | ||||
|                 bandwidth, | ||||
|                 coding_rate, | ||||
|                 preamble_length, | ||||
|                 fixed_len, | ||||
|                 payload_len, | ||||
|                 crc_on, | ||||
|             ); | ||||
|         let denominator = bandwidth.value_in_hz(); | ||||
|         if denominator == 0 { | ||||
|             Err(RadioError::InvalidBandwidth) | ||||
|         } else { | ||||
|             Ok((numerator + denominator - 1) / denominator) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms] | ||||
|     pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> { | ||||
|         if self.packet_params.is_some() { | ||||
|             self.sub_set_dio_irq_params( | ||||
|                 IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), | ||||
|                 IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), | ||||
|                 IrqMask::None.value(), | ||||
|                 IrqMask::None.value(), | ||||
|             ) | ||||
|             .await?; | ||||
|  | ||||
|             let mut packet_params = self.packet_params.as_mut().unwrap(); | ||||
|             packet_params.payload_length = buffer.len() as u8; | ||||
|             self.sub_set_packet_params().await?; | ||||
|             self.sub_send_payload(buffer, timeout).await?; | ||||
|             Ok(()) | ||||
|         } else { | ||||
|             Err(RadioError::PacketParamsMissing) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Set the radio in sleep mode | ||||
|     pub async fn sleep(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_sleep(SleepParams { | ||||
|             wakeup_rtc: false, | ||||
|             reset: false, | ||||
|             warm_start: true, | ||||
|         }) | ||||
|         .await?; | ||||
|         Timer::after(Duration::from_millis(2)).await; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Set the radio in standby mode | ||||
|     pub async fn standby(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_standby(StandbyMode::RC).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)]        | ||||
|     pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_dio_irq_params( | ||||
|             IrqMask::All.value(), | ||||
|             IrqMask::All.value(), | ||||
|             IrqMask::None.value(), | ||||
|             IrqMask::None.value(), | ||||
|         ) | ||||
|         .await?; | ||||
|  | ||||
|         if self.rx_continuous { | ||||
|             self.sub_set_rx(0xFFFFFF).await?; | ||||
|         } else { | ||||
|             self.sub_set_rx(timeout << 6).await?; | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Start a Channel Activity Detection | ||||
|     pub async fn start_cad(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_dio_irq_params( | ||||
|             IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), | ||||
|             IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), | ||||
|             IrqMask::None.value(), | ||||
|             IrqMask::None.value(), | ||||
|         ) | ||||
|         .await?; | ||||
|         self.sub_set_cad().await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Sets the radio in continuous wave transmission mode | ||||
|     ///   frequency    channel RF frequency | ||||
|     ///   power        output power [dBm] | ||||
|     ///   timeout      transmission mode timeout [s] | ||||
|     pub async fn set_tx_continuous_wave( | ||||
|         &mut self, | ||||
|         frequency: u32, | ||||
|         power: i8, | ||||
|         _timeout: u16, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_rf_frequency(frequency).await?; | ||||
|         self.brd_set_rf_tx_power(power).await?; | ||||
|         self.sub_set_tx_continuous_wave().await?; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Read the current RSSI value for the LoRa modem (only) [dBm] | ||||
|     pub async fn get_rssi(&mut self) -> Result<i16, RadioError<BUS>> { | ||||
|         let value = self.sub_get_rssi_inst().await?; | ||||
|         Ok(value as i16) | ||||
|     } | ||||
|  | ||||
|     /// Write one or more radio registers with a buffer of a given size, starting at the first register address | ||||
|     pub async fn write_registers_from_buffer( | ||||
|         &mut self, | ||||
|         start_register: Register, | ||||
|         buffer: &[u8], | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_registers(start_register, buffer).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Read one or more radio registers into a buffer of a given size, starting at the first register address | ||||
|     pub async fn read_registers_into_buffer( | ||||
|         &mut self, | ||||
|         start_register: Register, | ||||
|         buffer: &mut [u8], | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_read_registers(start_register, buffer).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Set the maximum payload length (in bytes) for a LoRa modem (only). | ||||
|     pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError<BUS>> { | ||||
|         if self.packet_params.is_some() { | ||||
|             let packet_params = self.packet_params.as_mut().unwrap(); | ||||
|             self.max_payload_length = max; | ||||
|             packet_params.payload_length = max; | ||||
|             self.sub_set_packet_params().await?; | ||||
|             Ok(()) | ||||
|         } else { | ||||
|             Err(RadioError::PacketParamsMissing) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Get the time required for the board plus radio to get out of sleep [ms] | ||||
|     pub fn get_wakeup_time(&mut self) -> u32 { | ||||
|         self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME | ||||
|     } | ||||
|  | ||||
|     /// Process the radio irq | ||||
|     pub async fn process_irq( | ||||
|         &mut self, | ||||
|         receiving_buffer: Option<&mut [u8]>, | ||||
|         received_len: Option<&mut u8>, | ||||
|         cad_activity_detected: Option<&mut bool>, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         loop { | ||||
|             info!("process_irq loop entered"); // debug ??? | ||||
|  | ||||
|             let de = self.sub_get_device_errors().await?; | ||||
|             info!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", | ||||
|                                de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp); | ||||
|             let st = self.sub_get_status().await?; | ||||
|             info!( | ||||
|                 "radio status: cmd_status: {:x}, chip_mode: {:x}", | ||||
|                 st.cmd_status, st.chip_mode | ||||
|             ); | ||||
|  | ||||
|             self.dio1.wait_for_high().await.map_err(|_| DIO1)?; | ||||
|             let operating_mode = self.brd_get_operating_mode(); | ||||
|             let irq_flags = self.sub_get_irq_status().await?; | ||||
|             self.sub_clear_irq_status(irq_flags).await?; | ||||
|             info!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // debug ??? | ||||
|  | ||||
|             // check for errors and unexpected interrupt masks (based on operation mode) | ||||
|             if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() { | ||||
|                 if !self.rx_continuous { | ||||
|                     self.brd_set_operating_mode(RadioMode::StandbyRC); | ||||
|                 } | ||||
|                 return Err(RadioError::HeaderError); | ||||
|             } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() { | ||||
|                 if operating_mode == RadioMode::Receive { | ||||
|                     if !self.rx_continuous { | ||||
|                         self.brd_set_operating_mode(RadioMode::StandbyRC); | ||||
|                     } | ||||
|                     return Err(RadioError::CRCErrorOnReceive); | ||||
|                 } else { | ||||
|                     return Err(RadioError::CRCErrorUnexpected); | ||||
|                 } | ||||
|             } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() { | ||||
|                 if operating_mode == RadioMode::Transmit { | ||||
|                     self.brd_set_operating_mode(RadioMode::StandbyRC); | ||||
|                     return Err(RadioError::TransmitTimeout); | ||||
|                 } else if operating_mode == RadioMode::Receive { | ||||
|                     self.brd_set_operating_mode(RadioMode::StandbyRC); | ||||
|                     return Err(RadioError::ReceiveTimeout); | ||||
|                 } else { | ||||
|                     return Err(RadioError::TimeoutUnexpected); | ||||
|                 } | ||||
|             } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value()) | ||||
|                 && (operating_mode != RadioMode::Transmit) | ||||
|             { | ||||
|                 return Err(RadioError::TransmitDoneUnexpected); | ||||
|             } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value()) | ||||
|                 && (operating_mode != RadioMode::Receive) | ||||
|             { | ||||
|                 return Err(RadioError::ReceiveDoneUnexpected); | ||||
|             } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value()) | ||||
|                 || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value())) | ||||
|                 && (operating_mode != RadioMode::ChannelActivityDetection) | ||||
|             { | ||||
|                 return Err(RadioError::CADUnexpected); | ||||
|             } | ||||
|  | ||||
|             // debug ??? | ||||
|             if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() { | ||||
|                 info!("HeaderValid"); | ||||
|             } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() { | ||||
|                 info!("PreambleDetected"); | ||||
|             } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() { | ||||
|                 info!("SyncwordValid"); | ||||
|             } | ||||
|  | ||||
|             // handle completions | ||||
|             if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() { | ||||
|                 self.brd_set_operating_mode(RadioMode::StandbyRC); | ||||
|                 return Ok(()); | ||||
|             } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() { | ||||
|                 if !self.rx_continuous { | ||||
|                     self.brd_set_operating_mode(RadioMode::StandbyRC); | ||||
|  | ||||
|                     // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3) | ||||
|                     self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?; | ||||
|                     let mut evt_clr = [0x00u8]; | ||||
|                     self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?; | ||||
|                     evt_clr[0] |= 1 << 1; | ||||
|                     self.brd_write_registers(Register::EvtClr, &evt_clr).await?; | ||||
|                 } | ||||
|  | ||||
|                 if receiving_buffer.is_some() && received_len.is_some() { | ||||
|                     *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?; | ||||
|                 } | ||||
|                 self.packet_status = self.sub_get_packet_status().await?.into(); | ||||
|                 return Ok(()); | ||||
|             } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() { | ||||
|                 if cad_activity_detected.is_some() { | ||||
|                     *(cad_activity_detected.unwrap()) = | ||||
|                         (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value(); | ||||
|                 } | ||||
|                 self.brd_set_operating_mode(RadioMode::StandbyRC); | ||||
|                 return Ok(()); | ||||
|             } | ||||
|  | ||||
|             // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid | ||||
|             // are in that category), loop to wait again | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // SX126x-specific functions | ||||
|  | ||||
|     /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms] | ||||
|     pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_dio_irq_params( | ||||
|             IrqMask::All.value(), | ||||
|             IrqMask::All.value(), | ||||
|             IrqMask::None.value(), | ||||
|             IrqMask::None.value(), | ||||
|         ) | ||||
|         .await?; | ||||
|  | ||||
|         if self.rx_continuous { | ||||
|             self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous | ||||
|         } else { | ||||
|             self.sub_set_rx_boosted(timeout << 6).await?; | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Set the Rx duty cycle management parameters (SX126x radios only) | ||||
|     ///   rx_time       structure describing reception timeout value | ||||
|     ///   sleep_time    structure describing sleep timeout value | ||||
|     pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub fn get_latest_packet_status(&mut self) -> Option<PacketStatus> { | ||||
|         self.packet_status | ||||
|     } | ||||
|  | ||||
|     // Utilities | ||||
|  | ||||
|     async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError<BUS>> { | ||||
|         let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize]; | ||||
|  | ||||
|         // Read the address and registers already added to the list | ||||
|         self.brd_read_registers(Register::RetentionList, &mut buffer).await?; | ||||
|  | ||||
|         let number_of_registers = buffer[0]; | ||||
|         for i in 0..number_of_registers { | ||||
|             if register_address | ||||
|                 == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16) | ||||
|             { | ||||
|                 return Ok(()); // register already in list | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION { | ||||
|             buffer[0] += 1; // increment number of registers | ||||
|  | ||||
|             buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8; | ||||
|             buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8; | ||||
|             self.brd_write_registers(Register::RetentionList, &buffer).await?; | ||||
|  | ||||
|             Ok(()) | ||||
|         } else { | ||||
|             Err(RadioError::RetentionListExceeded) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn get_lora_time_on_air_numerator( | ||||
|         spreading_factor: SpreadingFactor, | ||||
|         bandwidth: Bandwidth, | ||||
|         coding_rate: CodingRate, | ||||
|         preamble_length: u16, | ||||
|         fixed_len: bool, | ||||
|         payload_len: u8, | ||||
|         crc_on: bool, | ||||
|     ) -> u32 { | ||||
|         let cell_denominator; | ||||
|         let cr_denominator = (coding_rate.value() as i32) + 4; | ||||
|  | ||||
|         // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6 | ||||
|         let mut preamble_length_final = preamble_length; | ||||
|         if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) | ||||
|             && (preamble_length < 12) | ||||
|         { | ||||
|             preamble_length_final = 12; | ||||
|         } | ||||
|  | ||||
|         let mut low_data_rate_optimize = false; | ||||
|         if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) | ||||
|             && (bandwidth == Bandwidth::_125KHz)) | ||||
|             || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) | ||||
|         { | ||||
|             low_data_rate_optimize = true; | ||||
|         } | ||||
|  | ||||
|         let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 }) | ||||
|             - (4 * spreading_factor.value() as i32) | ||||
|             + (if fixed_len { 0 } else { 20 }); | ||||
|  | ||||
|         if spreading_factor.value() <= 6 { | ||||
|             cell_denominator = 4 * (spreading_factor.value() as i32); | ||||
|         } else { | ||||
|             cell_numerator += 8; | ||||
|             if low_data_rate_optimize { | ||||
|                 cell_denominator = 4 * ((spreading_factor.value() as i32) - 2); | ||||
|             } else { | ||||
|                 cell_denominator = 4 * (spreading_factor.value() as i32); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if cell_numerator < 0 { | ||||
|             cell_numerator = 0; | ||||
|         } | ||||
|  | ||||
|         let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator) | ||||
|             + (preamble_length_final as i32) | ||||
|             + 12; | ||||
|  | ||||
|         if spreading_factor.value() <= 6 { | ||||
|             intermediate = intermediate + 2; | ||||
|         } | ||||
|  | ||||
|         (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32 | ||||
|     } | ||||
| } | ||||
							
								
								
									
										469
									
								
								embassy-lora/src/sx126x/sx126x_lora/mod_params.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										469
									
								
								embassy-lora/src/sx126x/sx126x_lora/mod_params.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,469 @@ | ||||
| use core::fmt::Debug; | ||||
|  | ||||
| use lorawan_device::async_device::radio as device; | ||||
|  | ||||
| #[allow(clippy::upper_case_acronyms)] | ||||
| #[derive(Debug)] | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| pub enum RadioError<BUS> { | ||||
|     SPI(BUS), | ||||
|     CS, | ||||
|     Reset, | ||||
|     AntRx, | ||||
|     AntTx, | ||||
|     Busy, | ||||
|     DIO1, | ||||
|     PayloadSizeMismatch(usize, usize), | ||||
|     RetentionListExceeded, | ||||
|     InvalidBandwidth, | ||||
|     ModulationParamsMissing, | ||||
|     PacketParamsMissing, | ||||
|     HeaderError, | ||||
|     CRCErrorUnexpected, | ||||
|     CRCErrorOnReceive, | ||||
|     TransmitTimeout, | ||||
|     ReceiveTimeout, | ||||
|     TimeoutUnexpected, | ||||
|     TransmitDoneUnexpected, | ||||
|     ReceiveDoneUnexpected, | ||||
|     CADUnexpected, | ||||
| } | ||||
|  | ||||
| pub struct RadioSystemError { | ||||
|     pub rc_64khz_calibration: bool, | ||||
|     pub rc_13mhz_calibration: bool, | ||||
|     pub pll_calibration: bool, | ||||
|     pub adc_calibration: bool, | ||||
|     pub image_calibration: bool, | ||||
|     pub xosc_start: bool, | ||||
|     pub pll_lock: bool, | ||||
|     pub pa_ramp: bool, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy, PartialEq)] | ||||
| pub enum PacketType { | ||||
|     GFSK = 0x00, | ||||
|     LoRa = 0x01, | ||||
|     None = 0x0F, | ||||
| } | ||||
|  | ||||
| impl PacketType { | ||||
|     pub const fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
|     pub fn to_enum(value: u8) -> Self { | ||||
|         if value == 0x00 { | ||||
|             PacketType::GFSK | ||||
|         } else if value == 0x01 { | ||||
|             PacketType::LoRa | ||||
|         } else { | ||||
|             PacketType::None | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub struct PacketStatus { | ||||
|     pub rssi: i8, | ||||
|     pub snr: i8, | ||||
|     pub signal_rssi: i8, | ||||
|     pub freq_error: u32, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy, PartialEq)] | ||||
| pub enum RadioType { | ||||
|     SX1261, | ||||
|     SX1262, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy, PartialEq)] | ||||
| pub enum RadioMode { | ||||
|     Sleep = 0x00,                    // sleep mode | ||||
|     StandbyRC = 0x01,                // standby mode with RC oscillator | ||||
|     StandbyXOSC = 0x02,              // standby mode with XOSC oscillator | ||||
|     FrequencySynthesis = 0x03,       // frequency synthesis mode | ||||
|     Transmit = 0x04,                 // transmit mode | ||||
|     Receive = 0x05,                  // receive mode | ||||
|     ReceiveDutyCycle = 0x06,         // receive duty cycle mode | ||||
|     ChannelActivityDetection = 0x07, // channel activity detection mode | ||||
| } | ||||
|  | ||||
| impl RadioMode { | ||||
|     /// Returns the value of the mode. | ||||
|     pub const fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
|     pub fn to_enum(value: u8) -> Self { | ||||
|         if value == 0x00 { | ||||
|             RadioMode::Sleep | ||||
|         } else if value == 0x01 { | ||||
|             RadioMode::StandbyRC | ||||
|         } else if value == 0x02 { | ||||
|             RadioMode::StandbyXOSC | ||||
|         } else if value == 0x03 { | ||||
|             RadioMode::FrequencySynthesis | ||||
|         } else if value == 0x04 { | ||||
|             RadioMode::Transmit | ||||
|         } else if value == 0x05 { | ||||
|             RadioMode::Receive | ||||
|         } else if value == 0x06 { | ||||
|             RadioMode::ReceiveDutyCycle | ||||
|         } else if value == 0x07 { | ||||
|             RadioMode::ChannelActivityDetection | ||||
|         } else { | ||||
|             RadioMode::Sleep | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub enum RadioState { | ||||
|     Idle = 0x00, | ||||
|     RxRunning = 0x01, | ||||
|     TxRunning = 0x02, | ||||
|     ChannelActivityDetecting = 0x03, | ||||
| } | ||||
|  | ||||
| impl RadioState { | ||||
|     /// Returns the value of the state. | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct RadioStatus { | ||||
|     pub cmd_status: u8, | ||||
|     pub chip_mode: u8, | ||||
| } | ||||
|  | ||||
| impl RadioStatus { | ||||
|     pub fn value(self) -> u8 { | ||||
|         (self.chip_mode << 4) | (self.cmd_status << 1) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum IrqMask { | ||||
|     None = 0x0000, | ||||
|     TxDone = 0x0001, | ||||
|     RxDone = 0x0002, | ||||
|     PreambleDetected = 0x0004, | ||||
|     SyncwordValid = 0x0008, | ||||
|     HeaderValid = 0x0010, | ||||
|     HeaderError = 0x0020, | ||||
|     CRCError = 0x0040, | ||||
|     CADDone = 0x0080, | ||||
|     CADActivityDetected = 0x0100, | ||||
|     RxTxTimeout = 0x0200, | ||||
|     All = 0xFFFF, | ||||
| } | ||||
|  | ||||
| impl IrqMask { | ||||
|     pub fn value(self) -> u16 { | ||||
|         self as u16 | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum Register { | ||||
|     PacketParams = 0x0704,          // packet configuration | ||||
|     PayloadLength = 0x0702,         // payload size | ||||
|     SynchTimeout = 0x0706,          // recalculated number of symbols | ||||
|     Syncword = 0x06C0,              // Syncword values | ||||
|     LoRaSyncword = 0x0740,          // LoRa Syncword value | ||||
|     GeneratedRandomNumber = 0x0819, //32-bit generated random number | ||||
|     AnaLNA = 0x08E2,                // disable the LNA | ||||
|     AnaMixer = 0x08E5,              // disable the mixer | ||||
|     RxGain = 0x08AC,                // RX gain (0x94: power saving, 0x96: rx boosted) | ||||
|     XTATrim = 0x0911,               // device internal trimming capacitor | ||||
|     OCP = 0x08E7,                   // over current protection max value | ||||
|     RetentionList = 0x029F,         // retention list | ||||
|     IQPolarity = 0x0736,            // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) | ||||
|     TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) | ||||
|     TxClampCfg = 0x08D8,   // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) | ||||
|     RTCCtrl = 0x0902,      // RTC control | ||||
|     EvtClr = 0x0944,       // event clear | ||||
| } | ||||
|  | ||||
| impl Register { | ||||
|     pub fn addr(self) -> u16 { | ||||
|         self as u16 | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy, PartialEq)] | ||||
| pub enum OpCode { | ||||
|     GetStatus = 0xC0, | ||||
|     WriteRegister = 0x0D, | ||||
|     ReadRegister = 0x1D, | ||||
|     WriteBuffer = 0x0E, | ||||
|     ReadBuffer = 0x1E, | ||||
|     SetSleep = 0x84, | ||||
|     SetStandby = 0x80, | ||||
|     SetFS = 0xC1, | ||||
|     SetTx = 0x83, | ||||
|     SetRx = 0x82, | ||||
|     SetRxDutyCycle = 0x94, | ||||
|     SetCAD = 0xC5, | ||||
|     SetTxContinuousWave = 0xD1, | ||||
|     SetTxContinuousPremable = 0xD2, | ||||
|     SetPacketType = 0x8A, | ||||
|     GetPacketType = 0x11, | ||||
|     SetRFFrequency = 0x86, | ||||
|     SetTxParams = 0x8E, | ||||
|     SetPAConfig = 0x95, | ||||
|     SetCADParams = 0x88, | ||||
|     SetBufferBaseAddress = 0x8F, | ||||
|     SetModulationParams = 0x8B, | ||||
|     SetPacketParams = 0x8C, | ||||
|     GetRxBufferStatus = 0x13, | ||||
|     GetPacketStatus = 0x14, | ||||
|     GetRSSIInst = 0x15, | ||||
|     GetStats = 0x10, | ||||
|     ResetStats = 0x00, | ||||
|     CfgDIOIrq = 0x08, | ||||
|     GetIrqStatus = 0x12, | ||||
|     ClrIrqStatus = 0x02, | ||||
|     Calibrate = 0x89, | ||||
|     CalibrateImage = 0x98, | ||||
|     SetRegulatorMode = 0x96, | ||||
|     GetErrors = 0x17, | ||||
|     ClrErrors = 0x07, | ||||
|     SetTCXOMode = 0x97, | ||||
|     SetTxFallbackMode = 0x93, | ||||
|     SetRFSwitchMode = 0x9D, | ||||
|     SetStopRxTimerOnPreamble = 0x9F, | ||||
|     SetLoRaSymbTimeout = 0xA0, | ||||
| } | ||||
|  | ||||
| impl OpCode { | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct SleepParams { | ||||
|     pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC | ||||
|     pub reset: bool, | ||||
|     pub warm_start: bool, | ||||
| } | ||||
|  | ||||
| impl SleepParams { | ||||
|     pub fn value(self) -> u8 { | ||||
|         ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy, PartialEq)] | ||||
| pub enum StandbyMode { | ||||
|     RC = 0x00, | ||||
|     XOSC = 0x01, | ||||
| } | ||||
|  | ||||
| impl StandbyMode { | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum RegulatorMode { | ||||
|     UseLDO = 0x00, | ||||
|     UseDCDC = 0x01, | ||||
| } | ||||
|  | ||||
| impl RegulatorMode { | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub struct CalibrationParams { | ||||
|     pub rc64k_enable: bool,     // calibrate RC64K clock | ||||
|     pub rc13m_enable: bool,     // calibrate RC13M clock | ||||
|     pub pll_enable: bool,       // calibrate PLL | ||||
|     pub adc_pulse_enable: bool, // calibrate ADC Pulse | ||||
|     pub adc_bulkn_enable: bool, // calibrate ADC bulkN | ||||
|     pub adc_bulkp_enable: bool, // calibrate ADC bulkP | ||||
|     pub img_enable: bool, | ||||
| } | ||||
|  | ||||
| impl CalibrationParams { | ||||
|     pub fn value(self) -> u8 { | ||||
|         ((self.img_enable as u8) << 6) | ||||
|             | ((self.adc_bulkp_enable as u8) << 5) | ||||
|             | ((self.adc_bulkn_enable as u8) << 4) | ||||
|             | ((self.adc_pulse_enable as u8) << 3) | ||||
|             | ((self.pll_enable as u8) << 2) | ||||
|             | ((self.rc13m_enable as u8) << 1) | ||||
|             | ((self.rc64k_enable as u8) << 0) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum TcxoCtrlVoltage { | ||||
|     Ctrl1V6 = 0x00, | ||||
|     Ctrl1V7 = 0x01, | ||||
|     Ctrl1V8 = 0x02, | ||||
|     Ctrl2V2 = 0x03, | ||||
|     Ctrl2V4 = 0x04, | ||||
|     Ctrl2V7 = 0x05, | ||||
|     Ctrl3V0 = 0x06, | ||||
|     Ctrl3V3 = 0x07, | ||||
| } | ||||
|  | ||||
| impl TcxoCtrlVoltage { | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum RampTime { | ||||
|     Ramp10Us = 0x00, | ||||
|     Ramp20Us = 0x01, | ||||
|     Ramp40Us = 0x02, | ||||
|     Ramp80Us = 0x03, | ||||
|     Ramp200Us = 0x04, | ||||
|     Ramp800Us = 0x05, | ||||
|     Ramp1700Us = 0x06, | ||||
|     Ramp3400Us = 0x07, | ||||
| } | ||||
|  | ||||
| impl RampTime { | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy, PartialEq)] | ||||
| pub enum SpreadingFactor { | ||||
|     _5 = 0x05, | ||||
|     _6 = 0x06, | ||||
|     _7 = 0x07, | ||||
|     _8 = 0x08, | ||||
|     _9 = 0x09, | ||||
|     _10 = 0x0A, | ||||
|     _11 = 0x0B, | ||||
|     _12 = 0x0C, | ||||
| } | ||||
|  | ||||
| impl SpreadingFactor { | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<device::SpreadingFactor> for SpreadingFactor { | ||||
|     fn from(sf: device::SpreadingFactor) -> Self { | ||||
|         match sf { | ||||
|             device::SpreadingFactor::_7 => SpreadingFactor::_7, | ||||
|             device::SpreadingFactor::_8 => SpreadingFactor::_8, | ||||
|             device::SpreadingFactor::_9 => SpreadingFactor::_9, | ||||
|             device::SpreadingFactor::_10 => SpreadingFactor::_10, | ||||
|             device::SpreadingFactor::_11 => SpreadingFactor::_11, | ||||
|             device::SpreadingFactor::_12 => SpreadingFactor::_12, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy, PartialEq)] | ||||
| pub enum Bandwidth { | ||||
|     _500KHz = 0x06, | ||||
|     _250KHz = 0x05, | ||||
|     _125KHz = 0x04, | ||||
| } | ||||
|  | ||||
| impl Bandwidth { | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
|  | ||||
|     pub fn value_in_hz(self) -> u32 { | ||||
|         match self { | ||||
|             Bandwidth::_125KHz => 125000u32, | ||||
|             Bandwidth::_250KHz => 250000u32, | ||||
|             Bandwidth::_500KHz => 500000u32, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<device::Bandwidth> for Bandwidth { | ||||
|     fn from(bw: device::Bandwidth) -> Self { | ||||
|         match bw { | ||||
|             device::Bandwidth::_500KHz => Bandwidth::_500KHz, | ||||
|             device::Bandwidth::_250KHz => Bandwidth::_250KHz, | ||||
|             device::Bandwidth::_125KHz => Bandwidth::_125KHz, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum CodingRate { | ||||
|     _4_5 = 0x01, | ||||
|     _4_6 = 0x02, | ||||
|     _4_7 = 0x03, | ||||
|     _4_8 = 0x04, | ||||
| } | ||||
|  | ||||
| impl CodingRate { | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<device::CodingRate> for CodingRate { | ||||
|     fn from(cr: device::CodingRate) -> Self { | ||||
|         match cr { | ||||
|             device::CodingRate::_4_5 => CodingRate::_4_5, | ||||
|             device::CodingRate::_4_6 => CodingRate::_4_6, | ||||
|             device::CodingRate::_4_7 => CodingRate::_4_7, | ||||
|             device::CodingRate::_4_8 => CodingRate::_4_8, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub struct ModulationParams { | ||||
|     pub spreading_factor: SpreadingFactor, | ||||
|     pub bandwidth: Bandwidth, | ||||
|     pub coding_rate: CodingRate, | ||||
|     pub low_data_rate_optimize: u8, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub struct PacketParams { | ||||
|     pub preamble_length: u16,  // number of LoRa symbols in the preamble | ||||
|     pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length) | ||||
|     pub payload_length: u8, | ||||
|     pub crc_on: bool, | ||||
|     pub iq_inverted: bool, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum CADSymbols { | ||||
|     _1 = 0x00, | ||||
|     _2 = 0x01, | ||||
|     _4 = 0x02, | ||||
|     _8 = 0x03, | ||||
|     _16 = 0x04, | ||||
| } | ||||
|  | ||||
| impl CADSymbols { | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy)] | ||||
| pub enum CADExitMode { | ||||
|     CADOnly = 0x00, | ||||
|     CADRx = 0x01, | ||||
|     CADLBT = 0x10, | ||||
| } | ||||
|  | ||||
| impl CADExitMode { | ||||
|     pub fn value(self) -> u8 { | ||||
|         self as u8 | ||||
|     } | ||||
| } | ||||
							
								
								
									
										688
									
								
								embassy-lora/src/sx126x/sx126x_lora/subroutine.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										688
									
								
								embassy-lora/src/sx126x/sx126x_lora/subroutine.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,688 @@ | ||||
| use embedded_hal::digital::v2::OutputPin; | ||||
| use embedded_hal_async::digital::Wait; | ||||
| use embedded_hal_async::spi::SpiBus; | ||||
|  | ||||
| use super::mod_params::*; | ||||
| use super::LoRa; | ||||
|  | ||||
| // Internal frequency of the radio | ||||
| const SX126X_XTAL_FREQ: u32 = 32000000; | ||||
|  | ||||
| // Scaling factor used to perform fixed-point operations | ||||
| const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14; | ||||
|  | ||||
| // PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT | ||||
| const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT); | ||||
|  | ||||
| // Maximum value for parameter symbNum | ||||
| const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248; | ||||
|  | ||||
| // Provides board-specific functionality for Semtech SX126x-based boards | ||||
|  | ||||
| impl<SPI, CS, RESET, ANTRX, ANTTX, WAIT, BUS> LoRa<SPI, CS, RESET, ANTRX, ANTTX, WAIT> | ||||
| where | ||||
|     SPI: SpiBus<u8, Error = BUS>, | ||||
|     CS: OutputPin, | ||||
|     RESET: OutputPin, | ||||
|     ANTRX: OutputPin, | ||||
|     ANTTX: OutputPin, | ||||
|     WAIT: Wait, | ||||
| { | ||||
|     // Initialize the radio driver | ||||
|     pub(super) async fn sub_init(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_reset().await?; | ||||
|         self.brd_wakeup().await?; | ||||
|         self.sub_set_standby(StandbyMode::RC).await?; | ||||
|         self.brd_io_tcxo_init().await?; | ||||
|         self.brd_io_rf_switch_init().await?; | ||||
|         self.image_calibrated = false; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Wakeup the radio if it is in Sleep mode and check that Busy is low | ||||
|     pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         let operating_mode = self.brd_get_operating_mode(); | ||||
|         if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle { | ||||
|             self.brd_wakeup().await?; | ||||
|             self.brd_ant_sw_on().await?; | ||||
|         } | ||||
|         self.brd_wait_on_busy().await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Save the payload to be sent in the radio buffer | ||||
|     pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_buffer(0x00, payload).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Read the payload received. | ||||
|     pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> { | ||||
|         let (size, offset) = self.sub_get_rx_buffer_status().await?; | ||||
|         if (size as usize) > buffer.len() { | ||||
|             Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len())) | ||||
|         } else { | ||||
|             self.brd_read_buffer(offset, buffer).await?; | ||||
|             Ok(size) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Send a payload | ||||
|     pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> { | ||||
|         self.sub_set_payload(payload).await?; | ||||
|         self.sub_set_tx(timeout).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Get a 32-bit random value generated by the radio.  A valid packet type must have been configured before using this command. | ||||
|     // | ||||
|     // The radio must be in reception mode before executing this function.  This code can potentially result in interrupt generation. It is the responsibility of | ||||
|     // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts | ||||
|     // generated during this process will not cause undesired side-effects in the software. | ||||
|     // | ||||
|     // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing. | ||||
|     pub(super) async fn sub_get_random(&mut self) -> Result<u32, RadioError<BUS>> { | ||||
|         let mut reg_ana_lna_buffer_original = [0x00u8]; | ||||
|         let mut reg_ana_mixer_buffer_original = [0x00u8]; | ||||
|         let mut reg_ana_lna_buffer = [0x00u8]; | ||||
|         let mut reg_ana_mixer_buffer = [0x00u8]; | ||||
|         let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8]; | ||||
|  | ||||
|         self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original) | ||||
|             .await?; | ||||
|         reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0)); | ||||
|         self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer).await?; | ||||
|  | ||||
|         self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original) | ||||
|             .await?; | ||||
|         reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7)); | ||||
|         self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer) | ||||
|             .await?; | ||||
|  | ||||
|         // Set radio in continuous reception | ||||
|         self.sub_set_rx(0xFFFFFFu32).await?; | ||||
|  | ||||
|         self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer) | ||||
|             .await?; | ||||
|  | ||||
|         self.sub_set_standby(StandbyMode::RC).await?; | ||||
|  | ||||
|         self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer_original) | ||||
|             .await?; | ||||
|         self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer_original) | ||||
|             .await?; | ||||
|  | ||||
|         Ok(Self::convert_u8_buffer_to_u32(&number_buffer)) | ||||
|     } | ||||
|  | ||||
|     // Set the radio in sleep mode | ||||
|     pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_ant_sw_off().await?; | ||||
|  | ||||
|         let _fix_rx = self.antenna_rx.set_low(); | ||||
|         let _fix_tx = self.antenna_tx.set_low(); | ||||
|  | ||||
|         if !sleep_config.warm_start { | ||||
|             self.image_calibrated = false; | ||||
|         } | ||||
|  | ||||
|         self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()]) | ||||
|             .await?; | ||||
|         self.brd_set_operating_mode(RadioMode::Sleep); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the radio in configuration mode | ||||
|     pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?; | ||||
|         if mode == StandbyMode::RC { | ||||
|             self.brd_set_operating_mode(RadioMode::StandbyRC); | ||||
|         } else { | ||||
|             self.brd_set_operating_mode(RadioMode::StandbyXOSC); | ||||
|         } | ||||
|  | ||||
|         let _fix_rx = self.antenna_rx.set_low(); | ||||
|         let _fix_tx = self.antenna_tx.set_low(); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the radio in FS mode | ||||
|     pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         // antenna settings ??? | ||||
|         self.brd_write_command(OpCode::SetFS, &[]).await?; | ||||
|         self.brd_set_operating_mode(RadioMode::FrequencySynthesis); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the radio in transmission mode with timeout specified | ||||
|     pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> { | ||||
|         let buffer = [ | ||||
|             Self::timeout_1(timeout), | ||||
|             Self::timeout_2(timeout), | ||||
|             Self::timeout_3(timeout), | ||||
|         ]; | ||||
|  | ||||
|         let _fix_rx = self.antenna_rx.set_low(); | ||||
|         let _fix_tx = self.antenna_tx.set_high(); | ||||
|  | ||||
|         self.brd_set_operating_mode(RadioMode::Transmit); | ||||
|         self.brd_write_command(OpCode::SetTx, &buffer).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the radio in reception mode with timeout specified | ||||
|     pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> { | ||||
|         let buffer = [ | ||||
|             Self::timeout_1(timeout), | ||||
|             Self::timeout_2(timeout), | ||||
|             Self::timeout_3(timeout), | ||||
|         ]; | ||||
|  | ||||
|         let _fix_rx = self.antenna_rx.set_high(); | ||||
|         let _fix_tx = self.antenna_tx.set_low(); | ||||
|  | ||||
|         self.brd_set_operating_mode(RadioMode::Receive); | ||||
|         self.brd_write_registers(Register::RxGain, &[0x94u8]).await?; | ||||
|         self.brd_write_command(OpCode::SetRx, &buffer).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the radio in reception mode with Boosted LNA gain and timeout specified | ||||
|     pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> { | ||||
|         let buffer = [ | ||||
|             Self::timeout_1(timeout), | ||||
|             Self::timeout_2(timeout), | ||||
|             Self::timeout_3(timeout), | ||||
|         ]; | ||||
|  | ||||
|         let _fix_rx = self.antenna_rx.set_high(); | ||||
|         let _fix_tx = self.antenna_tx.set_low(); | ||||
|  | ||||
|         self.brd_set_operating_mode(RadioMode::Receive); | ||||
|         // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity | ||||
|         self.brd_write_registers(Register::RxGain, &[0x96u8]).await?; | ||||
|         self.brd_write_command(OpCode::SetRx, &buffer).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the Rx duty cycle management parameters | ||||
|     pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> { | ||||
|         let buffer = [ | ||||
|             ((rx_time >> 16) & 0xFF) as u8, | ||||
|             ((rx_time >> 8) & 0xFF) as u8, | ||||
|             (rx_time & 0xFF) as u8, | ||||
|             ((sleep_time >> 16) & 0xFF) as u8, | ||||
|             ((sleep_time >> 8) & 0xFF) as u8, | ||||
|             (sleep_time & 0xFF) as u8, | ||||
|         ]; | ||||
|  | ||||
|         // antenna settings ??? | ||||
|  | ||||
|         self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?; | ||||
|         self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the radio in CAD mode | ||||
|     pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         let _fix_rx = self.antenna_rx.set_high(); | ||||
|         let _fix_tx = self.antenna_tx.set_low(); | ||||
|  | ||||
|         self.brd_write_command(OpCode::SetCAD, &[]).await?; | ||||
|         self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the radio in continuous wave transmission mode | ||||
|     pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         let _fix_rx = self.antenna_rx.set_low(); | ||||
|         let _fix_tx = self.antenna_tx.set_high(); | ||||
|  | ||||
|         self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?; | ||||
|         self.brd_set_operating_mode(RadioMode::Transmit); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the radio in continuous preamble transmission mode | ||||
|     pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         let _fix_rx = self.antenna_rx.set_low(); | ||||
|         let _fix_tx = self.antenna_tx.set_high(); | ||||
|  | ||||
|         self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?; | ||||
|         self.brd_set_operating_mode(RadioMode::Transmit); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Decide which interrupt will stop the internal radio rx timer. | ||||
|     //   false     timer stop after header/syncword detection | ||||
|     //   true      timer stop after preamble detection | ||||
|     pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect( | ||||
|         &mut self, | ||||
|         enable: bool, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8]) | ||||
|             .await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the number of symbols the radio will wait to validate a reception | ||||
|     pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError<BUS>> { | ||||
|         let mut exp = 0u8; | ||||
|         let mut reg; | ||||
|         let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1; | ||||
|         while mant > 31 { | ||||
|             mant = (mant + 3) >> 2; | ||||
|             exp += 1; | ||||
|         } | ||||
|         reg = mant << ((2 * exp) + 1); | ||||
|  | ||||
|         self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?; | ||||
|  | ||||
|         if symb_num != 0 { | ||||
|             reg = exp + (mant << 3); | ||||
|             self.brd_write_registers(Register::SynchTimeout, &[reg]).await?; | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the power regulators operating mode (LDO or DC_DC).  Using only LDO implies that the Rx or Tx current is doubled | ||||
|     pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()]) | ||||
|             .await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Calibrate the given radio block | ||||
|     pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()]) | ||||
|             .await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Calibrate the image rejection based on the given frequency | ||||
|     pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError<BUS>> { | ||||
|         let mut cal_freq = [0x00u8, 0x00u8]; | ||||
|  | ||||
|         if freq > 900000000 { | ||||
|             cal_freq[0] = 0xE1; | ||||
|             cal_freq[1] = 0xE9; | ||||
|         } else if freq > 850000000 { | ||||
|             cal_freq[0] = 0xD7; | ||||
|             cal_freq[1] = 0xDB; | ||||
|         } else if freq > 770000000 { | ||||
|             cal_freq[0] = 0xC1; | ||||
|             cal_freq[1] = 0xC5; | ||||
|         } else if freq > 460000000 { | ||||
|             cal_freq[0] = 0x75; | ||||
|             cal_freq[1] = 0x81; | ||||
|         } else if freq > 425000000 { | ||||
|             cal_freq[0] = 0x6B; | ||||
|             cal_freq[1] = 0x6F; | ||||
|         } | ||||
|         self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Activate the extention of the timeout when a long preamble is used | ||||
|     pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError<BUS>> { | ||||
|         Ok(()) // no operation currently | ||||
|     } | ||||
|  | ||||
|     // Set the transmission parameters | ||||
|     //   hp_max          0 for sx1261, 7 for sx1262 | ||||
|     //   device_sel      1 for sx1261, 0 for sx1262 | ||||
|     //   pa_lut          0 for 14dBm LUT, 1 for 22dBm LUT | ||||
|     pub(super) async fn sub_set_pa_config( | ||||
|         &mut self, | ||||
|         pa_duty_cycle: u8, | ||||
|         hp_max: u8, | ||||
|         device_sel: u8, | ||||
|         pa_lut: u8, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut]) | ||||
|             .await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Define into which mode the chip goes after a TX / RX done | ||||
|     pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode]) | ||||
|             .await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the IRQ mask and DIO masks | ||||
|     pub(super) async fn sub_set_dio_irq_params( | ||||
|         &mut self, | ||||
|         irq_mask: u16, | ||||
|         dio1_mask: u16, | ||||
|         dio2_mask: u16, | ||||
|         dio3_mask: u16, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         let mut buffer = [0x00u8; 8]; | ||||
|  | ||||
|         buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8; | ||||
|         buffer[1] = (irq_mask & 0x00FF) as u8; | ||||
|         buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8; | ||||
|         buffer[3] = (dio1_mask & 0x00FF) as u8; | ||||
|         buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8; | ||||
|         buffer[5] = (dio2_mask & 0x00FF) as u8; | ||||
|         buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8; | ||||
|         buffer[7] = (dio3_mask & 0x00FF) as u8; | ||||
|         self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Return the current IRQ status | ||||
|     pub(super) async fn sub_get_irq_status(&mut self) -> Result<u16, RadioError<BUS>> { | ||||
|         let mut irq_status = [0x00u8, 0x00u8]; | ||||
|         self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?; | ||||
|         Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16)) | ||||
|     } | ||||
|  | ||||
|     // Indicate if DIO2 is used to control an RF Switch | ||||
|     pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Indicate if the radio main clock is supplied from a TCXO | ||||
|     //   tcxo_voltage        voltage used to control the TCXO on/off from DIO3 | ||||
|     //   timeout             duration given to the TCXO to go to 32MHz | ||||
|     pub(super) async fn sub_set_dio3_as_tcxo_ctrl( | ||||
|         &mut self, | ||||
|         tcxo_voltage: TcxoCtrlVoltage, | ||||
|         timeout: u32, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         let buffer = [ | ||||
|             tcxo_voltage.value() & 0x07, | ||||
|             Self::timeout_1(timeout), | ||||
|             Self::timeout_2(timeout), | ||||
|             Self::timeout_3(timeout), | ||||
|         ]; | ||||
|         self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the RF frequency (Hz) | ||||
|     pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> { | ||||
|         let mut buffer = [0x00u8; 4]; | ||||
|  | ||||
|         if !self.image_calibrated { | ||||
|             self.sub_calibrate_image(frequency).await?; | ||||
|             self.image_calibrated = true; | ||||
|         } | ||||
|  | ||||
|         let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency); | ||||
|  | ||||
|         buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8; | ||||
|         buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8; | ||||
|         buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8; | ||||
|         buffer[3] = (freq_in_pll_steps & 0xFF) as u8; | ||||
|         self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the radio for the given protocol (LoRa or GFSK).  This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters. | ||||
|     pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError<BUS>> { | ||||
|         self.packet_type = packet_type; | ||||
|         self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()]) | ||||
|             .await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Get the current radio protocol (LoRa or GFSK) | ||||
|     pub(super) fn sub_get_packet_type(&mut self) -> PacketType { | ||||
|         self.packet_type | ||||
|     } | ||||
|  | ||||
|     // Set the transmission parameters | ||||
|     //   power           RF output power [-18..13] dBm | ||||
|     //   ramp_time       transmission ramp up time | ||||
|     pub(super) async fn sub_set_tx_params( | ||||
|         &mut self, | ||||
|         mut power: i8, | ||||
|         ramp_time: RampTime, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         if self.brd_get_radio_type() == RadioType::SX1261 { | ||||
|             if power == 15 { | ||||
|                 self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?; | ||||
|             } else { | ||||
|                 self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?; | ||||
|             } | ||||
|  | ||||
|             if power >= 14 { | ||||
|                 power = 14; | ||||
|             } else if power < -17 { | ||||
|                 power = -17; | ||||
|             } | ||||
|         } else { | ||||
|             // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) | ||||
|             let mut tx_clamp_cfg = [0x00u8]; | ||||
|             self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?; | ||||
|             tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1); | ||||
|             self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?; | ||||
|  | ||||
|             self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?; | ||||
|  | ||||
|             if power > 22 { | ||||
|                 power = 22; | ||||
|             } else if power < -9 { | ||||
|                 power = -9; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // power conversion of negative number from i8 to u8 ??? | ||||
|         self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()]) | ||||
|             .await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the modulation parameters | ||||
|     pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         if self.modulation_params.is_some() { | ||||
|             let mut buffer = [0x00u8; 4]; | ||||
|  | ||||
|             // Since this driver only supports LoRa, ensure the packet type is set accordingly | ||||
|             self.sub_set_packet_type(PacketType::LoRa).await?; | ||||
|  | ||||
|             let modulation_params = self.modulation_params.unwrap(); | ||||
|             buffer[0] = modulation_params.spreading_factor.value(); | ||||
|             buffer[1] = modulation_params.bandwidth.value(); | ||||
|             buffer[2] = modulation_params.coding_rate.value(); | ||||
|             buffer[3] = modulation_params.low_data_rate_optimize; | ||||
|  | ||||
|             self.brd_write_command(OpCode::SetModulationParams, &buffer).await?; | ||||
|             Ok(()) | ||||
|         } else { | ||||
|             Err(RadioError::ModulationParamsMissing) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Set the packet parameters | ||||
|     pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         if self.packet_params.is_some() { | ||||
|             let mut buffer = [0x00u8; 6]; | ||||
|  | ||||
|             // Since this driver only supports LoRa, ensure the packet type is set accordingly | ||||
|             self.sub_set_packet_type(PacketType::LoRa).await?; | ||||
|  | ||||
|             let packet_params = self.packet_params.unwrap(); | ||||
|             buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8; | ||||
|             buffer[1] = (packet_params.preamble_length & 0xFF) as u8; | ||||
|             buffer[2] = packet_params.implicit_header as u8; | ||||
|             buffer[3] = packet_params.payload_length; | ||||
|             buffer[4] = packet_params.crc_on as u8; | ||||
|             buffer[5] = packet_params.iq_inverted as u8; | ||||
|  | ||||
|             self.brd_write_command(OpCode::SetPacketParams, &buffer).await?; | ||||
|             Ok(()) | ||||
|         } else { | ||||
|             Err(RadioError::PacketParamsMissing) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Set the channel activity detection (CAD) parameters | ||||
|     //   symbols            number of symbols to use for CAD operations | ||||
|     //   det_peak           limit for detection of SNR peak used in the CAD | ||||
|     //   det_min            minimum symbol recognition for CAD | ||||
|     //   exit_mode          operation to be done at the end of CAD action | ||||
|     //   timeout            timeout value to abort the CAD activity | ||||
|  | ||||
|     pub(super) async fn sub_set_cad_params( | ||||
|         &mut self, | ||||
|         symbols: CADSymbols, | ||||
|         det_peak: u8, | ||||
|         det_min: u8, | ||||
|         exit_mode: CADExitMode, | ||||
|         timeout: u32, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         let mut buffer = [0x00u8; 7]; | ||||
|  | ||||
|         buffer[0] = symbols.value(); | ||||
|         buffer[1] = det_peak; | ||||
|         buffer[2] = det_min; | ||||
|         buffer[3] = exit_mode.value(); | ||||
|         buffer[4] = Self::timeout_1(timeout); | ||||
|         buffer[5] = Self::timeout_2(timeout); | ||||
|         buffer[6] = Self::timeout_3(timeout); | ||||
|  | ||||
|         self.brd_write_command(OpCode::SetCADParams, &buffer).await?; | ||||
|         self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Set the data buffer base address for transmission and reception | ||||
|     pub(super) async fn sub_set_buffer_base_address( | ||||
|         &mut self, | ||||
|         tx_base_address: u8, | ||||
|         rx_base_address: u8, | ||||
|     ) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address]) | ||||
|             .await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Get the current radio status | ||||
|     pub(super) async fn sub_get_status(&mut self) -> Result<RadioStatus, RadioError<BUS>> { | ||||
|         let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?; | ||||
|         Ok(RadioStatus { | ||||
|             cmd_status: (status & (0x07 << 1)) >> 1, | ||||
|             chip_mode: (status & (0x07 << 4)) >> 4, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     // Get the instantaneous RSSI value for the last packet received | ||||
|     pub(super) async fn sub_get_rssi_inst(&mut self) -> Result<i8, RadioError<BUS>> { | ||||
|         let mut buffer = [0x00u8]; | ||||
|         self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?; | ||||
|         let rssi: i8 = (-(buffer[0] as i8)) >> 1; // check this ??? | ||||
|         Ok(rssi) | ||||
|     } | ||||
|  | ||||
|     // Get the last received packet buffer status | ||||
|     pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError<BUS>> { | ||||
|         if self.packet_params.is_some() { | ||||
|             let mut status = [0x00u8; 2]; | ||||
|             let mut payload_length_buffer = [0x00u8]; | ||||
|  | ||||
|             self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?; | ||||
|             if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header { | ||||
|                 self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer) | ||||
|                     .await?; | ||||
|             } else { | ||||
|                 payload_length_buffer[0] = status[0]; | ||||
|             } | ||||
|  | ||||
|             let payload_length = payload_length_buffer[0]; | ||||
|             let offset = status[1]; | ||||
|  | ||||
|             Ok((payload_length, offset)) | ||||
|         } else { | ||||
|             Err(RadioError::PacketParamsMissing) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get the last received packet payload status | ||||
|     pub(super) async fn sub_get_packet_status(&mut self) -> Result<PacketStatus, RadioError<BUS>> { | ||||
|         let mut status = [0x00u8; 3]; | ||||
|         self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?; | ||||
|  | ||||
|         // check this ??? | ||||
|         let rssi = (-(status[0] as i8)) >> 1; | ||||
|         let snr = ((status[1] as i8) + 2) >> 2; | ||||
|         let signal_rssi = (-(status[2] as i8)) >> 1; | ||||
|         let freq_error = self.frequency_error; | ||||
|  | ||||
|         Ok(PacketStatus { | ||||
|             rssi, | ||||
|             snr, | ||||
|             signal_rssi, | ||||
|             freq_error, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     // Get the possible system errors | ||||
|     pub(super) async fn sub_get_device_errors(&mut self) -> Result<RadioSystemError, RadioError<BUS>> { | ||||
|         let mut errors = [0x00u8; 2]; | ||||
|         self.brd_read_command(OpCode::GetErrors, &mut errors).await?; | ||||
|  | ||||
|         Ok(RadioSystemError { | ||||
|             rc_64khz_calibration: (errors[1] & (1 << 0)) != 0, | ||||
|             rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0, | ||||
|             pll_calibration: (errors[1] & (1 << 2)) != 0, | ||||
|             adc_calibration: (errors[1] & (1 << 3)) != 0, | ||||
|             image_calibration: (errors[1] & (1 << 4)) != 0, | ||||
|             xosc_start: (errors[1] & (1 << 5)) != 0, | ||||
|             pll_lock: (errors[1] & (1 << 6)) != 0, | ||||
|             pa_ramp: (errors[0] & (1 << 0)) != 0, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     // Clear all the errors in the device | ||||
|     pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError<BUS>> { | ||||
|         self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Clear the IRQs | ||||
|     pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError<BUS>> { | ||||
|         let mut buffer = [0x00u8, 0x00u8]; | ||||
|         buffer[0] = ((irq >> 8) & 0xFF) as u8; | ||||
|         buffer[1] = (irq & 0xFF) as u8; | ||||
|         self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     // Utility functions | ||||
|  | ||||
|     fn timeout_1(timeout: u32) -> u8 { | ||||
|         ((timeout >> 16) & 0xFF) as u8 | ||||
|     } | ||||
|     fn timeout_2(timeout: u32) -> u8 { | ||||
|         ((timeout >> 8) & 0xFF) as u8 | ||||
|     } | ||||
|     fn timeout_3(timeout: u32) -> u8 { | ||||
|         (timeout & 0xFF) as u8 | ||||
|     } | ||||
|  | ||||
|     // check this ??? | ||||
|     fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 { | ||||
|         let b0 = buffer[0] as u32; | ||||
|         let b1 = buffer[1] as u32; | ||||
|         let b2 = buffer[2] as u32; | ||||
|         let b3 = buffer[3] as u32; | ||||
|         (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 | ||||
|     } | ||||
|  | ||||
|     fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 { | ||||
|         // Get integer and fractional parts of the frequency computed with a PLL step scaled value | ||||
|         let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED; | ||||
|         let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED); | ||||
|  | ||||
|         (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT) | ||||
|             + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED) | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user