diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index 5d3cf658a6..2e20ee8ba9 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -66,15 +66,15 @@ where let mut bus = self.bus.lock().await; self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let op_res: Result<(), BUS::Error> = try { + let op_res: Result<(), Self::Error> = try { for op in operations { match op { - Operation::Read(buf) => bus.read(buf).await?, - Operation::Write(buf) => bus.write(buf).await?, - Operation::Transfer(read, write) => bus.transfer(read, write).await?, - Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?, + Operation::Read(buf) => bus.read(buf).await.map_err(SpiDeviceError::Spi)?, + Operation::Write(buf) => bus.write(buf).await.map_err(SpiDeviceError::Spi)?, + Operation::Transfer(read, write) => bus.transfer(read, write).await.map_err(SpiDeviceError::Spi)?, + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await.map_err(SpiDeviceError::Spi)?, #[cfg(not(feature = "time"))] - Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported), + Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported)?, #[cfg(feature = "time")] Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await, } @@ -85,11 +85,10 @@ where let flush_res = bus.flush().await; let cs_res = self.cs.set_high(); - let op_res = op_res.map_err(SpiDeviceError::Spi)?; flush_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(op_res) + op_res } } diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 846c391991..b728f3a7d7 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -29,3 +29,6 @@ embedded-hal = { version = "0.2", features = ["unproven"] } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } lora-phy = { version = "2" } lorawan-device = { version = "0.11.0", default-features = false, features = ["async"], optional = true } + +[patch.crates-io] +lora-phy = { git = "https://github.com/CBJamo/lora-phy.git", branch = "SpiDevice" } diff --git a/embassy-lora/src/iv.rs b/embassy-lora/src/iv.rs index d22beb3370..84193413b2 100644 --- a/embassy-lora/src/iv.rs +++ b/embassy-lora/src/iv.rs @@ -66,14 +66,6 @@ where fn set_board_type(&mut self, board_type: BoardType) { self.board_type = board_type; } - async fn set_nss_low(&mut self) -> Result<(), RadioError> { - pac::PWR.subghzspicr().modify(|w| w.set_nss(false)); - Ok(()) - } - async fn set_nss_high(&mut self) -> Result<(), RadioError> { - pac::PWR.subghzspicr().modify(|w| w.set_nss(true)); - Ok(()) - } async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> { pac::RCC.csr().modify(|w| w.set_rfrst(true)); pac::RCC.csr().modify(|w| w.set_rfrst(false)); @@ -125,7 +117,6 @@ where /// Base for the InterfaceVariant implementation for an stm32l0/sx1276 combination pub struct Stm32l0InterfaceVariant { board_type: BoardType, - nss: CTRL, reset: CTRL, irq: WAIT, rf_switch_rx: Option, @@ -139,7 +130,6 @@ where { /// Create an InterfaceVariant instance for an stm32l0/sx1276 combination pub fn new( - nss: CTRL, reset: CTRL, irq: WAIT, rf_switch_rx: Option, @@ -147,7 +137,6 @@ where ) -> Result { Ok(Self { board_type: BoardType::Stm32l0Sx1276, // updated when associated with a specific LoRa board - nss, reset, irq, rf_switch_rx, @@ -164,12 +153,6 @@ where fn set_board_type(&mut self, board_type: BoardType) { self.board_type = board_type; } - async fn set_nss_low(&mut self) -> Result<(), RadioError> { - self.nss.set_low().map_err(|_| NSS) - } - async fn set_nss_high(&mut self) -> Result<(), RadioError> { - self.nss.set_high().map_err(|_| NSS) - } async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> { delay.delay_ms(10).await; self.reset.set_low().map_err(|_| Reset)?; @@ -220,7 +203,6 @@ where /// Base for the InterfaceVariant implementation for a generic Sx126x LoRa board pub struct GenericSx126xInterfaceVariant { board_type: BoardType, - nss: CTRL, reset: CTRL, dio1: WAIT, busy: WAIT, @@ -235,7 +217,6 @@ where { /// Create an InterfaceVariant instance for an nrf52840/sx1262 combination pub fn new( - nss: CTRL, reset: CTRL, dio1: WAIT, busy: WAIT, @@ -244,7 +225,6 @@ where ) -> Result { Ok(Self { board_type: BoardType::Rak4631Sx1262, // updated when associated with a specific LoRa board - nss, reset, dio1, busy, @@ -262,12 +242,6 @@ where fn set_board_type(&mut self, board_type: BoardType) { self.board_type = board_type; } - async fn set_nss_low(&mut self) -> Result<(), RadioError> { - self.nss.set_low().map_err(|_| NSS) - } - async fn set_nss_high(&mut self) -> Result<(), RadioError> { - self.nss.set_high().map_err(|_| NSS) - } async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> { delay.delay_ms(10).await; self.reset.set_low().map_err(|_| Reset)?; diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 5637802bb7..7c718d062d 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![feature(async_fn_in_trait)] +#![cfg_attr(feature = "stm32wl", feature(try_blocks))] //! embassy-lora holds LoRa-specific functionality. pub(crate) mod fmt; @@ -7,6 +8,9 @@ pub(crate) mod fmt; /// interface variants required by the external lora physical layer crate (lora-phy) pub mod iv; +#[cfg(feature = "stm32wl")] +pub mod stm32wl; + #[cfg(feature = "time")] use embassy_time::{Duration, Instant, Timer}; diff --git a/embassy-lora/src/stm32wl.rs b/embassy-lora/src/stm32wl.rs new file mode 100644 index 0000000000..74e4b4d399 --- /dev/null +++ b/embassy-lora/src/stm32wl.rs @@ -0,0 +1,89 @@ +use core::fmt::Debug; + +use embassy_stm32::peripherals::SUBGHZSPI; +use embassy_stm32::{pac, spi as stm_spi, Peripheral}; +use embedded_hal_async::spi::{self, Operation}; + +pub struct SubGhzSpiDevice<'d, Tx, Rx> +where + Tx: stm_spi::TxDma, + Rx: stm_spi::RxDma, +{ + bus: stm_spi::Spi<'d, SUBGHZSPI, Tx, Rx>, +} + +impl<'d, Tx, Rx> SubGhzSpiDevice<'d, Tx, Rx> +where + Tx: stm_spi::TxDma, + Rx: stm_spi::RxDma, +{ + pub fn new( + spi: impl Peripheral

+ 'd, + txdma: impl Peripheral

+ 'd, + rxdma: impl Peripheral

+ 'd, + ) -> Self { + let bus = stm_spi::Spi::new_subghz(spi, txdma, rxdma); + Self { bus } + } +} + +/// Error returned by SPI device implementations in this crate. +#[derive(Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum SpiDeviceError { + /// An operation on the inner SPI bus failed. + Spi(stm_spi::Error), + /// DelayUs operations are not supported when the `time` Cargo feature is not enabled. + DelayUsNotSupported, +} + +impl spi::Error for SpiDeviceError { + fn kind(&self) -> spi::ErrorKind { + match self { + Self::Spi(e) => e.kind(), + Self::DelayUsNotSupported => spi::ErrorKind::Other, + } + } +} + +impl spi::ErrorType for SubGhzSpiDevice<'_, Tx, Rx> +where + Tx: stm_spi::TxDma, + Rx: stm_spi::RxDma, +{ + type Error = SpiDeviceError; +} + +impl spi::SpiDevice for SubGhzSpiDevice<'_, Tx, Rx> +where + Tx: stm_spi::TxDma, + Rx: stm_spi::RxDma, +{ + async fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { + pac::PWR.subghzspicr().modify(|w| w.set_nss(false)); + + let op_res: Result<(), Self::Error> = try { + for op in operations { + match op { + Operation::Read(buf) => self.bus.read(buf).await.map_err(SpiDeviceError::Spi)?, + Operation::Write(buf) => self.bus.write(buf).await.map_err(SpiDeviceError::Spi)?, + Operation::Transfer(read, write) => { + self.bus.transfer(read, write).await.map_err(SpiDeviceError::Spi)? + } + Operation::TransferInPlace(buf) => { + self.bus.transfer_in_place(buf).await.map_err(SpiDeviceError::Spi)? + } + #[cfg(not(feature = "time"))] + Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported)?, + #[cfg(feature = "time")] + Operation::DelayUs(us) => embassy_time::Timer::after_micros(*us as _).await, + } + } + }; + + pac::PWR.subghzspicr().modify(|w| w.set_nss(true)); + + op_res + } +} diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 6a338af408..bc0dbbd618 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -30,3 +30,6 @@ chrono = { version = "^0.4", default-features = false } [profile.release] debug = 2 + +[patch.crates-io] +lora-phy = { git = "https://github.com/CBJamo/lora-phy.git", branch = "SpiDevice" } diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs index 35a6a8425c..5e802b955a 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -8,10 +8,10 @@ use defmt::info; use embassy_executor::Spawner; use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; +use embassy_lora::stm32wl::SubGhzSpiDevice; use embassy_lora::LoraTimer; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; use embassy_stm32::rng::{self, Rng}; -use embassy_stm32::spi::Spi; use embassy_stm32::time::Hertz; use embassy_stm32::{bind_interrupts, peripherals}; use embassy_time::Delay; @@ -53,7 +53,7 @@ async fn main(_spawner: Spawner) { } let p = embassy_stm32::init(config); - let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); + let spi = SubGhzSpiDevice::new(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); diff --git a/examples/stm32wl/src/bin/lora_p2p_receive.rs b/examples/stm32wl/src/bin/lora_p2p_receive.rs index 1c485d7390..a4a6345f52 100644 --- a/examples/stm32wl/src/bin/lora_p2p_receive.rs +++ b/examples/stm32wl/src/bin/lora_p2p_receive.rs @@ -8,9 +8,9 @@ use defmt::info; use embassy_executor::Spawner; use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; +use embassy_lora::stm32wl::SubGhzSpiDevice; use embassy_stm32::bind_interrupts; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; -use embassy_stm32::spi::Spi; use embassy_stm32::time::Hertz; use embassy_time::{Delay, Timer}; use lora_phy::mod_params::*; @@ -46,7 +46,7 @@ async fn main(_spawner: Spawner) { } let p = embassy_stm32::init(config); - let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); + let spi = SubGhzSpiDevice::new(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); diff --git a/examples/stm32wl/src/bin/lora_p2p_send.rs b/examples/stm32wl/src/bin/lora_p2p_send.rs index 3afa78acb8..846a3db40b 100644 --- a/examples/stm32wl/src/bin/lora_p2p_send.rs +++ b/examples/stm32wl/src/bin/lora_p2p_send.rs @@ -8,9 +8,9 @@ use defmt::info; use embassy_executor::Spawner; use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; +use embassy_lora::stm32wl::SubGhzSpiDevice; use embassy_stm32::bind_interrupts; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; -use embassy_stm32::spi::Spi; use embassy_stm32::time::Hertz; use embassy_time::Delay; use lora_phy::mod_params::*; @@ -46,7 +46,7 @@ async fn main(_spawner: Spawner) { } let p = embassy_stm32::init(config); - let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); + let spi = SubGhzSpiDevice::new(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High);