mod radio_kind_params;
use defmt::debug;
use embedded_hal_async::delay::DelayUs;
use embedded_hal_async::spi::*;
use radio_kind_params::*;
use crate::mod_params::*;
use crate::{InterfaceVariant, RadioKind, SpiInterface};
const LORA_MAC_PUBLIC_SYNCWORD: u8 = 0x34; const LORA_MAC_PRIVATE_SYNCWORD: u8 = 0x12; const TCXO_FOR_OSCILLATOR: u8 = 0x10u8;
const FREQUENCY_SYNTHESIZER_STEP: f64 = 61.03515625; impl ModulationParams {
pub fn new_for_sx1276_7_8_9(
spreading_factor: SpreadingFactor,
bandwidth: Bandwidth,
coding_rate: CodingRate,
frequency_in_hz: u32,
) -> Result<Self, RadioError> {
spreading_factor_value(spreading_factor)?;
bandwidth_value(bandwidth)?;
coding_rate_value(coding_rate)?;
if ((bandwidth == Bandwidth::_250KHz) || (bandwidth == Bandwidth::_500KHz)) && (frequency_in_hz < 400_000_000) {
return Err(RadioError::InvalidBandwidthForFrequency);
}
let bw_in_hz = bandwidth.value_in_hz();
let symbol_duration = 1000 / (bw_in_hz / (0x01u32 << spreading_factor_value(spreading_factor)?));
let mut low_data_rate_optimize = 0x00u8;
if symbol_duration > 16 {
low_data_rate_optimize = 0x01u8
}
Ok(Self {
spreading_factor,
bandwidth,
coding_rate,
low_data_rate_optimize,
frequency_in_hz,
})
}
}
impl PacketParams {
pub fn new_for_sx1276_7_8_9(
preamble_length: u16,
implicit_header: bool,
payload_length: u8,
crc_on: bool,
iq_inverted: bool,
modulation_params: &ModulationParams,
) -> Result<Self, RadioError> {
if (modulation_params.spreading_factor == SpreadingFactor::_6) && !implicit_header {
return Err(RadioError::InvalidSF6ExplicitHeaderRequest);
}
Ok(Self {
preamble_length,
implicit_header,
payload_length,
crc_on,
iq_inverted,
})
}
}
pub struct SX1276_7_8_9<SPI, IV> {
board_type: BoardType,
intf: SpiInterface<SPI, IV>,
}
impl<SPI, IV> SX1276_7_8_9<SPI, IV>
where
SPI: SpiBus<u8> + 'static,
IV: InterfaceVariant + 'static,
{
pub fn new(board_type: BoardType, spi: SPI, mut iv: IV) -> Self {
iv.set_board_type(board_type);
let intf = SpiInterface::new(spi, iv);
Self { board_type, intf }
}
async fn write_register(
&mut self,
register: Register,
value: u8,
is_sleep_command: bool,
) -> Result<(), RadioError> {
let write_buffer = [register.write_addr(), value];
self.intf.write(&[&write_buffer], is_sleep_command).await
}
async fn read_register(&mut self, register: Register) -> Result<u8, RadioError> {
let write_buffer = [register.read_addr()];
let mut read_buffer = [0x00u8];
self.intf.read(&[&write_buffer], &mut read_buffer, None).await?;
Ok(read_buffer[0])
}
async fn set_lora_symbol_num_timeout(&mut self, symbol_num: u8) -> Result<(), RadioError> {
self.write_register(Register::RegSymbTimeoutLsb, symbol_num, false)
.await
}
async fn set_ocp(&mut self, ocp_trim: OcpTrim) -> Result<(), RadioError> {
self.write_register(Register::RegOcp, ocp_trim.value(), false).await
}
}
impl<SPI, IV> RadioKind for SX1276_7_8_9<SPI, IV>
where
SPI: SpiBus<u8> + 'static,
IV: InterfaceVariant + 'static,
{
fn get_board_type(&self) -> BoardType {
self.board_type
}
async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> {
self.intf.iv.reset(delay).await?;
self.set_sleep(delay).await?; Ok(())
}
async fn ensure_ready(&mut self, _mode: RadioMode) -> Result<(), RadioError> {
Ok(())
}
async fn init_rf_switch(&mut self) -> Result<(), RadioError> {
Ok(())
}
async fn set_standby(&mut self) -> Result<(), RadioError> {
self.write_register(Register::RegOpMode, LoRaMode::Standby.value(), false)
.await?;
self.intf.iv.disable_rf_switch().await
}
async fn set_sleep(&mut self, _delay: &mut impl DelayUs) -> Result<bool, RadioError> {
self.intf.iv.disable_rf_switch().await?;
self.write_register(Register::RegOpMode, LoRaMode::Sleep.value(), true)
.await?;
Ok(false) }
async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError> {
if enable_public_network {
self.write_register(Register::RegSyncWord, LORA_MAC_PUBLIC_SYNCWORD, false)
.await
} else {
self.write_register(Register::RegSyncWord, LORA_MAC_PRIVATE_SYNCWORD, false)
.await
}
}
async fn set_oscillator(&mut self) -> Result<(), RadioError> {
self.write_register(Register::RegTcxo, TCXO_FOR_OSCILLATOR, false).await
}
async fn set_regulator_mode(&mut self) -> Result<(), RadioError> {
Ok(())
}
async fn set_tx_rx_buffer_base_address(
&mut self,
tx_base_addr: usize,
rx_base_addr: usize,
) -> Result<(), RadioError> {
if tx_base_addr > 255 || rx_base_addr > 255 {
return Err(RadioError::InvalidBaseAddress(tx_base_addr, rx_base_addr));
}
self.write_register(Register::RegFifoTxBaseAddr, 0x00u8, false).await?;
self.write_register(Register::RegFifoRxBaseAddr, 0x00u8, false).await
}
async fn set_tx_power_and_ramp_time(
&mut self,
p_out: i32,
_mdltn_params: Option<&ModulationParams>,
tx_boosted_if_possible: bool,
is_tx_prep: bool,
) -> Result<(), RadioError> {
if tx_boosted_if_possible {
if !(2..=20).contains(&p_out) {
return Err(RadioError::InvalidOutputPower);
}
let output_power: i32 = p_out - 2;
debug!("tx power = {}", output_power);
if p_out > 17 {
self.write_register(Register::RegPaDac, PaDac::_20DbmOn.value(), false)
.await?;
self.set_ocp(OcpTrim::_240Ma).await?;
} else {
self.write_register(Register::RegPaDac, PaDac::_20DbmOff.value(), false)
.await?;
self.set_ocp(OcpTrim::_100Ma).await?;
}
self.write_register(
Register::RegPaConfig,
PaConfig::PaBoost.value() | (output_power as u8),
false,
)
.await?;
} else {
if !(-4..=14).contains(&p_out) {
return Err(RadioError::InvalidOutputPower);
}
let output_power: i32 = p_out;
debug!("tx power = {}", output_power);
self.write_register(Register::RegPaDac, PaDac::_20DbmOff.value(), false)
.await?;
self.set_ocp(OcpTrim::_100Ma).await?;
self.write_register(
Register::RegPaConfig,
PaConfig::MaxPower7NoPaBoost.value() | (output_power as u8),
false,
)
.await?;
}
let ramp_time = match is_tx_prep {
true => RampTime::Ramp40Us, false => RampTime::Ramp250Us, };
self.write_register(Register::RegPaRamp, ramp_time.value(), false).await
}
async fn update_retention_list(&mut self) -> Result<(), RadioError> {
Ok(())
}
async fn set_modulation_params(&mut self, mdltn_params: &ModulationParams) -> Result<(), RadioError> {
let spreading_factor_val = spreading_factor_value(mdltn_params.spreading_factor)?;
let bandwidth_val = bandwidth_value(mdltn_params.bandwidth)?;
let coding_rate_denominator_val = coding_rate_denominator_value(mdltn_params.coding_rate)?;
debug!(
"sf = {}, bw = {}, cr_denom = {}",
spreading_factor_val, bandwidth_val, coding_rate_denominator_val
);
let mut ldro_agc_auto_flags = 0x00u8; if mdltn_params.low_data_rate_optimize != 0 {
ldro_agc_auto_flags = 0x08u8; }
let mut optimize = 0xc3u8;
let mut threshold = 0x0au8;
if mdltn_params.spreading_factor == SpreadingFactor::_6 {
optimize = 0xc5u8;
threshold = 0x0cu8;
}
self.write_register(Register::RegDetectionOptimize, optimize, false)
.await?;
self.write_register(Register::RegDetectionThreshold, threshold, false)
.await?;
let mut config_2 = self.read_register(Register::RegModemConfig2).await?;
config_2 = (config_2 & 0x0fu8) | ((spreading_factor_val << 4) & 0xf0u8);
self.write_register(Register::RegModemConfig2, config_2, false).await?;
let mut config_1 = self.read_register(Register::RegModemConfig1).await?;
config_1 = (config_1 & 0x0fu8) | (bandwidth_val << 4);
self.write_register(Register::RegModemConfig1, config_1, false).await?;
let cr = coding_rate_denominator_val - 4;
config_1 = self.read_register(Register::RegModemConfig1).await?;
config_1 = (config_1 & 0xf1u8) | (cr << 1);
self.write_register(Register::RegModemConfig1, config_1, false).await?;
let mut config_3 = self.read_register(Register::RegModemConfig3).await?;
config_3 = (config_3 & 0xf3u8) | ldro_agc_auto_flags;
self.write_register(Register::RegModemConfig3, config_3, false).await
}
async fn set_packet_params(&mut self, pkt_params: &PacketParams) -> Result<(), RadioError> {
self.write_register(
Register::RegPreambleMsb,
((pkt_params.preamble_length >> 8) & 0x00ff) as u8,
false,
)
.await?;
self.write_register(
Register::RegPreambleLsb,
(pkt_params.preamble_length & 0x00ff) as u8,
false,
)
.await?;
let mut config_1 = self.read_register(Register::RegModemConfig1).await?;
if pkt_params.implicit_header {
config_1 |= 0x01u8;
} else {
config_1 &= 0xfeu8;
}
self.write_register(Register::RegModemConfig1, config_1, false).await?;
let mut config_2 = self.read_register(Register::RegModemConfig2).await?;
if pkt_params.crc_on {
config_2 |= 0x04u8;
} else {
config_2 &= 0xfbu8;
}
self.write_register(Register::RegModemConfig2, config_2, false).await?;
let mut invert_iq = 0x27u8;
let mut invert_iq2 = 0x1du8;
if pkt_params.iq_inverted {
invert_iq = 0x66u8;
invert_iq2 = 0x19u8;
}
self.write_register(Register::RegInvertiq, invert_iq, false).await?;
self.write_register(Register::RegInvertiq2, invert_iq2, false).await
}
async fn calibrate_image(&mut self, _frequency_in_hz: u32) -> Result<(), RadioError> {
Ok(())
}
async fn set_channel(&mut self, frequency_in_hz: u32) -> Result<(), RadioError> {
debug!("channel = {}", frequency_in_hz);
let frf = (frequency_in_hz as f64 / FREQUENCY_SYNTHESIZER_STEP) as u32;
self.write_register(Register::RegFrfMsb, ((frf & 0x00FF0000) >> 16) as u8, false)
.await?;
self.write_register(Register::RegFrfMid, ((frf & 0x0000FF00) >> 8) as u8, false)
.await?;
self.write_register(Register::RegFrfLsb, (frf & 0x000000FF) as u8, false)
.await
}
async fn set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError> {
self.write_register(Register::RegFifoAddrPtr, 0x00u8, false).await?;
self.write_register(Register::RegPayloadLength, 0x00u8, false).await?;
for byte in payload {
self.write_register(Register::RegFifo, *byte, false).await?;
}
self.write_register(Register::RegPayloadLength, payload.len() as u8, false)
.await
}
async fn do_tx(&mut self, _timeout_in_ms: u32) -> Result<(), RadioError> {
self.intf.iv.enable_rf_switch_tx().await?;
self.write_register(Register::RegOpMode, LoRaMode::Tx.value(), false)
.await
}
async fn do_rx(
&mut self,
_rx_pkt_params: &PacketParams,
duty_cycle_params: Option<&DutyCycleParams>,
rx_continuous: bool,
rx_boosted_if_supported: bool,
symbol_timeout: u16,
_rx_timeout_in_ms: u32,
) -> Result<(), RadioError> {
if let Some(&_duty_cycle) = duty_cycle_params {
return Err(RadioError::DutyCycleUnsupported);
}
self.intf.iv.enable_rf_switch_rx().await?;
let mut symbol_timeout_final = symbol_timeout;
if rx_continuous {
symbol_timeout_final = 0;
}
if symbol_timeout_final > 0x00ffu16 {
return Err(RadioError::InvalidSymbolTimeout);
}
self.set_lora_symbol_num_timeout(symbol_timeout_final as u8).await?;
let mut lna_gain_final = LnaGain::G1.value();
if rx_boosted_if_supported {
lna_gain_final = LnaGain::G1.boosted_value();
}
self.write_register(Register::RegLna, lna_gain_final, false).await?;
self.write_register(Register::RegFifoAddrPtr, 0x00u8, false).await?;
self.write_register(Register::RegPayloadLength, 0xffu8, false).await?; if rx_continuous {
self.write_register(Register::RegOpMode, LoRaMode::RxContinuous.value(), false)
.await
} else {
self.write_register(Register::RegOpMode, LoRaMode::RxSingle.value(), false)
.await
}
}
async fn get_rx_payload(
&mut self,
_rx_pkt_params: &PacketParams,
receiving_buffer: &mut [u8],
) -> Result<u8, RadioError> {
let payload_length = self.read_register(Register::RegRxNbBytes).await?;
if (payload_length as usize) > receiving_buffer.len() {
return Err(RadioError::PayloadSizeMismatch(
payload_length as usize,
receiving_buffer.len(),
));
}
let fifo_addr = self.read_register(Register::RegFifoRxCurrentAddr).await?;
self.write_register(Register::RegFifoAddrPtr, fifo_addr, false).await?;
for i in 0..payload_length {
let byte = self.read_register(Register::RegFifo).await?;
receiving_buffer[i as usize] = byte;
}
self.write_register(Register::RegFifoAddrPtr, 0x00u8, false).await?;
Ok(payload_length)
}
async fn get_rx_packet_status(&mut self) -> Result<PacketStatus, RadioError> {
let rssi_raw = self.read_register(Register::RegPktRssiValue).await?;
let rssi = (rssi_raw as i16) - 157i16; let snr_raw = self.read_register(Register::RegPktRssiValue).await?;
let snr = snr_raw as i16;
Ok(PacketStatus { rssi, snr })
}
async fn do_cad(
&mut self,
_mdltn_params: &ModulationParams,
rx_boosted_if_supported: bool,
) -> Result<(), RadioError> {
self.intf.iv.enable_rf_switch_rx().await?;
let mut lna_gain_final = LnaGain::G1.value();
if rx_boosted_if_supported {
lna_gain_final = LnaGain::G1.boosted_value();
}
self.write_register(Register::RegLna, lna_gain_final, false).await?;
self.write_register(Register::RegOpMode, LoRaMode::Cad.value(), false)
.await
}
async fn set_irq_params(&mut self, radio_mode: Option<RadioMode>) -> Result<(), RadioError> {
match radio_mode {
Some(RadioMode::Transmit) => {
self.write_register(
Register::RegIrqFlagsMask,
IrqMask::All.value() ^ IrqMask::TxDone.value(),
false,
)
.await?;
let mut dio_mapping_1 = self.read_register(Register::RegDioMapping1).await?;
dio_mapping_1 = (dio_mapping_1 & DioMapping1Dio0::Mask.value()) | DioMapping1Dio0::TxDone.value();
self.write_register(Register::RegDioMapping1, dio_mapping_1, false)
.await?;
self.write_register(Register::RegIrqFlags, 0x00u8, false).await?;
}
Some(RadioMode::Receive) => {
self.write_register(
Register::RegIrqFlagsMask,
IrqMask::All.value()
^ (IrqMask::RxDone.value() | IrqMask::RxTimeout.value() | IrqMask::CRCError.value()),
false,
)
.await?;
let mut dio_mapping_1 = self.read_register(Register::RegDioMapping1).await?;
dio_mapping_1 = (dio_mapping_1 & DioMapping1Dio0::Mask.value()) | DioMapping1Dio0::RxDone.value();
self.write_register(Register::RegDioMapping1, dio_mapping_1, false)
.await?;
self.write_register(Register::RegIrqFlags, 0x00u8, false).await?;
}
Some(RadioMode::ChannelActivityDetection) => {
self.write_register(
Register::RegIrqFlagsMask,
IrqMask::All.value() ^ (IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value()),
false,
)
.await?;
let mut dio_mapping_1 = self.read_register(Register::RegDioMapping1).await?;
dio_mapping_1 = (dio_mapping_1 & DioMapping1Dio0::Mask.value()) | DioMapping1Dio0::CadDone.value();
self.write_register(Register::RegDioMapping1, dio_mapping_1, false)
.await?;
self.write_register(Register::RegIrqFlags, 0x00u8, false).await?;
}
_ => {
self.write_register(Register::RegIrqFlagsMask, IrqMask::All.value(), false)
.await?;
let mut dio_mapping_1 = self.read_register(Register::RegDioMapping1).await?;
dio_mapping_1 = (dio_mapping_1 & DioMapping1Dio0::Mask.value()) | DioMapping1Dio0::Other.value();
self.write_register(Register::RegDioMapping1, dio_mapping_1, false)
.await?;
self.write_register(Register::RegIrqFlags, 0xffu8, false).await?;
}
}
Ok(())
}
async fn process_irq(
&mut self,
radio_mode: RadioMode,
_rx_continuous: bool,
cad_activity_detected: Option<&mut bool>,
) -> Result<(), RadioError> {
loop {
debug!("process_irq loop entered");
self.intf.iv.await_irq().await?;
let irq_flags = self.read_register(Register::RegIrqFlags).await?;
self.write_register(Register::RegIrqFlags, 0xffu8, false).await?; debug!(
"process_irq satisfied: irq_flags = 0x{:x} in radio mode {}",
irq_flags, radio_mode
);
if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() {
debug!("HeaderValid in radio mode {}", radio_mode);
}
if radio_mode == RadioMode::Transmit {
if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() {
debug!("TxDone in radio mode {}", radio_mode);
return Ok(());
}
} else if radio_mode == RadioMode::Receive {
if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() {
debug!("CRCError in radio mode {}", radio_mode);
}
if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() {
debug!("RxDone in radio mode {}", radio_mode);
return Ok(());
}
if (irq_flags & IrqMask::RxTimeout.value()) == IrqMask::RxTimeout.value() {
debug!("RxTimeout in radio mode {}", radio_mode);
return Err(RadioError::ReceiveTimeout);
}
} else if radio_mode == RadioMode::ChannelActivityDetection
&& (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value()
{
debug!("CADDone in radio mode {}", radio_mode);
if cad_activity_detected.is_some() {
*(cad_activity_detected.unwrap()) =
(irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value();
}
return Ok(());
}
}
}
}