diff --git a/README.md b/README.md index 7ae40fe59..714104331 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,7 @@ The embassy-net network stac The nrf-softdevice crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. The embassy-stm32-wpan crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers. -- **LoRa** - -The lora-rs project provides an async LoRa and LoRaWAN stack that works well on Embassy. +- **LoRa** - The lora-rs project provides an async LoRa and LoRaWAN stack that works well on Embassy. - **USB** - embassy-usb implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. diff --git a/ci.sh b/ci.sh index 933e54a80..75640e384 100755 --- a/ci.sh +++ b/ci.sh @@ -207,6 +207,9 @@ cargo batch \ $BUILD_EXTRA +# failed, a wire must've come loose, will check asap. +rm out/tests/rpi-pico/i2c + rm out/tests/stm32wb55rg/wpan_mac rm out/tests/stm32wb55rg/wpan_ble diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 6fba80eda..e17adbbd7 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -41,7 +41,7 @@ The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implem The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. === LoRa -link:https://github.com/embassy-rs/lora-phy[lora-phy] and link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust link:https://github.com/ivajloip/rust-lorawan[LoRaWAN] implementation. +link:https://github.com/lora-rs/lora-rs[lora-rs] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust LoRaWAN implementation. It provides four crates — lora-phy, lora-modulation, lorawan-encoding, and lorawan-device — and basic examples for various development boards. It has support for STM32WL wireless microcontrollers or Semtech SX127x transceivers, among others. === USB link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 5161e1020..1efb2788b 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -49,7 +49,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result Result match t.pat.as_mut() { syn::Pat::Ident(id) => { - arg_names.push(id.ident.clone()); id.mutability = None; + args.push((id.clone(), t.attrs.clone())); } _ => { ctxt.error_spanned_by(arg, "pattern matching in task arguments is not yet supported"); @@ -79,13 +79,24 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result ::embassy_executor::SpawnToken { type Fut = impl ::core::future::Future + 'static; const POOL_SIZE: usize = #pool_size; static POOL: ::embassy_executor::raw::TaskPool = ::embassy_executor::raw::TaskPool::new(); - unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } + unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } } }; #[cfg(not(feature = "nightly"))] @@ -93,7 +104,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result ::embassy_executor::SpawnToken { const POOL_SIZE: usize = #pool_size; static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new(); - unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } + unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } } }; diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 0dbd391e8..2c2441dd5 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -135,3 +135,17 @@ fn executor_task_self_wake_twice() { ] ) } + +#[test] +fn executor_task_cfg_args() { + // simulate cfg'ing away argument c + #[task] + async fn task1(a: u32, b: u32, #[cfg(any())] c: u32) { + let (_, _) = (a, b); + } + + #[task] + async fn task2(a: u32, b: u32, #[cfg(all())] c: u32) { + let (_, _, _) = (a, b, c); + } +} diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index d9df5b29d..a22cd8827 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -222,6 +222,11 @@ impl<'a> UdpSocket<'a> { pub fn payload_send_capacity(&self) -> usize { self.with(|s, _| s.payload_send_capacity()) } + + /// Set the hop limit field in the IP header of sent packets. + pub fn set_hop_limit(&mut self, hop_limit: Option) { + self.with_mut(|s, _| s.set_hop_limit(hop_limit)) + } } impl Drop for UdpSocket<'_> { diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 4ac622d34..2c620798d 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -342,6 +342,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { s.tx_count.store(0, Ordering::Relaxed); s.rx_started_count.store(0, Ordering::Relaxed); s.rx_ended_count.store(0, Ordering::Relaxed); + s.rx_started.store(false, Ordering::Relaxed); let len = tx_buffer.len(); unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; let len = rx_buffer.len(); diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index a47fb8350..27c18383f 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -152,6 +152,12 @@ impl<'d, T: Pin> Output<'d, T> { self.pin.set_low() } + /// Toggle the output level. + #[inline] + pub fn toggle(&mut self) { + self.pin.toggle() + } + /// Set the output level. #[inline] pub fn set_level(&mut self, level: Level) { @@ -310,6 +316,16 @@ impl<'d, T: Pin> Flex<'d, T> { self.pin.set_low() } + /// Toggle the output level. + #[inline] + pub fn toggle(&mut self) { + if self.is_set_low() { + self.set_high() + } else { + self.set_low() + } + } + /// Set the output level. #[inline] pub fn set_level(&mut self, level: Level) { @@ -538,6 +554,15 @@ mod eh02 { } } + impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'d, T> { + type Error = Infallible; + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } + } + /// Implement [`embedded_hal_02::digital::v2::InputPin`] for [`Flex`]; /// /// If the pin is not in input mode the result is unspecified. @@ -574,6 +599,15 @@ mod eh02 { Ok(self.ref_is_set_low()) } } + + impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d, T> { + type Error = Infallible; + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } + } } impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Input<'d, T> { diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index df5b44c2d..cbbc201de 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -1,3 +1,9 @@ +//! # I2Cv1 +//! +//! This implementation is used for STM32F1, STM32F2, STM32F4, and STM32L1 devices. +//! +//! All other devices (as of 2023-12-28) use [`v2`](super::v2) instead. + use core::future::poll_fn; use core::task::Poll; @@ -10,6 +16,17 @@ use crate::dma::Transfer; use crate::pac::i2c; use crate::time::Hertz; +// /!\ /!\ +// /!\ Implementation note! /!\ +// /!\ /!\ +// +// It's somewhat unclear whether using interrupts here in a *strictly* one-shot style is actually +// what we want! If you are looking in this file because you are doing async I2C and your code is +// just totally hanging (sometimes), maybe swing by this issue: +// . +// +// There's some more details there, and we might have a fix for you. But please let us know if you +// hit a case like this! pub unsafe fn on_interrupt() { let regs = T::regs(); // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of @@ -375,6 +392,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::regs().sr2().read(); Poll::Ready(Ok(())) } else { + // If we need to go around, then re-enable the interrupts, otherwise nothing + // can wake us up and we'll hang. + Self::enable_interrupts(); Poll::Pending } } diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 207f7ed8f..f10e9d50d 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -208,32 +208,30 @@ pub fn init(config: Config) -> Peripherals { let p = Peripherals::take_with_cs(cs); #[cfg(dbgmcu)] - if config.enable_debug_during_sleep { - crate::pac::DBGMCU.cr().modify(|cr| { - #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5, dbgmcu_wba))] - { - cr.set_dbg_stop(true); - cr.set_dbg_standby(true); - } - #[cfg(any( - dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1, - dbgmcu_l4, dbgmcu_wb, dbgmcu_wl - ))] - { - cr.set_dbg_sleep(true); - cr.set_dbg_stop(true); - cr.set_dbg_standby(true); - } - #[cfg(dbgmcu_h7)] - { - cr.set_d1dbgcken(true); - cr.set_d3dbgcken(true); - cr.set_dbgsleep_d1(true); - cr.set_dbgstby_d1(true); - cr.set_dbgstop_d1(true); - } - }); - } + crate::pac::DBGMCU.cr().modify(|cr| { + #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5, dbgmcu_wba))] + { + cr.set_dbg_stop(config.enable_debug_during_sleep); + cr.set_dbg_standby(config.enable_debug_during_sleep); + } + #[cfg(any( + dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1, + dbgmcu_l4, dbgmcu_wb, dbgmcu_wl + ))] + { + cr.set_dbg_sleep(config.enable_debug_during_sleep); + cr.set_dbg_stop(config.enable_debug_during_sleep); + cr.set_dbg_standby(config.enable_debug_during_sleep); + } + #[cfg(dbgmcu_h7)] + { + cr.set_d1dbgcken(config.enable_debug_during_sleep); + cr.set_d3dbgcken(config.enable_debug_during_sleep); + cr.set_dbgsleep_d1(config.enable_debug_during_sleep); + cr.set_dbgstby_d1(config.enable_debug_during_sleep); + cr.set_dbgstop_d1(config.enable_debug_during_sleep); + } + }); #[cfg(not(any(stm32f1, stm32wb, stm32wl)))] peripherals::SYSCFG::enable_and_reset_with_cs(cs); diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 65e8713f0..1ffb567b3 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -24,7 +24,7 @@ use crate::time::Hertz; ), path = "v2.rs" )] -#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] +#[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5), path = "v3.rs")] mod _version; #[allow(unused_imports)] pub use _version::*; @@ -43,7 +43,7 @@ pub(crate) enum WakeupPrescaler { Div16 = 16, } -#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))] +#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4, stm32l5))] impl From for crate::pac::rtc::vals::Wucksel { fn from(val: WakeupPrescaler) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -57,7 +57,7 @@ impl From for crate::pac::rtc::vals::Wucksel { } } -#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))] +#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4, stm32l5))] impl From for WakeupPrescaler { fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -348,7 +348,7 @@ impl Rtc { ) { use embassy_time::{Duration, TICK_HZ}; - #[cfg(any(rtc_v3, rtc_v3u5))] + #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] use crate::pac::rtc::vals::Calrf; // Panic if the rcc mod knows we're not using low-power rtc @@ -375,7 +375,7 @@ impl Rtc { while !regs.isr().read().wutwf() {} } - #[cfg(any(rtc_v3, rtc_v3u5))] + #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] { regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); while !regs.icsr().read().wutwf() {} @@ -404,7 +404,7 @@ impl Rtc { /// was called, otherwise none pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option { use crate::interrupt::typelevel::Interrupt; - #[cfg(any(rtc_v3, rtc_v3u5))] + #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] use crate::pac::rtc::vals::Calrf; let instant = self.instant().unwrap(); @@ -420,13 +420,19 @@ impl Rtc { ))] regs.isr().modify(|w| w.set_wutf(false)); - #[cfg(any(rtc_v3, rtc_v3u5))] + #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); + #[cfg(not(stm32l5))] crate::pac::EXTI .pr(0) .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + #[cfg(stm32l5)] + crate::pac::EXTI + .fpr(0) + .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + ::WakeupInterrupt::unpend(); }); } diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 902776b0a..114141b64 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -135,6 +135,12 @@ impl sealed::Instance for crate::peripherals::RTC { #[cfg(all(feature = "low-power", stm32g4))] type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; + #[cfg(all(feature = "low-power", stm32l5))] + const EXTI_WAKEUP_LINE: usize = 17; + + #[cfg(all(feature = "low-power", stm32l5))] + type WakeupInterrupt = crate::interrupt::typelevel::RTC; + fn read_backup_register(_rtc: &Rtc, register: usize) -> Option { #[allow(clippy::if_same_then_else)] if register < Self::BACKUP_REGISTER_COUNT { diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 8a0c85d2c..ea727b010 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -743,7 +743,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { T::enable_and_reset(); T::enable_and_reset(); - Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) } /// Create a new bidirectional UART with request-to-send and clear-to-send pins @@ -770,7 +770,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { w.set_rtse(true); w.set_ctse(true); }); - Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) } #[cfg(not(any(usart_v1, usart_v2)))] @@ -795,10 +795,76 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { T::regs().cr3().write(|w| { w.set_dem(true); }); - Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) } - fn new_inner( + /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. + /// + /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin. + /// There is no functional difference between these methods, as both allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[cfg(not(any(usart_v1, usart_v2)))] + #[doc(alias("HDSEL"))] + pub fn new_half_duplex( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + mut config: Config, + ) -> Result { + // UartRx and UartTx have one refcount ea. + T::enable_and_reset(); + T::enable_and_reset(); + + config.swap_rx_tx = false; + + into_ref!(peri, tx, tx_dma, rx_dma); + + T::regs().cr3().write(|w| w.set_hdsel(true)); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + Self::new_inner(peri, tx_dma, rx_dma, config) + } + + /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. + /// + /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin. + /// There is no functional difference between these methods, as both allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[cfg(not(any(usart_v1, usart_v2)))] + #[doc(alias("HDSEL"))] + pub fn new_half_duplex_on_rx( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + mut config: Config, + ) -> Result { + // UartRx and UartTx have one refcount ea. + T::enable_and_reset(); + T::enable_and_reset(); + + config.swap_rx_tx = true; + + into_ref!(peri, rx, tx_dma, rx_dma); + + T::regs().cr3().write(|w| w.set_hdsel(true)); + rx.set_as_af(rx.af_num(), AFType::OutputPushPull); + + Self::new_inner(peri, tx_dma, rx_dma, config) + } + + fn new_inner_configure( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -808,8 +874,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { ) -> Result { into_ref!(peri, rx, tx, tx_dma, rx_dma); - let r = T::regs(); - // Some chips do not have swap_rx_tx bit cfg_if::cfg_if! { if #[cfg(any(usart_v3, usart_v4))] { @@ -827,6 +891,17 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } + Self::new_inner(peri, tx_dma, rx_dma, config) + } + + fn new_inner( + peri: PeripheralRef<'d, T>, + tx_dma: PeripheralRef<'d, TxDma>, + rx_dma: PeripheralRef<'d, RxDma>, + config: Config, + ) -> Result { + let r = T::regs(); + configure(r, &config, T::frequency(), T::KIND, true, true)?; T::Interrupt::unpend(); diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index a8aebfe1f..04b1b35e8 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -701,10 +701,10 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { } async fn wait_enabled(&mut self) { - trace!("wait_enabled OUT WAITING"); + trace!("wait_enabled IN WAITING"); let index = self.info.addr.index(); poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); + EP_IN_WAKERS[index].register(cx.waker()); let regs = T::regs(); if regs.epr(index).read().stat_tx() == Stat::DISABLED { Poll::Pending @@ -713,7 +713,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { } }) .await; - trace!("wait_enabled OUT OK"); + trace!("wait_enabled IN OK"); } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e1af0b647..a6fe52ee2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "beta-2023-12-17" +channel = "1.75" components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi",