diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 9d5e7aed..a80557a8 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -18,6 +18,7 @@ flavors = [ sx127x = [] stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] time = [] +defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] [dependencies] @@ -34,5 +35,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw embedded-hal = { version = "0.2", features = ["unproven"] } bit_field = { version = "0.10" } -lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] } +lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } lorawan = { version = "0.7.1", default-features = false } diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 1b2dd45c..2483dcb2 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -11,13 +11,35 @@ pub mod stm32wl; #[cfg(feature = "sx127x")] pub mod sx127x; +#[cfg(feature = "time")] +use embassy_time::{Duration, Instant, Timer}; + /// A convenience timer to use with the LoRaWAN crate -pub struct LoraTimer; +#[cfg(feature = "time")] +pub struct LoraTimer { + start: Instant, +} + +#[cfg(feature = "time")] +impl LoraTimer { + pub fn new() -> Self { + Self { start: Instant::now() } + } +} #[cfg(feature = "time")] impl lorawan_device::async_device::radio::Timer for LoraTimer { + fn reset(&mut self) { + self.start = Instant::now(); + } + + type AtFuture<'m> = impl core::future::Future + 'm; + fn at<'m>(&'m mut self, millis: u64) -> Self::AtFuture<'m> { + Timer::at(self.start + Duration::from_millis(millis)) + } + type DelayFuture<'m> = impl core::future::Future + 'm; fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> { - embassy_time::Timer::after(embassy_time::Duration::from_millis(millis)) + Timer::after(Duration::from_millis(millis)) } } diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 7822d015..4d11244b 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -1,18 +1,17 @@ //! A radio driver integration for the radio found on STM32WL family devices. use core::future::Future; -use core::mem::MaybeUninit; +use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::dma::NoDma; -use embassy_stm32::gpio::{AnyPin, Output}; -use embassy_stm32::interrupt::{InterruptExt, SUBGHZ_RADIO}; +use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; use embassy_stm32::subghz::{ - CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, Irq, LoRaBandwidth, LoRaModParams, LoRaPacketParams, - LoRaSyncWord, Ocp, PaConfig, PaSel, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, + CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams, + LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; -use embassy_stm32::Peripheral; -use embassy_sync::signal::Signal; +use embassy_sync::waitqueue::AtomicWaker; +use futures::future::poll_fn; use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; use lorawan_device::async_device::Timings; @@ -28,102 +27,52 @@ pub enum State { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RadioError; -static IRQ: Signal<(Status, u16)> = Signal::new(); - -struct StateInner<'d> { - radio: SubGhz<'d, NoDma, NoDma>, - switch: RadioSwitch<'d>, -} - -/// External state storage for the radio state -pub struct SubGhzState<'a>(MaybeUninit>); -impl<'d> SubGhzState<'d> { - pub const fn new() -> Self { - Self(MaybeUninit::uninit()) - } -} +static IRQ_WAKER: AtomicWaker = AtomicWaker::new(); /// The radio peripheral keeping the radio state and owning the radio IRQ. -pub struct SubGhzRadio<'d> { - state: *mut StateInner<'d>, - _irq: PeripheralRef<'d, SUBGHZ_RADIO>, +pub struct SubGhzRadio<'d, RS> { + radio: SubGhz<'d, NoDma, NoDma>, + switch: RS, + irq: PeripheralRef<'d, SUBGHZ_RADIO>, } -impl<'d> SubGhzRadio<'d> { +#[derive(Default)] +#[non_exhaustive] +pub struct SubGhzRadioConfig { + pub reg_mode: RegMode, + pub calibrate_image: CalibrateImage, + pub pa_config: PaConfig, + pub tx_params: TxParams, +} + +impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { /// Create a new instance of a SubGhz radio for LoRaWAN. - /// - /// # Safety - /// Do not leak self or futures - pub unsafe fn new( - state: &'d mut SubGhzState<'d>, - radio: SubGhz<'d, NoDma, NoDma>, - switch: RadioSwitch<'d>, + pub fn new( + mut radio: SubGhz<'d, NoDma, NoDma>, + switch: RS, irq: impl Peripheral

