Merge branch 'main' into configurable-bank-support

This commit is contained in:
Dion Dokter 2025-05-06 13:16:33 +02:00
commit 8017d58eb9
13 changed files with 67 additions and 457 deletions

View File

@ -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)
## <a href="https://embassy.dev/book/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
## 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 <a href="https://rust-lang.github.io/async-book/">async/await</a> 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 <a href="https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown">faster and smaller than one!</a>
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.
- <a href="https://docs.embassy.dev/embassy-stm32/">embassy-stm32</a>, for all STM32 microcontroller families.
- <a href="https://docs.embassy.dev/embassy-nrf/">embassy-nrf</a>, for the Nordic Semiconductor nRF52, nRF53, nRF54 and nRF91 series.
- <a href="https://docs.embassy.dev/embassy-rp/">embassy-rp</a>, for the Raspberry Pi RP2040 and RP23xx microcontrollers.
- <a href="https://docs.embassy.dev/embassy-mspm0/">embassy-mspm0</a>, for the Texas Instruments MSPM0 microcontrollers.
- <a href="https://github.com/esp-rs">esp-rs</a>, 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.
- <a href="https://github.com/ch32-rs/ch32-hal">ch32-hal</a>, for the WCH 32-bit RISC-V(CH32V) series of chips.
- <a href="https://github.com/AlexCharlton/mpfs-hal">mpfs-hal</a>, for the Microchip PolarFire SoC.
- <a href="https://github.com/py32-rs/py32-hal">py32-hal</a>, 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. <a href="https://docs.embassy.dev/embassy-time">embassy_time</a> 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 <a href="https://github.com/embassy-rs/embassy/blob/master/examples/nrf52840/src/bin/multiprio.rs">example</a>.
- **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 <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> 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 <a href="https://github.com/embassy-rs/trouble">trouble</a> crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the <a href="https://github.com/embassy-rs/bt-hci">bt-hci</a> traits (currently `nRF52`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported).
- The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
- The <a href="https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan">embassy-stm32-wpan</a> 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 <a href="https://github.com/lora-rs/lora-rs">lora-rs</a> 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** -
<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> 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** -
<a href="https://github.com/embassy-rs/embassy/tree/master/embassy-boot">embassy-boot</a> 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?

View File

@ -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"

View File

@ -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",

View File

@ -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!
────────────────────────────────────────────────────────────────────────────────

View File

@ -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"]
----

View File

@ -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
]
----

View File

@ -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

View File

@ -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<T::Interrupt, InterruptHandler<T>> + '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();
}
}

View File

@ -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,
_ => (),
}

View File

@ -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()
}

View File

@ -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 {

View File

@ -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<Output = T> + '_ {
poll_fn(move |cx| self.poll_wait(cx))
}

View File

@ -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<M: RawMutex, T: Clone, const N: usize> WatchBehavior<T> for Watch<M, T, N>
}
impl<M: RawMutex, T: Clone, const N: usize> Watch<M, T, N> {
/// 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 {