diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-09-04 07:17:23 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-09-04 07:17:23 +0000 |
| commit | 6264fe39a563b77e5a8fd873e7d29841af3b3c73 (patch) | |
| tree | b0f17887591523166f3ed3f7eea4330743c7b692 | |
| parent | 7d5c1fcebf875a5584518b99a70e8de980fba2ae (diff) | |
| parent | 6cdff72d6d2becb3f8a4659571fd5e6a339cbefa (diff) | |
Merge #839
839: Misc LoRaWAN improvements r=lulf a=timokroeger
Trying too get `embassy-lora` running on a [LoRa-E5 Dev Board](https://wiki.seeedstudio.com/LoRa_E5_Dev_Board/).
I can see the join message arriving in the The Things Network console but the device does not receive the accept message yet.
Opening this PR anyway because I think there are some nice things to decouple the lora crate from the nucleo board.
`@lulf` Could you test if this PR breaks your LoRa setup? Marking as draft for the time being.
Co-authored-by: Timo Kröger <[email protected]>
Co-authored-by: Ulf Lilleengen <[email protected]>
| -rw-r--r-- | embassy-lora/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-lora/src/lib.rs | 26 | ||||
| -rw-r--r-- | embassy-lora/src/stm32wl/mod.rs | 310 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/wl.rs | 116 | ||||
| -rw-r--r-- | embassy-stm32/src/spi/mod.rs | 13 | ||||
| -rw-r--r-- | embassy-stm32/src/subghz/mod.rs | 7 | ||||
| -rw-r--r-- | examples/stm32l0/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/stm32l0/src/bin/lorawan.rs | 2 | ||||
| -rw-r--r-- | examples/stm32wl/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/stm32wl/src/bin/lorawan.rs | 39 | ||||
| -rw-r--r-- | examples/stm32wl/src/bin/subghz.rs | 2 |
11 files changed, 267 insertions, 255 deletions
diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 9d5e7aed2..a80557a89 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml | |||
| @@ -18,6 +18,7 @@ flavors = [ | |||
| 18 | sx127x = [] | 18 | sx127x = [] |
| 19 | stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] | 19 | stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] |
| 20 | time = [] | 20 | time = [] |
| 21 | defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] | ||
| 21 | 22 | ||
| 22 | [dependencies] | 23 | [dependencies] |
| 23 | 24 | ||
| @@ -34,5 +35,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw | |||
| 34 | embedded-hal = { version = "0.2", features = ["unproven"] } | 35 | embedded-hal = { version = "0.2", features = ["unproven"] } |
| 35 | bit_field = { version = "0.10" } | 36 | bit_field = { version = "0.10" } |
| 36 | 37 | ||
| 37 | lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] } | 38 | lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } |
| 38 | lorawan = { version = "0.7.1", default-features = false } | 39 | lorawan = { version = "0.7.1", default-features = false } |
diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 1b2dd45c2..2483dcb2e 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs | |||
| @@ -11,13 +11,35 @@ pub mod stm32wl; | |||
| 11 | #[cfg(feature = "sx127x")] | 11 | #[cfg(feature = "sx127x")] |
| 12 | pub mod sx127x; | 12 | pub mod sx127x; |
| 13 | 13 | ||
| 14 | #[cfg(feature = "time")] | ||
| 15 | use embassy_time::{Duration, Instant, Timer}; | ||
| 16 | |||
| 14 | /// A convenience timer to use with the LoRaWAN crate | 17 | /// A convenience timer to use with the LoRaWAN crate |
| 15 | pub struct LoraTimer; | 18 | #[cfg(feature = "time")] |
| 19 | pub struct LoraTimer { | ||
| 20 | start: Instant, | ||
| 21 | } | ||
| 22 | |||
| 23 | #[cfg(feature = "time")] | ||
| 24 | impl LoraTimer { | ||
| 25 | pub fn new() -> Self { | ||
| 26 | Self { start: Instant::now() } | ||
| 27 | } | ||
| 28 | } | ||
| 16 | 29 | ||
| 17 | #[cfg(feature = "time")] | 30 | #[cfg(feature = "time")] |
| 18 | impl lorawan_device::async_device::radio::Timer for LoraTimer { | 31 | impl lorawan_device::async_device::radio::Timer for LoraTimer { |
| 32 | fn reset(&mut self) { | ||
| 33 | self.start = Instant::now(); | ||
| 34 | } | ||
| 35 | |||
| 36 | type AtFuture<'m> = impl core::future::Future<Output = ()> + 'm; | ||
| 37 | fn at<'m>(&'m mut self, millis: u64) -> Self::AtFuture<'m> { | ||
| 38 | Timer::at(self.start + Duration::from_millis(millis)) | ||
| 39 | } | ||
| 40 | |||
| 19 | type DelayFuture<'m> = impl core::future::Future<Output = ()> + 'm; | 41 | type DelayFuture<'m> = impl core::future::Future<Output = ()> + 'm; |
| 20 | fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> { | 42 | fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> { |
| 21 | embassy_time::Timer::after(embassy_time::Duration::from_millis(millis)) | 43 | Timer::after(Duration::from_millis(millis)) |
| 22 | } | 44 | } |
| 23 | } | 45 | } |
diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 7822d0153..4d11244b6 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs | |||
| @@ -1,18 +1,17 @@ | |||
| 1 | //! A radio driver integration for the radio found on STM32WL family devices. | 1 | //! A radio driver integration for the radio found on STM32WL family devices. |
| 2 | use core::future::Future; | 2 | use core::future::Future; |
| 3 | use core::mem::MaybeUninit; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use embassy_hal_common::{into_ref, PeripheralRef}; | 5 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; |
| 6 | use embassy_stm32::dma::NoDma; | 6 | use embassy_stm32::dma::NoDma; |
| 7 | use embassy_stm32::gpio::{AnyPin, Output}; | 7 | use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; |
| 8 | use embassy_stm32::interrupt::{InterruptExt, SUBGHZ_RADIO}; | ||
| 9 | use embassy_stm32::subghz::{ | 8 | use embassy_stm32::subghz::{ |
| 10 | CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, Irq, LoRaBandwidth, LoRaModParams, LoRaPacketParams, | 9 | CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams, |
| 11 | LoRaSyncWord, Ocp, PaConfig, PaSel, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, | 10 | LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, |
| 12 | Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, | 11 | Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, |
| 13 | }; | 12 | }; |
| 14 | use embassy_stm32::Peripheral; | 13 | use embassy_sync::waitqueue::AtomicWaker; |
| 15 | use embassy_sync::signal::Signal; | 14 | use futures::future::poll_fn; |
| 16 | use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; | 15 | use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; |
| 17 | use lorawan_device::async_device::Timings; | 16 | use lorawan_device::async_device::Timings; |
| 18 | 17 | ||
| @@ -28,102 +27,52 @@ pub enum State { | |||
| 28 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 29 | pub struct RadioError; | 28 | pub struct RadioError; |
| 30 | 29 | ||
| 31 | static IRQ: Signal<(Status, u16)> = Signal::new(); | 30 | static IRQ_WAKER: AtomicWaker = AtomicWaker::new(); |
| 32 | 31 | ||
| 33 | struct StateInner<'d> { | 32 | /// The radio peripheral keeping the radio state and owning the radio IRQ. |
| 33 | pub struct SubGhzRadio<'d, RS> { | ||
| 34 | radio: SubGhz<'d, NoDma, NoDma>, | 34 | radio: SubGhz<'d, NoDma, NoDma>, |
| 35 | switch: RadioSwitch<'d>, | 35 | switch: RS, |
| 36 | } | 36 | irq: PeripheralRef<'d, SUBGHZ_RADIO>, |
| 37 | |||
| 38 | /// External state storage for the radio state | ||
| 39 | pub struct SubGhzState<'a>(MaybeUninit<StateInner<'a>>); | ||
| 40 | impl<'d> SubGhzState<'d> { | ||
| 41 | pub const fn new() -> Self { | ||
| 42 | Self(MaybeUninit::uninit()) | ||
| 43 | } | ||
| 44 | } | 37 | } |
| 45 | 38 | ||
| 46 | /// The radio peripheral keeping the radio state and owning the radio IRQ. | 39 | #[derive(Default)] |
| 47 | pub struct SubGhzRadio<'d> { | 40 | #[non_exhaustive] |
| 48 | state: *mut StateInner<'d>, | 41 | pub struct SubGhzRadioConfig { |
| 49 | _irq: PeripheralRef<'d, SUBGHZ_RADIO>, | 42 | pub reg_mode: RegMode, |
| 43 | pub calibrate_image: CalibrateImage, | ||
| 44 | pub pa_config: PaConfig, | ||
| 45 | pub tx_params: TxParams, | ||
| 50 | } | 46 | } |
| 51 | 47 | ||
| 52 | impl<'d> SubGhzRadio<'d> { | 48 | impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { |
| 53 | /// Create a new instance of a SubGhz radio for LoRaWAN. | 49 | /// Create a new instance of a SubGhz radio for LoRaWAN. |
| 54 | /// | 50 | pub fn new( |
| 55 | /// # Safety | 51 | mut radio: SubGhz<'d, NoDma, NoDma>, |
| 56 | /// Do not leak self or futures | 52 | switch: RS, |
| 57 | pub unsafe fn new( | ||
| 58 | state: &'d mut SubGhzState<'d>, | ||
| 59 | radio: SubGhz<'d, NoDma, NoDma>, | ||
| 60 | switch: RadioSwitch<'d>, | ||
| 61 | irq: impl Peripheral<P = SUBGHZ_RADIO> + 'd, | 53 | irq: impl Peripheral<P = SUBGHZ_RADIO> + 'd, |
| 62 | ) -> Self { | 54 | config: SubGhzRadioConfig, |
| 55 | ) -> Result<Self, RadioError> { | ||
| 63 | into_ref!(irq); | 56 | into_ref!(irq); |
| 64 | 57 | ||
| 65 | let mut inner = StateInner { radio, switch }; | 58 | radio.reset(); |
| 66 | inner.radio.reset(); | ||
| 67 | |||
| 68 | let state_ptr = state.0.as_mut_ptr(); | ||
| 69 | state_ptr.write(inner); | ||
| 70 | 59 | ||
| 71 | irq.disable(); | 60 | irq.disable(); |
| 72 | irq.set_handler(|p| { | 61 | irq.set_handler(|_| { |
| 73 | // This is safe because we only get interrupts when configured for, so | 62 | IRQ_WAKER.wake(); |
| 74 | // the radio will be awaiting on the signal at this point. If not, the ISR will | 63 | unsafe { SUBGHZ_RADIO::steal().disable() }; |
| 75 | // anyway only adjust the state in the IRQ signal state. | ||
| 76 | let state = &mut *(p as *mut StateInner<'d>); | ||
| 77 | state.on_interrupt(); | ||
| 78 | }); | 64 | }); |
| 79 | irq.set_handler_context(state_ptr as *mut ()); | ||
| 80 | irq.enable(); | ||
| 81 | |||
| 82 | Self { | ||
| 83 | state: state_ptr, | ||
| 84 | _irq: irq, | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | impl<'d> StateInner<'d> { | ||
| 90 | /// Configure radio settings in preparation for TX or RX | ||
| 91 | pub(crate) fn configure(&mut self) -> Result<(), RadioError> { | ||
| 92 | trace!("Configuring STM32WL SUBGHZ radio"); | ||
| 93 | self.radio.set_standby(StandbyClk::Rc)?; | ||
| 94 | let tcxo_mode = TcxoMode::new() | ||
| 95 | .set_txco_trim(TcxoTrim::Volts1pt7) | ||
| 96 | .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(40))); | ||
| 97 | |||
| 98 | self.radio.set_tcxo_mode(&tcxo_mode)?; | ||
| 99 | self.radio.set_regulator_mode(RegMode::Ldo)?; | ||
| 100 | |||
| 101 | self.radio.calibrate_image(CalibrateImage::ISM_863_870)?; | ||
| 102 | |||
| 103 | self.radio.set_buffer_base_address(0, 0)?; | ||
| 104 | 65 | ||
| 105 | self.radio | 66 | configure_radio(&mut radio, config)?; |
| 106 | .set_pa_config(&PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp))?; | ||
| 107 | |||
| 108 | self.radio.set_pa_ocp(Ocp::Max140m)?; | ||
| 109 | |||
| 110 | // let tx_params = TxParams::LP_14.set_ramp_time(RampTime::Micros40); | ||
| 111 | self.radio | ||
| 112 | .set_tx_params(&TxParams::new().set_ramp_time(RampTime::Micros40).set_power(0x0A))?; | ||
| 113 | 67 | ||
| 114 | self.radio.set_packet_type(PacketType::LoRa)?; | 68 | Ok(Self { radio, switch, irq }) |
| 115 | self.radio.set_lora_sync_word(LoRaSyncWord::Public)?; | ||
| 116 | trace!("Done initializing STM32WL SUBGHZ radio"); | ||
| 117 | Ok(()) | ||
| 118 | } | 69 | } |
| 119 | 70 | ||
| 120 | /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form | 71 | /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form |
| 121 | /// the upcoming RX window start. | 72 | /// the upcoming RX window start. |
| 122 | async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, RadioError> { | 73 | async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, RadioError> { |
| 123 | //trace!("TX Request: {}", config); | 74 | trace!("TX request: {}", config); |
| 124 | trace!("TX START"); | 75 | self.switch.set_tx(); |
| 125 | self.switch.set_tx_lp(); | ||
| 126 | self.configure()?; | ||
| 127 | 76 | ||
| 128 | self.radio | 77 | self.radio |
| 129 | .set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?; | 78 | .set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?; |
| @@ -139,34 +88,26 @@ impl<'d> StateInner<'d> { | |||
| 139 | 88 | ||
| 140 | self.radio.set_lora_packet_params(&packet_params)?; | 89 | self.radio.set_lora_packet_params(&packet_params)?; |
| 141 | 90 | ||
| 142 | let irq_cfg = CfgIrq::new() | 91 | let irq_cfg = CfgIrq::new().irq_enable_all(Irq::TxDone).irq_enable_all(Irq::Timeout); |
| 143 | .irq_enable_all(Irq::TxDone) | ||
| 144 | .irq_enable_all(Irq::RxDone) | ||
| 145 | .irq_enable_all(Irq::Timeout); | ||
| 146 | self.radio.set_irq_cfg(&irq_cfg)?; | 92 | self.radio.set_irq_cfg(&irq_cfg)?; |
| 147 | 93 | ||
| 148 | self.radio.set_buffer_base_address(0, 0)?; | 94 | self.radio.set_buffer_base_address(0, 0)?; |
| 149 | self.radio.write_buffer(0, buf)?; | 95 | self.radio.write_buffer(0, buf)?; |
| 150 | 96 | ||
| 151 | self.radio.set_tx(Timeout::DISABLED)?; | 97 | // The maximum airtime for any LoRaWAN package is 2793.5ms. |
| 98 | // The value of 4000ms is copied from C driver and gives us a good safety margin. | ||
| 99 | self.radio.set_tx(Timeout::from_millis_sat(4000))?; | ||
| 100 | trace!("TX started"); | ||
| 152 | 101 | ||
| 153 | loop { | 102 | loop { |
| 154 | let (_status, irq_status) = IRQ.wait().await; | 103 | let (_status, irq_status) = self.irq_wait().await; |
| 155 | IRQ.reset(); | ||
| 156 | 104 | ||
| 157 | if irq_status & Irq::TxDone.mask() != 0 { | 105 | if irq_status & Irq::TxDone.mask() != 0 { |
| 158 | let stats = self.radio.lora_stats()?; | 106 | trace!("TX done"); |
| 159 | let (status, error_mask) = self.radio.op_error()?; | ||
| 160 | trace!( | ||
| 161 | "TX done. Stats: {:?}. OP error: {:?}, mask {:?}", | ||
| 162 | stats, | ||
| 163 | status, | ||
| 164 | error_mask | ||
| 165 | ); | ||
| 166 | |||
| 167 | return Ok(0); | 107 | return Ok(0); |
| 168 | } else if irq_status & Irq::Timeout.mask() != 0 { | 108 | } |
| 169 | trace!("TX timeout"); | 109 | |
| 110 | if irq_status & Irq::Timeout.mask() != 0 { | ||
| 170 | return Err(RadioError); | 111 | return Err(RadioError); |
| 171 | } | 112 | } |
| 172 | } | 113 | } |
| @@ -174,10 +115,15 @@ impl<'d> StateInner<'d> { | |||
| 174 | 115 | ||
| 175 | fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> { | 116 | fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> { |
| 176 | let mod_params = LoRaModParams::new() | 117 | let mod_params = LoRaModParams::new() |
| 177 | .set_sf(convert_spreading_factor(config.spreading_factor)) | 118 | .set_sf(convert_spreading_factor(&config.spreading_factor)) |
| 178 | .set_bw(convert_bandwidth(config.bandwidth)) | 119 | .set_bw(convert_bandwidth(&config.bandwidth)) |
| 179 | .set_cr(CodingRate::Cr45) | 120 | .set_cr(CodingRate::Cr45) |
| 180 | .set_ldro_en(true); | 121 | .set_ldro_en(matches!( |
| 122 | (config.spreading_factor, config.bandwidth), | ||
| 123 | (SpreadingFactor::_12, Bandwidth::_125KHz) | ||
| 124 | | (SpreadingFactor::_12, Bandwidth::_250KHz) | ||
| 125 | | (SpreadingFactor::_11, Bandwidth::_125KHz) | ||
| 126 | )); | ||
| 181 | self.radio.set_lora_mod_params(&mod_params) | 127 | self.radio.set_lora_mod_params(&mod_params) |
| 182 | } | 128 | } |
| 183 | 129 | ||
| @@ -185,10 +131,8 @@ impl<'d> StateInner<'d> { | |||
| 185 | /// be able to hold a single LoRaWAN packet. | 131 | /// be able to hold a single LoRaWAN packet. |
| 186 | async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> { | 132 | async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> { |
| 187 | assert!(buf.len() >= 255); | 133 | assert!(buf.len() >= 255); |
| 188 | trace!("RX START"); | 134 | trace!("RX request: {}", config); |
| 189 | // trace!("Starting RX: {}", config); | ||
| 190 | self.switch.set_rx(); | 135 | self.switch.set_rx(); |
| 191 | self.configure()?; | ||
| 192 | 136 | ||
| 193 | self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; | 137 | self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; |
| 194 | 138 | ||
| @@ -198,77 +142,110 @@ impl<'d> StateInner<'d> { | |||
| 198 | .set_preamble_len(8) | 142 | .set_preamble_len(8) |
| 199 | .set_header_type(HeaderType::Variable) | 143 | .set_header_type(HeaderType::Variable) |
| 200 | .set_payload_len(0xFF) | 144 | .set_payload_len(0xFF) |
| 201 | .set_crc_en(true) | 145 | .set_crc_en(false) |
| 202 | .set_invert_iq(true); | 146 | .set_invert_iq(true); |
| 203 | self.radio.set_lora_packet_params(&packet_params)?; | 147 | self.radio.set_lora_packet_params(&packet_params)?; |
| 204 | 148 | ||
| 205 | let irq_cfg = CfgIrq::new() | 149 | let irq_cfg = CfgIrq::new() |
| 206 | .irq_enable_all(Irq::RxDone) | 150 | .irq_enable_all(Irq::RxDone) |
| 207 | .irq_enable_all(Irq::PreambleDetected) | 151 | .irq_enable_all(Irq::PreambleDetected) |
| 152 | .irq_enable_all(Irq::HeaderValid) | ||
| 208 | .irq_enable_all(Irq::HeaderErr) | 153 | .irq_enable_all(Irq::HeaderErr) |
| 209 | .irq_enable_all(Irq::Timeout) | 154 | .irq_enable_all(Irq::Err) |
| 210 | .irq_enable_all(Irq::Err); | 155 | .irq_enable_all(Irq::Timeout); |
| 211 | self.radio.set_irq_cfg(&irq_cfg)?; | 156 | self.radio.set_irq_cfg(&irq_cfg)?; |
| 212 | 157 | ||
| 158 | self.radio.set_buffer_base_address(0, 0)?; | ||
| 159 | |||
| 160 | // NOTE: Upper layer handles timeout by cancelling the future | ||
| 213 | self.radio.set_rx(Timeout::DISABLED)?; | 161 | self.radio.set_rx(Timeout::DISABLED)?; |
| 162 | |||
| 214 | trace!("RX started"); | 163 | trace!("RX started"); |
| 215 | 164 | ||
| 216 | loop { | 165 | loop { |
| 217 | let (status, irq_status) = IRQ.wait().await; | 166 | let (_status, irq_status) = self.irq_wait().await; |
| 218 | IRQ.reset(); | ||
| 219 | trace!("RX IRQ {:?}, {:?}", status, irq_status); | ||
| 220 | if irq_status & Irq::RxDone.mask() != 0 { | ||
| 221 | let (status, len, ptr) = self.radio.rx_buffer_status()?; | ||
| 222 | 167 | ||
| 168 | if irq_status & Irq::RxDone.mask() != 0 { | ||
| 169 | let (_status, len, ptr) = self.radio.rx_buffer_status()?; | ||
| 223 | let packet_status = self.radio.lora_packet_status()?; | 170 | let packet_status = self.radio.lora_packet_status()?; |
| 224 | let rssi = packet_status.rssi_pkt().to_integer(); | 171 | let rssi = packet_status.rssi_pkt().to_integer(); |
| 225 | let snr = packet_status.snr_pkt().to_integer(); | 172 | let snr = packet_status.snr_pkt().to_integer(); |
| 226 | trace!( | ||
| 227 | "RX done. Received {} bytes. RX status: {:?}. Pkt status: {:?}", | ||
| 228 | len, | ||
| 229 | status.cmd(), | ||
| 230 | packet_status, | ||
| 231 | ); | ||
| 232 | self.radio.read_buffer(ptr, &mut buf[..len as usize])?; | 173 | self.radio.read_buffer(ptr, &mut buf[..len as usize])?; |
| 233 | self.radio.set_standby(StandbyClk::Rc)?; | 174 | self.radio.set_standby(StandbyClk::Rc)?; |
| 175 | |||
| 176 | trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]); | ||
| 234 | return Ok((len as usize, RxQuality::new(rssi, snr as i8))); | 177 | return Ok((len as usize, RxQuality::new(rssi, snr as i8))); |
| 235 | } else if irq_status & (Irq::Timeout.mask() | Irq::TxDone.mask()) != 0 { | 178 | } |
| 179 | |||
| 180 | if irq_status & Irq::Timeout.mask() != 0 { | ||
| 236 | return Err(RadioError); | 181 | return Err(RadioError); |
| 237 | } | 182 | } |
| 238 | } | 183 | } |
| 239 | } | 184 | } |
| 240 | 185 | ||
| 241 | /// Read interrupt status and store in global signal | 186 | async fn irq_wait(&mut self) -> (Status, u16) { |
| 242 | fn on_interrupt(&mut self) { | 187 | poll_fn(|cx| { |
| 243 | let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); | 188 | self.irq.unpend(); |
| 244 | self.radio | 189 | self.irq.enable(); |
| 245 | .clear_irq_status(irq_status) | 190 | IRQ_WAKER.register(cx.waker()); |
| 246 | .expect("error clearing irq status"); | 191 | |
| 247 | if irq_status & Irq::PreambleDetected.mask() != 0 { | 192 | let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); |
| 248 | trace!("Preamble detected, ignoring"); | 193 | self.radio |
| 249 | } else { | 194 | .clear_irq_status(irq_status) |
| 250 | IRQ.signal((status, irq_status)); | 195 | .expect("error clearing irq status"); |
| 251 | } | 196 | |
| 197 | trace!("SUGHZ IRQ 0b{=u16:b}, {:?}", irq_status, status); | ||
| 198 | |||
| 199 | if irq_status == 0 { | ||
| 200 | Poll::Pending | ||
| 201 | } else { | ||
| 202 | Poll::Ready((status, irq_status)) | ||
| 203 | } | ||
| 204 | }) | ||
| 205 | .await | ||
| 252 | } | 206 | } |
| 253 | } | 207 | } |
| 254 | 208 | ||
| 255 | impl PhyRxTx for SubGhzRadio<'static> { | 209 | fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConfig) -> Result<(), RadioError> { |
| 210 | trace!("Configuring STM32WL SUBGHZ radio"); | ||
| 211 | |||
| 212 | radio.set_regulator_mode(config.reg_mode)?; | ||
| 213 | radio.set_standby(StandbyClk::Rc)?; | ||
| 214 | |||
| 215 | let tcxo_mode = TcxoMode::new() | ||
| 216 | .set_txco_trim(TcxoTrim::Volts1pt7) | ||
| 217 | .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(100))); | ||
| 218 | radio.set_tcxo_mode(&tcxo_mode)?; | ||
| 219 | // Reduce input capacitance as shown in Reference Manual "Figure 23. HSE32 TCXO control". | ||
| 220 | // The STM32CUBE C driver also does this. | ||
| 221 | radio.set_hse_in_trim(HseTrim::MIN)?; | ||
| 222 | |||
| 223 | // Re-calibrate everything after setting the TXCO config. | ||
| 224 | radio.calibrate(0x7F)?; | ||
| 225 | radio.calibrate_image(config.calibrate_image)?; | ||
| 226 | |||
| 227 | radio.set_pa_config(&config.pa_config)?; | ||
| 228 | radio.set_tx_params(&config.tx_params)?; | ||
| 229 | radio.set_pa_ocp(Ocp::Max140m)?; | ||
| 230 | |||
| 231 | radio.set_packet_type(PacketType::LoRa)?; | ||
| 232 | radio.set_lora_sync_word(LoRaSyncWord::Public)?; | ||
| 233 | |||
| 234 | trace!("Done initializing STM32WL SUBGHZ radio"); | ||
| 235 | Ok(()) | ||
| 236 | } | ||
| 237 | |||
| 238 | impl<RS: RadioSwitch> PhyRxTx for SubGhzRadio<'static, RS> { | ||
| 256 | type PhyError = RadioError; | 239 | type PhyError = RadioError; |
| 257 | 240 | ||
| 258 | type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm; | 241 | type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm where RS: 'm; |
| 259 | fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { | 242 | fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { |
| 260 | async move { | 243 | async move { self.do_tx(config, buf).await } |
| 261 | let inner = unsafe { &mut *self.state }; | ||
| 262 | inner.do_tx(config, buf).await | ||
| 263 | } | ||
| 264 | } | 244 | } |
| 265 | 245 | ||
| 266 | type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm; | 246 | type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm where RS: 'm; |
| 267 | fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { | 247 | fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { |
| 268 | async move { | 248 | async move { self.do_rx(config, buf).await } |
| 269 | let inner = unsafe { &mut *self.state }; | ||
| 270 | inner.do_rx(config, buf).await | ||
| 271 | } | ||
| 272 | } | 249 | } |
| 273 | } | 250 | } |
| 274 | 251 | ||
| @@ -278,48 +255,21 @@ impl From<embassy_stm32::spi::Error> for RadioError { | |||
| 278 | } | 255 | } |
| 279 | } | 256 | } |
| 280 | 257 | ||
| 281 | impl<'d> Timings for SubGhzRadio<'d> { | 258 | impl<'d, RS> Timings for SubGhzRadio<'d, RS> { |
| 282 | fn get_rx_window_offset_ms(&self) -> i32 { | 259 | fn get_rx_window_offset_ms(&self) -> i32 { |
| 283 | -200 | 260 | -500 |
| 284 | } | 261 | } |
| 285 | fn get_rx_window_duration_ms(&self) -> u32 { | 262 | fn get_rx_window_duration_ms(&self) -> u32 { |
| 286 | 800 | 263 | 3000 |
| 287 | } | 264 | } |
| 288 | } | 265 | } |
| 289 | 266 | ||
| 290 | /// Represents the radio switch found on STM32WL based boards, used to control the radio for transmission or reception. | 267 | pub trait RadioSwitch { |
| 291 | pub struct RadioSwitch<'d> { | 268 | fn set_rx(&mut self); |
| 292 | ctrl1: Output<'d, AnyPin>, | 269 | fn set_tx(&mut self); |
| 293 | ctrl2: Output<'d, AnyPin>, | ||
| 294 | ctrl3: Output<'d, AnyPin>, | ||
| 295 | } | ||
| 296 | |||
| 297 | impl<'d> RadioSwitch<'d> { | ||
| 298 | pub fn new(ctrl1: Output<'d, AnyPin>, ctrl2: Output<'d, AnyPin>, ctrl3: Output<'d, AnyPin>) -> Self { | ||
| 299 | Self { ctrl1, ctrl2, ctrl3 } | ||
| 300 | } | ||
| 301 | |||
| 302 | pub(crate) fn set_rx(&mut self) { | ||
| 303 | self.ctrl1.set_high(); | ||
| 304 | self.ctrl2.set_low(); | ||
| 305 | self.ctrl3.set_high(); | ||
| 306 | } | ||
| 307 | |||
| 308 | pub(crate) fn set_tx_lp(&mut self) { | ||
| 309 | self.ctrl1.set_high(); | ||
| 310 | self.ctrl2.set_high(); | ||
| 311 | self.ctrl3.set_high(); | ||
| 312 | } | ||
| 313 | |||
| 314 | #[allow(dead_code)] | ||
| 315 | pub(crate) fn set_tx_hp(&mut self) { | ||
| 316 | self.ctrl2.set_high(); | ||
| 317 | self.ctrl1.set_low(); | ||
| 318 | self.ctrl3.set_high(); | ||
| 319 | } | ||
| 320 | } | 270 | } |
| 321 | 271 | ||
| 322 | fn convert_spreading_factor(sf: SpreadingFactor) -> SF { | 272 | fn convert_spreading_factor(sf: &SpreadingFactor) -> SF { |
| 323 | match sf { | 273 | match sf { |
| 324 | SpreadingFactor::_7 => SF::Sf7, | 274 | SpreadingFactor::_7 => SF::Sf7, |
| 325 | SpreadingFactor::_8 => SF::Sf8, | 275 | SpreadingFactor::_8 => SF::Sf8, |
| @@ -330,7 +280,7 @@ fn convert_spreading_factor(sf: SpreadingFactor) -> SF { | |||
| 330 | } | 280 | } |
| 331 | } | 281 | } |
| 332 | 282 | ||
| 333 | fn convert_bandwidth(bw: Bandwidth) -> LoRaBandwidth { | 283 | fn convert_bandwidth(bw: &Bandwidth) -> LoRaBandwidth { |
| 334 | match bw { | 284 | match bw { |
| 335 | Bandwidth::_125KHz => LoRaBandwidth::Bw125, | 285 | Bandwidth::_125KHz => LoRaBandwidth::Bw125, |
| 336 | Bandwidth::_250KHz => LoRaBandwidth::Bw250, | 286 | Bandwidth::_250KHz => LoRaBandwidth::Bw250, |
diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index 69c192c67..347674918 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs | |||
| @@ -202,54 +202,11 @@ impl Default for Config { | |||
| 202 | 202 | ||
| 203 | pub(crate) unsafe fn init(config: Config) { | 203 | pub(crate) unsafe fn init(config: Config) { |
| 204 | let (sys_clk, sw, vos) = match config.mux { | 204 | let (sys_clk, sw, vos) = match config.mux { |
| 205 | ClockSrc::HSI16 => { | 205 | ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Range2), |
| 206 | // Enable HSI16 | 206 | ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Range1), |
| 207 | RCC.cr().write(|w| w.set_hsion(true)); | 207 | ClockSrc::MSI(range) => (range.freq(), 0x00, range.vos()), |
| 208 | while !RCC.cr().read().hsirdy() {} | ||
| 209 | |||
| 210 | (HSI_FREQ.0, 0x01, VoltageScale::Range2) | ||
| 211 | } | ||
| 212 | ClockSrc::HSE32 => { | ||
| 213 | // Enable HSE32 | ||
| 214 | RCC.cr().write(|w| { | ||
| 215 | w.set_hsebyppwr(true); | ||
| 216 | w.set_hseon(true); | ||
| 217 | }); | ||
| 218 | while !RCC.cr().read().hserdy() {} | ||
| 219 | |||
| 220 | (HSE32_FREQ.0, 0x02, VoltageScale::Range1) | ||
| 221 | } | ||
| 222 | ClockSrc::MSI(range) => { | ||
| 223 | RCC.cr().write(|w| { | ||
| 224 | w.set_msirange(range.into()); | ||
| 225 | w.set_msion(true); | ||
| 226 | }); | ||
| 227 | |||
| 228 | while !RCC.cr().read().msirdy() {} | ||
| 229 | |||
| 230 | (range.freq(), 0x00, range.vos()) | ||
| 231 | } | ||
| 232 | }; | 208 | }; |
| 233 | 209 | ||
| 234 | RCC.cfgr().modify(|w| { | ||
| 235 | w.set_sw(sw.into()); | ||
| 236 | if config.ahb_pre == AHBPrescaler::NotDivided { | ||
| 237 | w.set_hpre(0); | ||
| 238 | } else { | ||
| 239 | w.set_hpre(config.ahb_pre.into()); | ||
| 240 | } | ||
| 241 | w.set_ppre1(config.apb1_pre.into()); | ||
| 242 | w.set_ppre2(config.apb2_pre.into()); | ||
| 243 | }); | ||
| 244 | |||
| 245 | RCC.extcfgr().modify(|w| { | ||
| 246 | if config.shd_ahb_pre == AHBPrescaler::NotDivided { | ||
| 247 | w.set_shdhpre(0); | ||
| 248 | } else { | ||
| 249 | w.set_shdhpre(config.shd_ahb_pre.into()); | ||
| 250 | } | ||
| 251 | }); | ||
| 252 | |||
| 253 | let ahb_freq: u32 = match config.ahb_pre { | 210 | let ahb_freq: u32 = match config.ahb_pre { |
| 254 | AHBPrescaler::NotDivided => sys_clk, | 211 | AHBPrescaler::NotDivided => sys_clk, |
| 255 | pre => { | 212 | pre => { |
| @@ -288,16 +245,6 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 288 | } | 245 | } |
| 289 | }; | 246 | }; |
| 290 | 247 | ||
| 291 | let apb3_freq = shd_ahb_freq; | ||
| 292 | |||
| 293 | if config.enable_lsi { | ||
| 294 | let csr = RCC.csr().read(); | ||
| 295 | if !csr.lsion() { | ||
| 296 | RCC.csr().modify(|w| w.set_lsion(true)); | ||
| 297 | while !RCC.csr().read().lsirdy() {} | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | // Adjust flash latency | 248 | // Adjust flash latency |
| 302 | let flash_clk_src_freq: u32 = shd_ahb_freq; | 249 | let flash_clk_src_freq: u32 = shd_ahb_freq; |
| 303 | let ws = match vos { | 250 | let ws = match vos { |
| @@ -319,6 +266,61 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 319 | 266 | ||
| 320 | while FLASH.acr().read().latency() != ws {} | 267 | while FLASH.acr().read().latency() != ws {} |
| 321 | 268 | ||
| 269 | match config.mux { | ||
| 270 | ClockSrc::HSI16 => { | ||
| 271 | // Enable HSI16 | ||
| 272 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 273 | while !RCC.cr().read().hsirdy() {} | ||
| 274 | } | ||
| 275 | ClockSrc::HSE32 => { | ||
| 276 | // Enable HSE32 | ||
| 277 | RCC.cr().write(|w| { | ||
| 278 | w.set_hsebyppwr(true); | ||
| 279 | w.set_hseon(true); | ||
| 280 | }); | ||
| 281 | while !RCC.cr().read().hserdy() {} | ||
| 282 | } | ||
| 283 | ClockSrc::MSI(range) => { | ||
| 284 | let cr = RCC.cr().read(); | ||
| 285 | assert!(!cr.msion() || cr.msirdy()); | ||
| 286 | RCC.cr().write(|w| { | ||
| 287 | w.set_msirgsel(true); | ||
| 288 | w.set_msirange(range.into()); | ||
| 289 | w.set_msion(true); | ||
| 290 | }); | ||
| 291 | while !RCC.cr().read().msirdy() {} | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | RCC.extcfgr().modify(|w| { | ||
| 296 | if config.shd_ahb_pre == AHBPrescaler::NotDivided { | ||
| 297 | w.set_shdhpre(0); | ||
| 298 | } else { | ||
| 299 | w.set_shdhpre(config.shd_ahb_pre.into()); | ||
| 300 | } | ||
| 301 | }); | ||
| 302 | |||
| 303 | RCC.cfgr().modify(|w| { | ||
| 304 | w.set_sw(sw.into()); | ||
| 305 | if config.ahb_pre == AHBPrescaler::NotDivided { | ||
| 306 | w.set_hpre(0); | ||
| 307 | } else { | ||
| 308 | w.set_hpre(config.ahb_pre.into()); | ||
| 309 | } | ||
| 310 | w.set_ppre1(config.apb1_pre.into()); | ||
| 311 | w.set_ppre2(config.apb2_pre.into()); | ||
| 312 | }); | ||
| 313 | |||
| 314 | // TODO: switch voltage range | ||
| 315 | |||
| 316 | if config.enable_lsi { | ||
| 317 | let csr = RCC.csr().read(); | ||
| 318 | if !csr.lsion() { | ||
| 319 | RCC.csr().modify(|w| w.set_lsion(true)); | ||
| 320 | while !RCC.csr().read().lsirdy() {} | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 322 | set_freqs(Clocks { | 324 | set_freqs(Clocks { |
| 323 | sys: Hertz(sys_clk), | 325 | sys: Hertz(sys_clk), |
| 324 | ahb1: Hertz(ahb_freq), | 326 | ahb1: Hertz(ahb_freq), |
| @@ -326,7 +328,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 326 | ahb3: Hertz(shd_ahb_freq), | 328 | ahb3: Hertz(shd_ahb_freq), |
| 327 | apb1: Hertz(apb1_freq), | 329 | apb1: Hertz(apb1_freq), |
| 328 | apb2: Hertz(apb2_freq), | 330 | apb2: Hertz(apb2_freq), |
| 329 | apb3: Hertz(apb3_freq), | 331 | apb3: Hertz(shd_ahb_freq), |
| 330 | apb1_tim: Hertz(apb1_tim_freq), | 332 | apb1_tim: Hertz(apb1_tim_freq), |
| 331 | apb2_tim: Hertz(apb2_tim_freq), | 333 | apb2_tim: Hertz(apb2_tim_freq), |
| 332 | }); | 334 | }); |
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index acc29d87e..02e6020b0 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> { | |||
| 179 | ) | 179 | ) |
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | /// Useful for on chip peripherals like SUBGHZ which are hardwired. | ||
| 183 | /// The bus can optionally be exposed externally with `Spi::new()` still. | ||
| 184 | #[allow(dead_code)] | ||
| 185 | pub(crate) fn new_internal( | ||
| 186 | peri: impl Peripheral<P = T> + 'd, | ||
| 187 | txdma: impl Peripheral<P = Tx> + 'd, | ||
| 188 | rxdma: impl Peripheral<P = Rx> + 'd, | ||
| 189 | freq: Hertz, | ||
| 190 | config: Config, | ||
| 191 | ) -> Self { | ||
| 192 | Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) | ||
| 193 | } | ||
| 194 | |||
| 182 | fn new_inner( | 195 | fn new_inner( |
| 183 | peri: impl Peripheral<P = T> + 'd, | 196 | peri: impl Peripheral<P = T> + 'd, |
| 184 | sck: Option<PeripheralRef<'d, AnyPin>>, | 197 | sck: Option<PeripheralRef<'d, AnyPin>>, |
diff --git a/embassy-stm32/src/subghz/mod.rs b/embassy-stm32/src/subghz/mod.rs index a74f9a6d5..33398fa1d 100644 --- a/embassy-stm32/src/subghz/mod.rs +++ b/embassy-stm32/src/subghz/mod.rs | |||
| @@ -81,7 +81,7 @@ pub use value_error::ValueError; | |||
| 81 | use crate::dma::NoDma; | 81 | use crate::dma::NoDma; |
| 82 | use crate::peripherals::SUBGHZSPI; | 82 | use crate::peripherals::SUBGHZSPI; |
| 83 | use crate::rcc::sealed::RccPeripheral; | 83 | use crate::rcc::sealed::RccPeripheral; |
| 84 | use crate::spi::{BitOrder, Config as SpiConfig, MisoPin, MosiPin, SckPin, Spi, MODE_0}; | 84 | use crate::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; |
| 85 | use crate::time::Hertz; | 85 | use crate::time::Hertz; |
| 86 | use crate::{pac, Peripheral}; | 86 | use crate::{pac, Peripheral}; |
| 87 | 87 | ||
| @@ -212,9 +212,6 @@ impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> { | |||
| 212 | /// clock. | 212 | /// clock. |
| 213 | pub fn new( | 213 | pub fn new( |
| 214 | peri: impl Peripheral<P = SUBGHZSPI> + 'd, | 214 | peri: impl Peripheral<P = SUBGHZSPI> + 'd, |
| 215 | sck: impl Peripheral<P = impl SckPin<SUBGHZSPI>> + 'd, | ||
| 216 | mosi: impl Peripheral<P = impl MosiPin<SUBGHZSPI>> + 'd, | ||
| 217 | miso: impl Peripheral<P = impl MisoPin<SUBGHZSPI>> + 'd, | ||
| 218 | txdma: impl Peripheral<P = Tx> + 'd, | 215 | txdma: impl Peripheral<P = Tx> + 'd, |
| 219 | rxdma: impl Peripheral<P = Rx> + 'd, | 216 | rxdma: impl Peripheral<P = Rx> + 'd, |
| 220 | ) -> Self { | 217 | ) -> Self { |
| @@ -227,7 +224,7 @@ impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> { | |||
| 227 | let mut config = SpiConfig::default(); | 224 | let mut config = SpiConfig::default(); |
| 228 | config.mode = MODE_0; | 225 | config.mode = MODE_0; |
| 229 | config.bit_order = BitOrder::MsbFirst; | 226 | config.bit_order = BitOrder::MsbFirst; |
| 230 | let spi = Spi::new(peri, sck, mosi, miso, txdma, rxdma, clk, config); | 227 | let spi = Spi::new_internal(peri, txdma, rxdma, clk, config); |
| 231 | 228 | ||
| 232 | unsafe { wakeup() }; | 229 | unsafe { wakeup() }; |
| 233 | 230 | ||
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 11751a21d..6358fe865 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 | |||
| 14 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } | 14 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } |
| 15 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} | 15 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} |
| 16 | 16 | ||
| 17 | lorawan-device = { version = "0.7.1", default-features = false, features = ["async"], optional = true } | 17 | lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } |
| 18 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } | 18 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } |
| 19 | 19 | ||
| 20 | defmt = "0.3" | 20 | defmt = "0.3" |
diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index 303558b96..00ff67f3f 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs | |||
| @@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) { | |||
| 47 | let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap(); | 47 | let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap(); |
| 48 | 48 | ||
| 49 | let region = region::EU868::default().into(); | 49 | let region = region::EU868::default().into(); |
| 50 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer, Rng::new(p.RNG)); | 50 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); |
| 51 | 51 | ||
| 52 | defmt::info!("Joining LoRaWAN network"); | 52 | defmt::info!("Joining LoRaWAN network"); |
| 53 | 53 | ||
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 5f6679f4b..e2e7d4078 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 | |||
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } | 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } |
| 11 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } | 11 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } |
| 12 | 12 | ||
| 13 | lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] } | 13 | lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } |
| 14 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } | 14 | lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } |
| 15 | 15 | ||
| 16 | defmt = "0.3" | 16 | defmt = "0.3" |
diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 7e8a8946d..9143e64da 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs | |||
| @@ -9,7 +9,7 @@ use embassy_executor::Spawner; | |||
| 9 | use embassy_lora::stm32wl::*; | 9 | use embassy_lora::stm32wl::*; |
| 10 | use embassy_lora::LoraTimer; | 10 | use embassy_lora::LoraTimer; |
| 11 | use embassy_stm32::dma::NoDma; | 11 | use embassy_stm32::dma::NoDma; |
| 12 | use embassy_stm32::gpio::{Level, Output, Pin, Speed}; | 12 | use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; |
| 13 | use embassy_stm32::rng::Rng; | 13 | use embassy_stm32::rng::Rng; |
| 14 | use embassy_stm32::subghz::*; | 14 | use embassy_stm32::subghz::*; |
| 15 | use embassy_stm32::{interrupt, pac}; | 15 | use embassy_stm32::{interrupt, pac}; |
| @@ -17,6 +17,32 @@ use lorawan::default_crypto::DefaultFactory as Crypto; | |||
| 17 | use lorawan_device::async_device::{region, Device, JoinMode}; | 17 | use lorawan_device::async_device::{region, Device, JoinMode}; |
| 18 | use {defmt_rtt as _, panic_probe as _}; | 18 | use {defmt_rtt as _, panic_probe as _}; |
| 19 | 19 | ||
| 20 | struct RadioSwitch<'a> { | ||
| 21 | ctrl1: Output<'a, AnyPin>, | ||
| 22 | ctrl2: Output<'a, AnyPin>, | ||
| 23 | ctrl3: Output<'a, AnyPin>, | ||
| 24 | } | ||
| 25 | |||
| 26 | impl<'a> RadioSwitch<'a> { | ||
| 27 | fn new(ctrl1: Output<'a, AnyPin>, ctrl2: Output<'a, AnyPin>, ctrl3: Output<'a, AnyPin>) -> Self { | ||
| 28 | Self { ctrl1, ctrl2, ctrl3 } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | impl<'a> embassy_lora::stm32wl::RadioSwitch for RadioSwitch<'a> { | ||
| 33 | fn set_rx(&mut self) { | ||
| 34 | self.ctrl1.set_high(); | ||
| 35 | self.ctrl2.set_low(); | ||
| 36 | self.ctrl3.set_high(); | ||
| 37 | } | ||
| 38 | |||
| 39 | fn set_tx(&mut self) { | ||
| 40 | self.ctrl1.set_high(); | ||
| 41 | self.ctrl2.set_high(); | ||
| 42 | self.ctrl3.set_high(); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 20 | #[embassy_executor::main] | 46 | #[embassy_executor::main] |
| 21 | async fn main(_spawner: Spawner) { | 47 | async fn main(_spawner: Spawner) { |
| 22 | let mut config = embassy_stm32::Config::default(); | 48 | let mut config = embassy_stm32::Config::default(); |
| @@ -31,18 +57,19 @@ async fn main(_spawner: Spawner) { | |||
| 31 | let ctrl3 = Output::new(p.PC5.degrade(), Level::High, Speed::High); | 57 | let ctrl3 = Output::new(p.PC5.degrade(), Level::High, Speed::High); |
| 32 | let rfs = RadioSwitch::new(ctrl1, ctrl2, ctrl3); | 58 | let rfs = RadioSwitch::new(ctrl1, ctrl2, ctrl3); |
| 33 | 59 | ||
| 34 | let radio = SubGhz::new(p.SUBGHZSPI, p.PA5, p.PA7, p.PA6, NoDma, NoDma); | 60 | let radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); |
| 35 | |||
| 36 | let irq = interrupt::take!(SUBGHZ_RADIO); | 61 | let irq = interrupt::take!(SUBGHZ_RADIO); |
| 37 | static mut RADIO_STATE: SubGhzState<'static> = SubGhzState::new(); | 62 | |
| 38 | let radio = unsafe { SubGhzRadio::new(&mut RADIO_STATE, radio, rfs, irq) }; | 63 | let mut radio_config = SubGhzRadioConfig::default(); |
| 64 | radio_config.calibrate_image = CalibrateImage::ISM_863_870; | ||
| 65 | let radio = SubGhzRadio::new(radio, rfs, irq, radio_config).unwrap(); | ||
| 39 | 66 | ||
| 40 | let mut region: region::Configuration = region::EU868::default().into(); | 67 | let mut region: region::Configuration = region::EU868::default().into(); |
| 41 | 68 | ||
| 42 | // NOTE: This is specific for TTN, as they have a special RX1 delay | 69 | // NOTE: This is specific for TTN, as they have a special RX1 delay |
| 43 | region.set_receive_delay1(5000); | 70 | region.set_receive_delay1(5000); |
| 44 | 71 | ||
| 45 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer, Rng::new(p.RNG)); | 72 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); |
| 46 | 73 | ||
| 47 | // Depending on network, this might be part of JOIN | 74 | // Depending on network, this might be part of JOIN |
| 48 | device.set_datarate(region::DR::_0); // SF12 | 75 | device.set_datarate(region::DR::_0); // SF12 |
diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index c5e9bb597..8f674d796 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs | |||
| @@ -72,7 +72,7 @@ async fn main(_spawner: Spawner) { | |||
| 72 | unsafe { interrupt::SUBGHZ_RADIO::steal() }.disable(); | 72 | unsafe { interrupt::SUBGHZ_RADIO::steal() }.disable(); |
| 73 | }); | 73 | }); |
| 74 | 74 | ||
| 75 | let mut radio = SubGhz::new(p.SUBGHZSPI, p.PA5, p.PA7, p.PA6, NoDma, NoDma); | 75 | let mut radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); |
| 76 | 76 | ||
| 77 | defmt::info!("Radio ready for use"); | 77 | defmt::info!("Radio ready for use"); |
| 78 | 78 | ||