+ 'd, - ) -> Self { + config: SubGhzRadioConfig, + ) -> Result { into_ref!(irq); - let mut inner = StateInner { radio, switch }; - inner.radio.reset(); - - let state_ptr = state.0.as_mut_ptr(); - state_ptr.write(inner); + radio.reset(); irq.disable(); - irq.set_handler(|p| { - // This is safe because we only get interrupts when configured for, so - // the radio will be awaiting on the signal at this point. If not, the ISR will - // anyway only adjust the state in the IRQ signal state. - let state = &mut *(p as *mut StateInner<'d>); - state.on_interrupt(); + irq.set_handler(|_| { + IRQ_WAKER.wake(); + unsafe { SUBGHZ_RADIO::steal().disable() }; }); - irq.set_handler_context(state_ptr as *mut ()); - irq.enable(); - Self { - state: state_ptr, - _irq: irq, - } - } -} + configure_radio(&mut radio, config)?; -impl<'d> StateInner<'d> { - /// Configure radio settings in preparation for TX or RX - pub(crate) fn configure(&mut self) -> Result<(), RadioError> { - trace!("Configuring STM32WL SUBGHZ radio"); - self.radio.set_standby(StandbyClk::Rc)?; - let tcxo_mode = TcxoMode::new() - .set_txco_trim(TcxoTrim::Volts1pt7) - .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(40))); - - self.radio.set_tcxo_mode(&tcxo_mode)?; - self.radio.set_regulator_mode(RegMode::Ldo)?; - - self.radio.calibrate_image(CalibrateImage::ISM_863_870)?; - - self.radio.set_buffer_base_address(0, 0)?; - - self.radio - .set_pa_config(&PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp))?; - - self.radio.set_pa_ocp(Ocp::Max140m)?; - - // let tx_params = TxParams::LP_14.set_ramp_time(RampTime::Micros40); - self.radio - .set_tx_params(&TxParams::new().set_ramp_time(RampTime::Micros40).set_power(0x0A))?; - - self.radio.set_packet_type(PacketType::LoRa)?; - self.radio.set_lora_sync_word(LoRaSyncWord::Public)?; - trace!("Done initializing STM32WL SUBGHZ radio"); - Ok(()) + Ok(Self { radio, switch, irq }) } /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form /// the upcoming RX window start. async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - //trace!("TX Request: {}", config); - trace!("TX START"); - self.switch.set_tx_lp(); - self.configure()?; + trace!("TX request: {}", config); + self.switch.set_tx(); self.radio .set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?; @@ -139,34 +88,26 @@ impl<'d> StateInner<'d> { self.radio.set_lora_packet_params(&packet_params)?; - let irq_cfg = CfgIrq::new() - .irq_enable_all(Irq::TxDone) - .irq_enable_all(Irq::RxDone) - .irq_enable_all(Irq::Timeout); + let irq_cfg = CfgIrq::new().irq_enable_all(Irq::TxDone).irq_enable_all(Irq::Timeout); self.radio.set_irq_cfg(&irq_cfg)?; self.radio.set_buffer_base_address(0, 0)?; self.radio.write_buffer(0, buf)?; - self.radio.set_tx(Timeout::DISABLED)?; + // The maximum airtime for any LoRaWAN package is 2793.5ms. + // The value of 4000ms is copied from C driver and gives us a good safety margin. + self.radio.set_tx(Timeout::from_millis_sat(4000))?; + trace!("TX started"); loop { - let (_status, irq_status) = IRQ.wait().await; - IRQ.reset(); + let (_status, irq_status) = self.irq_wait().await; if irq_status & Irq::TxDone.mask() != 0 { - let stats = self.radio.lora_stats()?; - let (status, error_mask) = self.radio.op_error()?; - trace!( - "TX done. Stats: {:?}. OP error: {:?}, mask {:?}", - stats, - status, - error_mask - ); - + trace!("TX done"); return Ok(0); - } else if irq_status & Irq::Timeout.mask() != 0 { - trace!("TX timeout"); + } + + if irq_status & Irq::Timeout.mask() != 0 { return Err(RadioError); } } @@ -174,10 +115,15 @@ impl<'d> StateInner<'d> { fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> { let mod_params = LoRaModParams::new() - .set_sf(convert_spreading_factor(config.spreading_factor)) - .set_bw(convert_bandwidth(config.bandwidth)) + .set_sf(convert_spreading_factor(&config.spreading_factor)) + .set_bw(convert_bandwidth(&config.bandwidth)) .set_cr(CodingRate::Cr45) - .set_ldro_en(true); + .set_ldro_en(matches!( + (config.spreading_factor, config.bandwidth), + (SpreadingFactor::_12, Bandwidth::_125KHz) + | (SpreadingFactor::_12, Bandwidth::_250KHz) + | (SpreadingFactor::_11, Bandwidth::_125KHz) + )); self.radio.set_lora_mod_params(&mod_params) } @@ -185,10 +131,8 @@ impl<'d> StateInner<'d> { /// be able to hold a single LoRaWAN packet. async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> { assert!(buf.len() >= 255); - trace!("RX START"); - // trace!("Starting RX: {}", config); + trace!("RX request: {}", config); self.switch.set_rx(); - self.configure()?; self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; @@ -198,77 +142,110 @@ impl<'d> StateInner<'d> { .set_preamble_len(8) .set_header_type(HeaderType::Variable) .set_payload_len(0xFF) - .set_crc_en(true) + .set_crc_en(false) .set_invert_iq(true); self.radio.set_lora_packet_params(&packet_params)?; let irq_cfg = CfgIrq::new() .irq_enable_all(Irq::RxDone) .irq_enable_all(Irq::PreambleDetected) + .irq_enable_all(Irq::HeaderValid) .irq_enable_all(Irq::HeaderErr) - .irq_enable_all(Irq::Timeout) - .irq_enable_all(Irq::Err); + .irq_enable_all(Irq::Err) + .irq_enable_all(Irq::Timeout); self.radio.set_irq_cfg(&irq_cfg)?; + self.radio.set_buffer_base_address(0, 0)?; + + // NOTE: Upper layer handles timeout by cancelling the future self.radio.set_rx(Timeout::DISABLED)?; + trace!("RX started"); loop { - let (status, irq_status) = IRQ.wait().await; - IRQ.reset(); - trace!("RX IRQ {:?}, {:?}", status, irq_status); - if irq_status & Irq::RxDone.mask() != 0 { - let (status, len, ptr) = self.radio.rx_buffer_status()?; + let (_status, irq_status) = self.irq_wait().await; + if irq_status & Irq::RxDone.mask() != 0 { + let (_status, len, ptr) = self.radio.rx_buffer_status()?; let packet_status = self.radio.lora_packet_status()?; let rssi = packet_status.rssi_pkt().to_integer(); let snr = packet_status.snr_pkt().to_integer(); - trace!( - "RX done. Received {} bytes. RX status: {:?}. Pkt status: {:?}", - len, - status.cmd(), - packet_status, - ); self.radio.read_buffer(ptr, &mut buf[..len as usize])?; self.radio.set_standby(StandbyClk::Rc)?; + + trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]); return Ok((len as usize, RxQuality::new(rssi, snr as i8))); - } else if irq_status & (Irq::Timeout.mask() | Irq::TxDone.mask()) != 0 { + } + + if irq_status & Irq::Timeout.mask() != 0 { return Err(RadioError); } } } - /// Read interrupt status and store in global signal - fn on_interrupt(&mut self) { - let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); - self.radio - .clear_irq_status(irq_status) - .expect("error clearing irq status"); - if irq_status & Irq::PreambleDetected.mask() != 0 { - trace!("Preamble detected, ignoring"); - } else { - IRQ.signal((status, irq_status)); - } + async fn irq_wait(&mut self) -> (Status, u16) { + poll_fn(|cx| { + self.irq.unpend(); + self.irq.enable(); + IRQ_WAKER.register(cx.waker()); + + let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); + self.radio + .clear_irq_status(irq_status) + .expect("error clearing irq status"); + + trace!("SUGHZ IRQ 0b{=u16:b}, {:?}", irq_status, status); + + if irq_status == 0 { + Poll::Pending + } else { + Poll::Ready((status, irq_status)) + } + }) + .await } } -impl PhyRxTx for SubGhzRadio<'static> { +fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConfig) -> Result<(), RadioError> { + trace!("Configuring STM32WL SUBGHZ radio"); + + radio.set_regulator_mode(config.reg_mode)?; + radio.set_standby(StandbyClk::Rc)?; + + let tcxo_mode = TcxoMode::new() + .set_txco_trim(TcxoTrim::Volts1pt7) + .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(100))); + radio.set_tcxo_mode(&tcxo_mode)?; + // Reduce input capacitance as shown in Reference Manual "Figure 23. HSE32 TCXO control". + // The STM32CUBE C driver also does this. + radio.set_hse_in_trim(HseTrim::MIN)?; + + // Re-calibrate everything after setting the TXCO config. + radio.calibrate(0x7F)?; + radio.calibrate_image(config.calibrate_image)?; + + radio.set_pa_config(&config.pa_config)?; + radio.set_tx_params(&config.tx_params)?; + radio.set_pa_ocp(Ocp::Max140m)?; + + radio.set_packet_type(PacketType::LoRa)?; + radio.set_lora_sync_word(LoRaSyncWord::Public)?; + + trace!("Done initializing STM32WL SUBGHZ radio"); + Ok(()) +} + +impl PhyRxTx for SubGhzRadio<'static, RS> { type PhyError = RadioError; - type TxFuture<'m> = impl Future> + 'm; + type TxFuture<'m> = impl Future> + 'm where RS: 'm; fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { - async move { - let inner = unsafe { &mut *self.state }; - inner.do_tx(config, buf).await - } + async move { self.do_tx(config, buf).await } } - type RxFuture<'m> = impl Future> + 'm; + type RxFuture<'m> = impl Future> + 'm where RS: 'm; fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { - async move { - let inner = unsafe { &mut *self.state }; - inner.do_rx(config, buf).await - } + async move { self.do_rx(config, buf).await } } } @@ -278,48 +255,21 @@ impl From for RadioError { } } -impl<'d> Timings for SubGhzRadio<'d> { +impl<'d, RS> Timings for SubGhzRadio<'d, RS> { fn get_rx_window_offset_ms(&self) -> i32 { - -200 + -500 } fn get_rx_window_duration_ms(&self) -> u32 { - 800 + 3000 } } -/// Represents the radio switch found on STM32WL based boards, used to control the radio for transmission or reception. -pub struct RadioSwitch<'d> { - ctrl1: Output<'d, AnyPin>, - ctrl2: Output<'d, AnyPin>, - ctrl3: Output<'d, AnyPin>, +pub trait RadioSwitch { + fn set_rx(&mut self); + fn set_tx(&mut self); } -impl<'d> RadioSwitch<'d> { - pub fn new(ctrl1: Output<'d, AnyPin>, ctrl2: Output<'d, AnyPin>, ctrl3: Output<'d, AnyPin>) -> Self { - Self { ctrl1, ctrl2, ctrl3 } - } - - pub(crate) fn set_rx(&mut self) { - self.ctrl1.set_high(); - self.ctrl2.set_low(); - self.ctrl3.set_high(); - } - - pub(crate) fn set_tx_lp(&mut self) { - self.ctrl1.set_high(); - self.ctrl2.set_high(); - self.ctrl3.set_high(); - } - - #[allow(dead_code)] - pub(crate) fn set_tx_hp(&mut self) { - self.ctrl2.set_high(); - self.ctrl1.set_low(); - self.ctrl3.set_high(); - } -} - -fn convert_spreading_factor(sf: SpreadingFactor) -> SF { +fn convert_spreading_factor(sf: &SpreadingFactor) -> SF { match sf { SpreadingFactor::_7 => SF::Sf7, SpreadingFactor::_8 => SF::Sf8, @@ -330,7 +280,7 @@ fn convert_spreading_factor(sf: SpreadingFactor) -> SF { } } -fn convert_bandwidth(bw: Bandwidth) -> LoRaBandwidth { +fn convert_bandwidth(bw: &Bandwidth) -> LoRaBandwidth { match bw { Bandwidth::_125KHz => LoRaBandwidth::Bw125, Bandwidth::_250KHz => LoRaBandwidth::Bw250, diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index 69c192c6..34767491 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -202,54 +202,11 @@ impl Default for Config { pub(crate) unsafe fn init(config: Config) { let (sys_clk, sw, vos) = match config.mux { - ClockSrc::HSI16 => { - // Enable HSI16 - RCC.cr().write(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - - (HSI_FREQ.0, 0x01, VoltageScale::Range2) - } - ClockSrc::HSE32 => { - // Enable HSE32 - RCC.cr().write(|w| { - w.set_hsebyppwr(true); - w.set_hseon(true); - }); - while !RCC.cr().read().hserdy() {} - - (HSE32_FREQ.0, 0x02, VoltageScale::Range1) - } - ClockSrc::MSI(range) => { - RCC.cr().write(|w| { - w.set_msirange(range.into()); - w.set_msion(true); - }); - - while !RCC.cr().read().msirdy() {} - - (range.freq(), 0x00, range.vos()) - } + ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Range2), + ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Range1), + ClockSrc::MSI(range) => (range.freq(), 0x00, range.vos()), }; - RCC.cfgr().modify(|w| { - w.set_sw(sw.into()); - if config.ahb_pre == AHBPrescaler::NotDivided { - w.set_hpre(0); - } else { - w.set_hpre(config.ahb_pre.into()); - } - w.set_ppre1(config.apb1_pre.into()); - w.set_ppre2(config.apb2_pre.into()); - }); - - RCC.extcfgr().modify(|w| { - if config.shd_ahb_pre == AHBPrescaler::NotDivided { - w.set_shdhpre(0); - } else { - w.set_shdhpre(config.shd_ahb_pre.into()); - } - }); - let ahb_freq: u32 = match config.ahb_pre { AHBPrescaler::NotDivided => sys_clk, pre => { @@ -288,16 +245,6 @@ pub(crate) unsafe fn init(config: Config) { } }; - let apb3_freq = shd_ahb_freq; - - if config.enable_lsi { - let csr = RCC.csr().read(); - if !csr.lsion() { - RCC.csr().modify(|w| w.set_lsion(true)); - while !RCC.csr().read().lsirdy() {} - } - } - // Adjust flash latency let flash_clk_src_freq: u32 = shd_ahb_freq; let ws = match vos { @@ -319,6 +266,61 @@ pub(crate) unsafe fn init(config: Config) { while FLASH.acr().read().latency() != ws {} + match config.mux { + ClockSrc::HSI16 => { + // Enable HSI16 + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + } + ClockSrc::HSE32 => { + // Enable HSE32 + RCC.cr().write(|w| { + w.set_hsebyppwr(true); + w.set_hseon(true); + }); + while !RCC.cr().read().hserdy() {} + } + ClockSrc::MSI(range) => { + let cr = RCC.cr().read(); + assert!(!cr.msion() || cr.msirdy()); + RCC.cr().write(|w| { + w.set_msirgsel(true); + w.set_msirange(range.into()); + w.set_msion(true); + }); + while !RCC.cr().read().msirdy() {} + } + } + + RCC.extcfgr().modify(|w| { + if config.shd_ahb_pre == AHBPrescaler::NotDivided { + w.set_shdhpre(0); + } else { + w.set_shdhpre(config.shd_ahb_pre.into()); + } + }); + + RCC.cfgr().modify(|w| { + w.set_sw(sw.into()); + if config.ahb_pre == AHBPrescaler::NotDivided { + w.set_hpre(0); + } else { + w.set_hpre(config.ahb_pre.into()); + } + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + }); + + // TODO: switch voltage range + + if config.enable_lsi { + let csr = RCC.csr().read(); + if !csr.lsion() { + RCC.csr().modify(|w| w.set_lsion(true)); + while !RCC.csr().read().lsirdy() {} + } + } + set_freqs(Clocks { sys: Hertz(sys_clk), ahb1: Hertz(ahb_freq), @@ -326,7 +328,7 @@ pub(crate) unsafe fn init(config: Config) { ahb3: Hertz(shd_ahb_freq), apb1: Hertz(apb1_freq), apb2: Hertz(apb2_freq), - apb3: Hertz(apb3_freq), + apb3: Hertz(shd_ahb_freq), apb1_tim: Hertz(apb1_tim_freq), apb2_tim: Hertz(apb2_tim_freq), }); diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index acc29d87..02e6020b 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -179,6 +179,19 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { ) } + /// Useful for on chip peripherals like SUBGHZ which are hardwired. + /// The bus can optionally be exposed externally with `Spi::new()` still. + #[allow(dead_code)] + pub(crate) fn new_internal( + peri: impl Peripheral

+ 'd, + txdma: impl Peripheral

+ 'd, + rxdma: impl Peripheral

+ 'd, + freq: Hertz, + config: Config, + ) -> Self { + Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) + } + fn new_inner( peri: impl Peripheral

+ 'd, sck: Option>, diff --git a/embassy-stm32/src/subghz/mod.rs b/embassy-stm32/src/subghz/mod.rs index a74f9a6d..33398fa1 100644 --- a/embassy-stm32/src/subghz/mod.rs +++ b/embassy-stm32/src/subghz/mod.rs @@ -81,7 +81,7 @@ pub use value_error::ValueError; use crate::dma::NoDma; use crate::peripherals::SUBGHZSPI; use crate::rcc::sealed::RccPeripheral; -use crate::spi::{BitOrder, Config as SpiConfig, MisoPin, MosiPin, SckPin, Spi, MODE_0}; +use crate::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -212,9 +212,6 @@ impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> { /// clock. pub fn new( peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - mosi: impl Peripheral

> + 'd, - miso: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, ) -> Self { @@ -227,7 +224,7 @@ impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> { let mut config = SpiConfig::default(); config.mode = MODE_0; config.bit_order = BitOrder::MsbFirst; - let spi = Spi::new(peri, sck, mosi, miso, txdma, rxdma, clk, config); + let spi = Spi::new_internal(peri, txdma, rxdma, clk, config); unsafe { wakeup() }; diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 11751a21..6358fe86 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -14,7 +14,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} -lorawan-device = { version = "0.7.1", default-features = false, features = ["async"], optional = true } +lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index 303558b9..00ff67f3 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs @@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) { let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap(); let region = region::EU868::default().into(); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer, Rng::new(p.RNG)); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); defmt::info!("Joining LoRaWAN network"); diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 5f6679f4..e2e7d407 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -10,7 +10,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } -lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] } +lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } defmt = "0.3" diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 7e8a8946..9143e64d 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -9,7 +9,7 @@ use embassy_executor::Spawner; use embassy_lora::stm32wl::*; use embassy_lora::LoraTimer; use embassy_stm32::dma::NoDma; -use embassy_stm32::gpio::{Level, Output, Pin, Speed}; +use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; use embassy_stm32::rng::Rng; use embassy_stm32::subghz::*; use embassy_stm32::{interrupt, pac}; @@ -17,6 +17,32 @@ use lorawan::default_crypto::DefaultFactory as Crypto; use lorawan_device::async_device::{region, Device, JoinMode}; use {defmt_rtt as _, panic_probe as _}; +struct RadioSwitch<'a> { + ctrl1: Output<'a, AnyPin>, + ctrl2: Output<'a, AnyPin>, + ctrl3: Output<'a, AnyPin>, +} + +impl<'a> RadioSwitch<'a> { + fn new(ctrl1: Output<'a, AnyPin>, ctrl2: Output<'a, AnyPin>, ctrl3: Output<'a, AnyPin>) -> Self { + Self { ctrl1, ctrl2, ctrl3 } + } +} + +impl<'a> embassy_lora::stm32wl::RadioSwitch for RadioSwitch<'a> { + fn set_rx(&mut self) { + self.ctrl1.set_high(); + self.ctrl2.set_low(); + self.ctrl3.set_high(); + } + + fn set_tx(&mut self) { + self.ctrl1.set_high(); + self.ctrl2.set_high(); + self.ctrl3.set_high(); + } +} + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -31,18 +57,19 @@ async fn main(_spawner: Spawner) { let ctrl3 = Output::new(p.PC5.degrade(), Level::High, Speed::High); let rfs = RadioSwitch::new(ctrl1, ctrl2, ctrl3); - let radio = SubGhz::new(p.SUBGHZSPI, p.PA5, p.PA7, p.PA6, NoDma, NoDma); - + let radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); let irq = interrupt::take!(SUBGHZ_RADIO); - static mut RADIO_STATE: SubGhzState<'static> = SubGhzState::new(); - let radio = unsafe { SubGhzRadio::new(&mut RADIO_STATE, radio, rfs, irq) }; + + let mut radio_config = SubGhzRadioConfig::default(); + radio_config.calibrate_image = CalibrateImage::ISM_863_870; + let radio = SubGhzRadio::new(radio, rfs, irq, radio_config).unwrap(); let mut region: region::Configuration = region::EU868::default().into(); // NOTE: This is specific for TTN, as they have a special RX1 delay region.set_receive_delay1(5000); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer, Rng::new(p.RNG)); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); // Depending on network, this might be part of JOIN device.set_datarate(region::DR::_0); // SF12 diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index c5e9bb59..8f674d79 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs @@ -72,7 +72,7 @@ async fn main(_spawner: Spawner) { unsafe { interrupt::SUBGHZ_RADIO::steal() }.disable(); }); - let mut radio = SubGhz::new(p.SUBGHZSPI, p.PA5, p.PA7, p.PA6, NoDma, NoDma); + let mut radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); defmt::info!("Radio ready for use");