From df84dfec7a55bed44ad5609511e4a2f596a156af Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 May 2025 14:35:46 +0200 Subject: [PATCH 1/9] Remove embassy_nrf::radio::ble. Fixes #4144 --- embassy-nrf/src/radio/ble.rs | 394 ---------------------------- embassy-nrf/src/radio/ieee802154.rs | 7 +- embassy-nrf/src/radio/mod.rs | 8 +- 3 files changed, 5 insertions(+), 404 deletions(-) delete mode 100644 embassy-nrf/src/radio/ble.rs diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs deleted file mode 100644 index d42bbe5f6..000000000 --- a/embassy-nrf/src/radio/ble.rs +++ /dev/null @@ -1,394 +0,0 @@ -//! Radio driver implementation focused on Bluetooth Low-Energy transmission. - -use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::Poll; - -use embassy_hal_internal::drop::OnDrop; -pub use pac::radio::vals::Mode; -#[cfg(not(feature = "_nrf51"))] -use pac::radio::vals::Plen as PreambleLength; - -use crate::interrupt::typelevel::Interrupt; -use crate::pac::radio::vals; -use crate::radio::*; -pub use crate::radio::{Error, TxPower}; -use crate::util::slice_in_ram_or; -use crate::Peri; - -/// Radio driver. -pub struct Radio<'d, T: Instance> { - _p: Peri<'d, T>, -} - -impl<'d, T: Instance> Radio<'d, T> { - /// Create a new radio driver. - pub fn new( - radio: Peri<'d, T>, - _irq: impl interrupt::typelevel::Binding> + 'd, - ) -> Self { - let r = T::regs(); - - r.pcnf1().write(|w| { - // It is 0 bytes long in a standard BLE packet - w.set_statlen(0); - // MaxLen configures the maximum packet payload plus add-on size in - // number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure - // that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means - // that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a - // packet larger than MAXLEN, the payload will be truncated at MAXLEN - // - // To simplify the implementation, It is setted as the maximum value - // and the length of the packet is controlled only by the LENGTH field in the packet - w.set_maxlen(255); - // Configure the length of the address field in the packet - // The prefix after the address fields is always appended, so is always 1 byte less than the size of the address - // The base address is truncated from the least significant byte if the BALEN is less than 4 - // - // BLE address is always 4 bytes long - w.set_balen(3); // 3 bytes base address (+ 1 prefix); - // Configure the endianess - // For BLE is always little endian (LSB first) - w.set_endian(vals::Endian::LITTLE); - // Data whitening is used to avoid long sequences of zeros or - // ones, e.g., 0b0000000 or 0b1111111, in the data bit stream. - // The whitener and de-whitener are defined the same way, - // using a 7-bit linear feedback shift register with the - // polynomial x7 + x4 + 1. - // - // In BLE Whitening shall be applied on the PDU and CRC of all - // Link Layer packets and is performed after the CRC generation - // in the transmitter. No other parts of the packets are whitened. - // De-whitening is performed before the CRC checking in the receiver - // Before whitening or de-whitening, the shift register should be - // initialized based on the channel index. - w.set_whiteen(true); - }); - - // Configure CRC - r.crccnf().write(|w| { - // In BLE the CRC shall be calculated on the PDU of all Link Layer - // packets (even if the packet is encrypted). - // It skips the address field - w.set_skipaddr(vals::Skipaddr::SKIP); - // In BLE 24-bit CRC = 3 bytes - w.set_len(vals::Len::THREE); - }); - - // Ch map between 2400 MHZ .. 2500 MHz - // All modes use this range - #[cfg(not(feature = "_nrf51"))] - r.frequency().write(|w| w.set_map(vals::Map::DEFAULT)); - - // Configure shortcuts to simplify and speed up sending and receiving packets. - r.shorts().write(|w| { - // start transmission/recv immediately after ramp-up - // disable radio when transmission/recv is done - w.set_ready_start(true); - w.set_end_disable(true); - }); - - // Enable NVIC interrupt - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - Self { _p: radio } - } - - fn state(&self) -> RadioState { - super::state(T::regs()) - } - - /// Set the radio mode - /// - /// The radio must be disabled before calling this function - pub fn set_mode(&mut self, mode: Mode) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - r.mode().write(|w| w.set_mode(mode)); - - #[cfg(not(feature = "_nrf51"))] - r.pcnf0().write(|w| { - w.set_plen(match mode { - Mode::BLE_1MBIT => PreambleLength::_8BIT, - Mode::BLE_2MBIT => PreambleLength::_16BIT, - #[cfg(any( - feature = "nrf52811", - feature = "nrf52820", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" - ))] - Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE, - _ => unimplemented!(), - }) - }); - } - - /// Set the header size changing the S1's len field - /// - /// The radio must be disabled before calling this function - pub fn set_header_expansion(&mut self, use_s1_field: bool) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - // s1 len in bits - let s1len: u8 = match use_s1_field { - false => 0, - true => 8, - }; - - r.pcnf0().write(|w| { - // Configure S0 to 1 byte length, this will represent the Data/Adv header flags - w.set_s0len(true); - // Configure the length (in bits) field to 1 byte length, this will represent the length of the payload - // and also be used to know how many bytes to read/write from/to the buffer - w.set_lflen(0); - // Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE. - w.set_s1len(s1len); - }); - } - - /// Set initial data whitening value - /// Data whitening is used to avoid long sequences of zeros or ones, e.g., 0b0000000 or 0b1111111, in the data bit stream - /// On BLE the initial value is the channel index | 0x40 - /// - /// The radio must be disabled before calling this function - pub fn set_whitening_init(&mut self, whitening_init: u8) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - r.datawhiteiv().write(|w| w.set_datawhiteiv(whitening_init)); - } - - /// Set the central frequency to be used - /// It should be in the range 2400..2500 - /// - /// [The radio must be disabled before calling this function](https://devzone.nordicsemi.com/f/nordic-q-a/15829/radio-frequency-change) - pub fn set_frequency(&mut self, frequency: u32) { - assert!(self.state() == RadioState::DISABLED); - assert!((2400..=2500).contains(&frequency)); - - let r = T::regs(); - - r.frequency().write(|w| w.set_frequency((frequency - 2400) as u8)); - } - - /// Set the acess address - /// This address is always constants for advertising - /// And a random value generate on each connection - /// It is used to filter the packages - /// - /// The radio must be disabled before calling this function - pub fn set_access_address(&mut self, access_address: u32) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - // Configure logical address - // The byte ordering on air is always least significant byte first for the address - // So for the address 0xAA_BB_CC_DD, the address on air will be DD CC BB AA - // The package order is BASE, PREFIX so BASE=0xBB_CC_DD and PREFIX=0xAA - r.prefix0().write(|w| w.set_ap0((access_address >> 24) as u8)); - - // The base address is truncated from the least significant byte (because the BALEN is less than 4) - // So it shifts the address to the right - r.base0().write_value(access_address << 8); - - // Don't match tx address - r.txaddress().write(|w| w.set_txaddress(0)); - - // Match on logical address - // This config only filter the packets by the address, - // so only packages send to the previous address - // will finish the reception (TODO: check the explanation) - r.rxaddresses().write(|w| { - w.set_addr0(true); - w.set_addr1(true); - w.set_addr2(true); - w.set_addr3(true); - w.set_addr4(true); - }); - } - - /// Set the CRC polynomial - /// It only uses the 24 least significant bits - /// - /// The radio must be disabled before calling this function - pub fn set_crc_poly(&mut self, crc_poly: u32) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - r.crcpoly().write(|w| { - // Configure the CRC polynomial - // Each term in the CRC polynomial is mapped to a bit in this - // register which index corresponds to the term's exponent. - // The least significant term/bit is hard-wired internally to - // 1, and bit number 0 of the register content is ignored by - // the hardware. The following example is for an 8 bit CRC - // polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 . - w.set_crcpoly(crc_poly & 0xFFFFFF) - }); - } - - /// Set the CRC init value - /// It only uses the 24 least significant bits - /// The CRC initial value varies depending of the PDU type - /// - /// The radio must be disabled before calling this function - pub fn set_crc_init(&mut self, crc_init: u32) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - r.crcinit().write(|w| w.set_crcinit(crc_init & 0xFFFFFF)); - } - - /// Set the radio tx power - /// - /// The radio must be disabled before calling this function - pub fn set_tx_power(&mut self, tx_power: TxPower) { - assert!(self.state() == RadioState::DISABLED); - - let r = T::regs(); - - r.txpower().write(|w| w.set_txpower(tx_power)); - } - - /// Set buffer to read/write - /// - /// This method is unsound. You should guarantee that the buffer will live - /// for the life time of the transmission or if the buffer will be modified. - /// Also if the buffer is smaller than the packet length, the radio will - /// read/write memory out of the buffer bounds. - fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { - slice_in_ram_or(buffer, Error::BufferNotInRAM)?; - - let r = T::regs(); - - // Here it consider that the length of the packet is - // correctly set in the buffer, otherwise it will send - // unowned regions of memory - let ptr = buffer.as_ptr(); - - // Configure the payload - r.packetptr().write_value(ptr as u32); - - Ok(()) - } - - /// Send packet - /// If the length byte in the package is greater than the buffer length - /// the radio will read memory out of the buffer bounds - pub async fn transmit(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.set_buffer(buffer)?; - - let r = T::regs(); - self.trigger_and_wait_end(move || { - // Initialize the transmission - // trace!("txen"); - - r.tasks_txen().write_value(1); - }) - .await; - - Ok(()) - } - - /// Receive packet - /// If the length byte in the received package is greater than the buffer length - /// the radio will write memory out of the buffer bounds - pub async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.set_buffer(buffer)?; - - let r = T::regs(); - self.trigger_and_wait_end(move || { - // Initialize the transmission - // trace!("rxen"); - r.tasks_rxen().write_value(1); - }) - .await; - - Ok(()) - } - - async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) { - let r = T::regs(); - let s = T::state(); - - // If the Future is dropped before the end of the transmission - // it disable the interrupt and stop the transmission - // to keep the state consistent - let drop = OnDrop::new(|| { - trace!("radio drop: stopping"); - - r.intenclr().write(|w| w.set_end(true)); - - r.tasks_stop().write_value(1); - - r.events_end().write_value(0); - - trace!("radio drop: stopped"); - }); - - // trace!("radio:enable interrupt"); - // Clear some remnant side-effects (TODO: check if this is necessary) - r.events_end().write_value(0); - - // Enable interrupt - r.intenset().write(|w| w.set_end(true)); - - compiler_fence(Ordering::SeqCst); - - // Trigger the transmission - trigger(); - - // On poll check if interrupt happen - poll_fn(|cx| { - s.event_waker.register(cx.waker()); - if r.events_end().read() == 1 { - // trace!("radio:end"); - return core::task::Poll::Ready(()); - } - Poll::Pending - }) - .await; - - compiler_fence(Ordering::SeqCst); - r.events_end().write_value(0); // ACK - - // Everthing ends fine, so it disable the drop - drop.defuse(); - } - - /// Disable the radio - fn disable(&mut self) { - let r = T::regs(); - - compiler_fence(Ordering::SeqCst); - // If it is already disabled, do nothing - if self.state() != RadioState::DISABLED { - trace!("radio:disable"); - // Trigger the disable task - r.tasks_disable().write_value(1); - - // Wait until the radio is disabled - while r.events_disabled().read() == 0 {} - - compiler_fence(Ordering::SeqCst); - - // Acknowledge it - r.events_disabled().write_value(0); - } - } -} - -impl<'d, T: Instance> Drop for Radio<'d, T> { - fn drop(&mut self) { - self.disable(); - } -} diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index 2f0bcbe04..7f4f8f462 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -5,10 +5,11 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; -use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower}; +use super::{Error, Instance, InterruptHandler, TxPower}; use crate::interrupt::typelevel::Interrupt; use crate::interrupt::{self}; use crate::pac::radio::vals; +pub use crate::pac::radio::vals::State as RadioState; use crate::Peri; /// Default (IEEE compliant) Start of Frame Delimiter @@ -200,7 +201,7 @@ impl<'d, T: Instance> Radio<'d, T> { /// Get the current radio state fn state(&self) -> RadioState { - state(T::regs()) + T::regs().state().read().state() } /// Moves the radio from any state to the DISABLED state @@ -293,7 +294,7 @@ impl<'d, T: Instance> Radio<'d, T> { r.shorts().write(|_| {}); r.tasks_stop().write_value(1); loop { - match state(r) { + match r.state().read().state() { RadioState::DISABLED | RadioState::RX_IDLE => break, _ => (), } diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 982436266..608ef9024 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -6,7 +6,6 @@ #![macro_use] /// Bluetooth Low Energy Radio driver. -pub mod ble; #[cfg(any( feature = "nrf52811", feature = "nrf52820", @@ -21,7 +20,6 @@ use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; use embassy_sync::waitqueue::AtomicWaker; -use pac::radio::vals::State as RadioState; pub use pac::radio::vals::Txpower as TxPower; use crate::{interrupt, pac}; @@ -82,6 +80,7 @@ macro_rules! impl_radio { pac::$pac_type } + #[allow(unused)] fn state() -> &'static crate::radio::State { static STATE: crate::radio::State = crate::radio::State::new(); &STATE @@ -99,8 +98,3 @@ pub trait Instance: SealedInstance + PeripheralType + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } - -/// Get the state of the radio -pub(crate) fn state(radio: pac::radio::Radio) -> RadioState { - radio.state().read().state() -} From 38fd357536e0332c3295d760eed4f481f4d01ce2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 May 2025 18:38:37 +0200 Subject: [PATCH 2/9] Update stm32-metapac --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 54badc8f2..e9f236881 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -73,7 +73,7 @@ rand_core = "0.6.3" sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a821bf5dd8d283c1e8de88fc7699235777a07e78" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-169e38298f9421dddbabc0eb81d0c30fb1eec0a7" } vcell = "0.1.3" nb = "1.0.0" @@ -102,7 +102,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a821bf5dd8d283c1e8de88fc7699235777a07e78", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-169e38298f9421dddbabc0eb81d0c30fb1eec0a7", default-features = false, features = ["metadata"] } [features] default = ["rt"] From e5c03e1e791bf3460fe6e0af65a02f2259763eaa Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 2 May 2025 17:56:32 -0400 Subject: [PATCH 3/9] chore: use `probe-rs` instead of `probe-run` I found a few remaining deprecated `probe-run` cases. --- docs/examples/basic/.cargo/config.toml | 4 ++-- docs/examples/layer-by-layer/.cargo/config.toml | 3 ++- docs/pages/getting_started.adoc | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/examples/basic/.cargo/config.toml b/docs/examples/basic/.cargo/config.toml index 8ca28df39..17616a054 100644 --- a/docs/examples/basic/.cargo/config.toml +++ b/docs/examples/basic/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF82840_xxAA with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip nRF52840_xxAA" +# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip nRF52840_xxAA" [build] target = "thumbv7em-none-eabi" diff --git a/docs/examples/layer-by-layer/.cargo/config.toml b/docs/examples/layer-by-layer/.cargo/config.toml index 3012f05dc..f30d9e446 100644 --- a/docs/examples/layer-by-layer/.cargo/config.toml +++ b/docs/examples/layer-by-layer/.cargo/config.toml @@ -1,5 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-run --chip STM32L475VG" +# replace your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32L475VG" rustflags = [ "-C", "link-arg=--nmagic", diff --git a/docs/pages/getting_started.adoc b/docs/pages/getting_started.adoc index 954f3fd28..d1f65a885 100644 --- a/docs/pages/getting_started.adoc +++ b/docs/pages/getting_started.adoc @@ -66,7 +66,7 @@ If everything worked correctly, you should see a blinking LED on your board, and [source] ---- Finished dev [unoptimized + debuginfo] target(s) in 1m 56s - Running `probe-run --chip STM32F407VGTx target/thumbv7em-none-eabi/debug/blinky` + Running `probe-rs run --chip STM32F407VGTx target/thumbv7em-none-eabi/debug/blinky` (HOST) INFO flashing program (71.36 KiB) (HOST) INFO success! ──────────────────────────────────────────────────────────────────────────────── From 7f259aaff26bb060e49f91f894ff00e257e1a744 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 2 May 2025 21:54:21 -0400 Subject: [PATCH 4/9] chore: minor readme cleanup use simpler markdown linking, minor grammar cleanup --- README.md | 85 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 383fb6671..669fa469b 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,55 @@ # Embassy -Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries. +Embassy is the next-generation framework for embedded applications. Write safe, correct, and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries. + +## [Documentation](https://embassy.dev/book/index.html) - [API reference](https://docs.embassy.dev/) - [Website](https://embassy.dev/) - [Chat](https://matrix.to/#/#embassy-rs:matrix.org) -## Documentation - API reference - Website - Chat ## Rust + async ❤️ embedded -The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system. +The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector, or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system. -Rust's async/await allows for unprecedentedly easy and efficient multitasking in embedded systems. Tasks get transformed at compile time into state machines that get run cooperatively. It requires no dynamic memory allocation, and runs on a single stack, so no per-task stack size tuning is required. It obsoletes the need for a traditional RTOS with kernel context switching, and is faster and smaller than one! +Rust's [async/await](https://rust-lang.github.io/async-book/) allows for unprecedentedly easy and efficient multitasking in embedded systems. Tasks get transformed at compile time into state machines that get run cooperatively. It requires no dynamic memory allocation and runs on a single stack, so no per-task stack size tuning is required. It obsoletes the need for a traditional RTOS with kernel context switching, and is [faster and smaller than one!](https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown) ## Batteries included -- **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy. - - embassy-stm32, for all STM32 microcontroller families. - - embassy-nrf, for the Nordic Semiconductor nRF52, nRF53, nRF54 and nRF91 series. - - embassy-rp, for the Raspberry Pi RP2040 and RP23xx microcontrollers. - - embassy-mspm0, for the Texas Instruments MSPM0 microcontrollers. - - esp-rs, for the Espressif Systems ESP32 series of chips. - - Embassy HAL support for Espressif chips, as well as Async WiFi, Bluetooth and ESP-NOW, is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository. - - ch32-hal, for the WCH 32-bit RISC-V(CH32V) series of chips. - - mpfs-hal, for the Microchip PolarFire SoC. - - py32-hal, for the Puya Semiconductor PY32 series of microcontrollers. +- **Hardware Abstraction Layers + ** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy. + - [embassy-stm32](https://docs.embassy.dev/embassy-stm32/), for all STM32 microcontroller families. + - [embassy-nrf](https://docs.embassy.dev/embassy-nrf/), for the Nordic Semiconductor nRF52, nRF53, nRF54 and nRF91 series. + - [embassy-rp](https://docs.embassy.dev/embassy-rp/), for the Raspberry Pi RP2040 and RP23xx microcontrollers. + - [embassy-mspm0](https://docs.embassy.dev/embassy-mspm0/), for the Texas Instruments MSPM0 microcontrollers. + - [esp-rs](https://github.com/esp-rs), for the Espressif Systems ESP32 series of chips. + - Embassy HAL support for Espressif chips, as well as Async Wi-Fi, Bluetooth, and ESP-NOW, is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository. + - [ch32-hal](https://github.com/ch32-rs/ch32-hal), for the WCH 32-bit RISC-V(CH32V) series of chips. + - [mpfs-hal](https://github.com/AlexCharlton/mpfs-hal), for the Microchip PolarFire SoC. + - [py32-hal](https://github.com/py32-rs/py32-hal), for the Puya Semiconductor PY32 series of microcontrollers. -- **Time that Just Works** - -No more messing with hardware timers. embassy_time provides Instant, Duration and Timer types that are globally available and never overflow. +- **Time that Just Works** - + No more messing with hardware timers. [embassy_time](https://docs.embassy.dev/embassy-time) provides Instant, Duration, and Timer types that are globally available and never overflow. -- **Real-time ready** - -Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities, so that higher priority tasks preempt lower priority ones. See the example. +- **Real-time ready** - + Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities so that higher priority tasks preempt lower priority ones. See the [example](https://github.com/embassy-rs/embassy/blob/master/examples/nrf52840/src/bin/multiprio.rs). -- **Low-power ready** - -Easily build devices with years of battery life. The async executor automatically puts the core to sleep when there's no work to do. Tasks are woken by interrupts, there is no busy-loop polling while waiting. - -- **Networking** - -The embassy-net network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. +- **Low-power ready** - + Easily build devices with years of battery life. The async executor automatically puts the core to sleep when there's no work to do. Tasks are woken by interrupts, there is no busy-loop polling while waiting. + +- **Networking** - + The [embassy-net](https://docs.embassy.dev/embassy-net/) network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP, and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. - **Bluetooth** - - The trouble crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the bt-hci traits (currently `nRF52`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported). - - 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. + - The [trouble](https://github.com/embassy-rs/trouble) crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the [bt-hci](https://github.com/embassy-rs/bt-hci) traits (currently + `nRF52`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported). + - The [nrf-softdevice](https://github.com/embassy-rs/nrf-softdevice) crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. + - The [embassy-stm32-wpan](https://github.com/embassy-rs/embassy/tree/main/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](https://github.com/lora-rs/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. - -- **Bootloader and DFU** - -embassy-boot is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks. +- **USB** - + [embassy-usb](https://docs.embassy.dev/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. +- **Bootloader and DFU** - + [embassy-boot](https://github.com/embassy-rs/embassy/tree/master/embassy-boot) is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks. ## Sneak peek @@ -93,13 +96,15 @@ async fn main(spawner: Spawner) { ## Examples -Examples are found in the `examples/` folder separated by the chip manufacturer they are designed to run on. For example: +Examples are found in the +`examples/` folder separated by the chip manufacturer they are designed to run on. For example: -* `examples/nrf52840` run on the `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards. -* `examples/nrf5340` run on the `nrf5340-dk` board (PCA10095). -* `examples/stm32xx` for the various STM32 families. -* `examples/rp` are for the RP2040 chip. -* `examples/std` are designed to run locally on your PC. +* `examples/nrf52840` run on the + `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards. +* `examples/nrf5340` run on the `nrf5340-dk` board (PCA10095). +* `examples/stm32xx` for the various STM32 families. +* `examples/rp` are for the RP2040 chip. +* `examples/std` are designed to run locally on your PC. ### Running examples @@ -126,7 +131,7 @@ cargo run --release --bin blinky For more help getting started, see [Getting Started][1] and [Running the Examples][2]. -## Developing Embassy with Rust Analyzer based editors +## Developing Embassy with Rust Analyzer-based editors The [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/) and others. Given the multiple targets that Embassy serves, there is no Cargo workspace file. Instead, the Rust Analyzer @@ -136,7 +141,7 @@ please refer to the `.vscode/settings.json` file's `rust-analyzer.linkedProjects ## Minimum supported Rust version (MSRV) Embassy is guaranteed to compile on stable Rust 1.75 and up. It *might* -compile with older versions but that may change in any new patch release. +compile with older versions, but that may change in any new patch release. ## Why the name? From 0406b83c526f03567863d80a603db77c514df27f Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Sun, 4 May 2025 09:39:35 +0900 Subject: [PATCH 5/9] Tweak the example `rust-toolchain.toml` in doc --- docs/pages/project_structure.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/project_structure.adoc b/docs/pages/project_structure.adoc index 722ec8d9d..227508b97 100644 --- a/docs/pages/project_structure.adoc +++ b/docs/pages/project_structure.adoc @@ -85,9 +85,9 @@ A minimal example: [source,toml] ---- [toolchain] -channel = "nightly-2023-08-19" # <- as of writing, this is the exact rust version embassy uses +channel = "1.85" # <- as of writing, this is the exact rust version embassy uses components = [ "rust-src", "rustfmt" ] # <- optionally add "llvm-tools-preview" for some extra features like "cargo size" targets = [ - "thumbv6m-none-eabi" # <-change for your platform + "thumbv6m-none-eabi" # <- change for your platform ] ---- From a42fa0a67aef90453b239b62b81df8db20664c87 Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Sun, 4 May 2025 09:54:37 +0900 Subject: [PATCH 6/9] Tweak --- docs/pages/new_project.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/new_project.adoc b/docs/pages/new_project.adoc index af1cb75c5..cd943b4f6 100644 --- a/docs/pages/new_project.adoc +++ b/docs/pages/new_project.adoc @@ -150,7 +150,7 @@ stm32g474-example # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2023-11-01" +channel = "1.85" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = ["thumbv7em-none-eabi"] ---- From 723ebfcb4f17e48f2a614f0e1e124e8da9c75b70 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sun, 4 May 2025 16:31:26 +0200 Subject: [PATCH 7/9] clarify docs for signal and watch --- embassy-sync/src/signal.rs | 5 +++-- embassy-sync/src/watch.rs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index a0f4b5a74..e7095401e 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -6,7 +6,7 @@ use core::task::{Context, Poll, Waker}; use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; -/// Single-slot signaling primitive. +/// Single-slot signaling primitive for a _single_ consumer. /// /// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except /// "sending" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead @@ -17,6 +17,7 @@ use crate::blocking_mutex::Mutex; /// updates. /// /// For more advanced use cases, you might want to use [`Channel`](crate::channel::Channel) instead. +/// For multiple consumers, use [`Watch`](crate::watch::Watch) instead. /// /// Signals are generally declared as `static`s and then borrowed as required. /// @@ -106,7 +107,7 @@ where }) } - /// Future that completes when this Signal has been signaled. + /// Future that completes when this Signal has been signaled, taking the value out of the signal. pub fn wait(&self) -> impl Future + '_ { poll_fn(move |cx| self.poll_wait(cx)) } diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index e76646c0b..08d6a833d 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -10,7 +10,7 @@ use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; use crate::waitqueue::MultiWakerRegistration; -/// The `Watch` is a single-slot signaling primitive that allows multiple receivers to concurrently await +/// The `Watch` is a single-slot signaling primitive that allows _multiple_ (`N`) receivers to concurrently await /// changes to the value. Unlike a [`Signal`](crate::signal::Signal), `Watch` supports multiple receivers, /// and unlike a [`PubSubChannel`](crate::pubsub::PubSubChannel), `Watch` immediately overwrites the previous /// value when a new one is sent, without waiting for all receivers to read the previous value. @@ -298,7 +298,7 @@ impl WatchBehavior for Watch } impl Watch { - /// Create a new `Watch` channel. + /// Create a new `Watch` channel for `N` receivers. pub const fn new() -> Self { Self { mutex: Mutex::new(RefCell::new(WatchState { From 5105442f1f0587c0899ea29b52c5202ed5c81e6c Mon Sep 17 00:00:00 2001 From: Hiroaki Yutani Date: Tue, 6 May 2025 09:44:00 +0900 Subject: [PATCH 8/9] Fix clippy::bad_bit_mask --- embassy-stm32/src/spdifrx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs index 08dba04fe..9c42217f0 100644 --- a/embassy-stm32/src/spdifrx/mod.rs +++ b/embassy-stm32/src/spdifrx/mod.rs @@ -223,7 +223,7 @@ impl<'d, T: Instance> Spdifrx<'d, T> { }; for sample in data.as_mut() { - if (*sample & (0x0002_u32)) == 0x0001 { + if (*sample & (0x0002_u32)) != 0 { // Discard invalid samples, setting them to mute level. *sample = 0; } else { From 254b203e1a88f7524911d520bc8738447f5c813c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 6 May 2025 12:46:52 +0200 Subject: [PATCH 9/9] fix: update embassy-net changelog --- embassy-net/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-net/CHANGELOG.md b/embassy-net/CHANGELOG.md index cfee5f3cf..8773772ce 100644 --- a/embassy-net/CHANGELOG.md +++ b/embassy-net/CHANGELOG.md @@ -9,10 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 No unreleased changes yet... Quick, go send a PR! -## 0.7 - 2025-02-14 +## 0.7 - 2025-05-06 - don't infinite loop if udp::send methods receive a buffer too large to ever be sent - add ICMP sockets and a ping utility +- configurable rate_limit for the ping utility +- Feature match udp sockets ## 0.6 - 2025-01-05