From 847b8be81480b249a2e8b1ff502e6188d2dadf04 Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Wed, 7 Feb 2024 15:25:07 +0100 Subject: [PATCH 01/32] feat/implement ble radio on nrf --- embassy-nrf/Cargo.toml | 5 + embassy-nrf/src/chips/nrf52805.rs | 6 + embassy-nrf/src/chips/nrf52810.rs | 6 + embassy-nrf/src/chips/nrf52811.rs | 6 + embassy-nrf/src/chips/nrf52820.rs | 6 + embassy-nrf/src/chips/nrf52832.rs | 6 + embassy-nrf/src/chips/nrf52833.rs | 6 + embassy-nrf/src/chips/nrf52840.rs | 6 + embassy-nrf/src/chips/nrf5340_net.rs | 6 + embassy-nrf/src/lib.rs | 6 + embassy-nrf/src/radio/ble.rs | 432 ++++++++++++++++++ embassy-nrf/src/radio/mod.rs | 89 ++++ examples/nrf52840/Cargo.toml | 8 +- .../nrf52840/src/bin/radio_ble_advertising.rs | 42 ++ 14 files changed, 628 insertions(+), 2 deletions(-) create mode 100644 embassy-nrf/src/radio/ble.rs create mode 100644 embassy-nrf/src/radio/mod.rs create mode 100644 examples/nrf52840/src/bin/radio_ble_advertising.rs diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 7e161df9b..dcdc7f313 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -57,6 +57,9 @@ unstable-pac = [] ## Enable GPIO tasks and events gpiote = [] +## Enable radio driver +radio = ["dep:jewel"] + ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz time-driver-rtc1 = ["_time-driver"] @@ -150,6 +153,8 @@ embedded-storage-async = "0.4.0" cfg-if = "1.0.0" document-features = "0.2.7" +jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel", optional = true } + nrf51-pac = { version = "0.12.0", optional = true } nrf52805-pac = { version = "0.12.0", optional = true } nrf52810-pac = { version = "0.12.0", optional = true } diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 624d6613d..b97c85f9e 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -129,6 +129,9 @@ embassy_hal_internal::peripherals! { // QDEC QDEC, + + // RADIO + RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -209,6 +212,9 @@ impl_ppi_channel!(PPI_CH31, 31 => static); impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_05, ANALOG_INPUT3); +#[cfg(feature = "radio")] +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 002feab3b..03548d03f 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -135,6 +135,9 @@ embassy_hal_internal::peripherals! { // PDM PDM, + + // Radio + RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -235,6 +238,9 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); +#[cfg(feature = "radio")] +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 5952907f8..992fbd129 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -135,6 +135,9 @@ embassy_hal_internal::peripherals! { // PDM PDM, + + // Radio + RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -237,6 +240,9 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); +#[cfg(feature = "radio")] +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index c2f792cb9..f241f4ea3 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -130,6 +130,9 @@ embassy_hal_internal::peripherals! { // QDEC QDEC, + + // Radio + RADIO, } impl_usb!(USBD, USBD, USBD); @@ -224,6 +227,9 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); +#[cfg(feature = "radio")] +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 65d52364d..6bbdd9a63 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -150,6 +150,9 @@ embassy_hal_internal::peripherals! { // PDM PDM, + + // Radio + RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -264,6 +267,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); +#[cfg(feature = "radio")] +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 7c9b66d69..e137e4dc6 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -170,6 +170,9 @@ embassy_hal_internal::peripherals! { // I2S I2S, + + // Radio + RADIO, } impl_usb!(USBD, USBD, USBD); @@ -306,6 +309,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); +#[cfg(feature = "radio")] +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 51c55cd4d..2d805f871 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -173,6 +173,9 @@ embassy_hal_internal::peripherals! { // I2S I2S, + + // Radio + RADIO, } impl_usb!(USBD, USBD, USBD); @@ -311,6 +314,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); +#[cfg(feature = "radio")] +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index a7cf82872..3248bde52 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -248,6 +248,9 @@ embassy_hal_internal::peripherals! { P1_13, P1_14, P1_15, + + // Radio + RADIO, } impl_uarte!(SERIAL0, UARTE0, SERIAL0); @@ -345,6 +348,9 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); +#[cfg(feature = "radio")] +impl_radio!(RADIO, RADIO, RADIO); + embassy_hal_internal::interrupt_mod!( CLOCK_POWER, RADIO, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 358a7cc27..961928d11 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -45,6 +45,12 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; + +#[cfg(feature = "radio")] +pub mod radio; +#[cfg(all(feature = "radio", feature = "_nrf9160"))] +compile_error!("feature `radio` is not valid for nRF91 series chips."); + #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] pub mod i2s; pub mod nvmc; diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs new file mode 100644 index 000000000..a5d9f447b --- /dev/null +++ b/embassy-nrf/src/radio/ble.rs @@ -0,0 +1,432 @@ +//! Radio driver implementation focused on Bluetooth Low-Energy transmission. +//! +//! The radio can calculate the CRC, perform data whitening, +//! automatically send the right preamble. +//! Most of the configuration is done automatically when you choose the mode and this driver. +//! +//! Some configuration can just be done when de device is disabled, +//! and the configuration varies depending if is a transmitter or a receiver. +//! Because of that we have a state machine to keep track of the state of the radio. +//! The Radio is the disable radio which configure the common parameters between +//! the bluetooth protocols, like the package format, the CRC and the whitening. +//! The TxRadio radio enable and configured as a transmitter with the specific parameters. + +use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; +use jewel::phy::{ + AdvertisingChannel, Channel, ChannelTrait, HeaderSize, Mode, Radio as BleRadio, ADV_ADDRESS, ADV_CRC_INIT, + CRC_POLY, MAX_PDU_LENGTH, +}; +use pac::radio::mode::MODE_A as PacMode; +use pac::radio::pcnf0::PLEN_A as PreambleLength; +// Re-export SVD variants to allow user to directly set values. +pub use pac::radio::{state::STATE_A as RadioState, txpower::TXPOWER_A as TxPower}; + +use crate::interrupt::typelevel::Interrupt; +use crate::radio::*; +use crate::util::slice_in_ram_or; + +/// UART error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// Buffer was too long. + BufferTooLong, + /// Buffer was to short. + BufferTooShort, + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, +} + +/// Radio driver. +pub struct Radio<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Radio<'d, T> { + /// Create a new radio driver. + pub fn new( + radio: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { + // From 5.4.1 of the nRF52840 Product Specification: + // > The HFXO must be running to use the RADIO or the calibration mechanism associated with the 32.768 kHz RC oscillator. + // Currently the jewel crate don't implement the calibration mechanism, so we need to ensure that the HFXO is running + utils::check_xtal(); + + into_ref!(radio); + + let r = T::regs(); + + r.pcnf1.write(|w| unsafe { + // It is 0 bytes long in a standard BLE packet + w.statlen() + .bits(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, I'm setting the max length to the maximum value + // and I'm using only the length field to truncate the payload + .maxlen() + .bits(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 + .balen() + .bits(3) // 3 bytes base address (+ 1 prefix); + // Configure the endianess + // For BLE is always little endian (LSB first) + .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. + .whiteen() + .set_bit() // Enable whitening + }); + + // 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). + // So here we skip the address field + w.skipaddr() + .skip() + // In BLE 24-bit CRC = 3 bytes + .len() + .three() + }); + + r.crcpoly.write(|w| unsafe { + // 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.crcpoly().bits(CRC_POLY & 0xFFFFFF) + }); + // The CRC initial value varies depending of the PDU type + + // Ch map between 2400 MHZ .. 2500 MHz + // All modes use this range + r.frequency.write(|w| w.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.ready_start().enabled().end_disable().enabled() + }); + + // Enable NVIC interrupt + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + let mut radio = Self { _p: radio }; + + // set defaults + radio.set_mode(Mode::Ble1mbit); + radio.set_tx_power(0); + radio.set_header_size(HeaderSize::TwoBytes); + radio.set_access_address(ADV_ADDRESS); + radio.set_crc_init(ADV_CRC_INIT); + radio.set_channel(AdvertisingChannel::Ch39.into()); + + radio + } + + #[allow(dead_code)] + fn trace_state(&self) { + let r = T::regs(); + + match r.state.read().state().variant().unwrap() { + RadioState::DISABLED => trace!("radio:state:DISABLED"), + RadioState::RX_RU => trace!("radio:state:RX_RU"), + RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"), + RadioState::RX => trace!("radio:state:RX"), + RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"), + RadioState::TX_RU => trace!("radio:state:TX_RU"), + RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"), + RadioState::TX => trace!("radio:state:TX"), + RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"), + } + } + + async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce() -> ()) { + //self.trace_state(); + + let r = T::regs(); + let s = T::state(); + + // If the Future is dropped before the end of the transmission + // we need to 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.end().clear()); + r.events_end.reset(); + + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + + // The docs don't explicitly mention any event to acknowledge the stop task + // So I guess it's the same as end + while r.events_end.read().events_end().bit_is_clear() {} + + trace!("radio drop: stopped"); + }); + + /* Config interrupt */ + // trace!("radio:enable interrupt"); + // Clear some remnant side-effects (I'm unsure if this is needed) + r.events_end.reset(); + + // Enable interrupt + r.intenset.write(|w| w.end().set()); + + compiler_fence(Ordering::SeqCst); + + // Trigger the transmission + trigger(); + // self.trace_state(); + + // On poll check if interrupt happen + poll_fn(|cx| { + s.end_waker.register(cx.waker()); + if r.events_end.read().events_end().bit_is_set() { + // trace!("radio:end"); + return core::task::Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + r.events_disabled.reset(); // ACK + + // Everthing ends fine, so we can disable the drop + drop.defuse(); + } + + /// Disable the radio. + fn disable(&mut self) { + let r = T::regs(); + + compiler_fence(Ordering::SeqCst); + // If is already disabled, do nothing + if !r.state.read().state().is_disabled() { + trace!("radio:disable"); + // Trigger the disable task + r.tasks_disable.write(|w| w.tasks_disable().set_bit()); + + // Wait until the radio is disabled + while r.events_disabled.read().events_disabled().bit_is_clear() {} + + compiler_fence(Ordering::SeqCst); + + // Acknowledge it + r.events_disabled.reset(); + } + } +} + +impl<'d, T: Instance> BleRadio for Radio<'d, T> { + type Error = Error; + + fn set_mode(&mut self, mode: Mode) { + let r = T::regs(); + r.mode.write(|w| { + w.mode().variant(match mode { + Mode::Ble1mbit => PacMode::BLE_1MBIT, + //Mode::Ble2mbit => PacMode::BLE_2MBIT, + }) + }); + + r.pcnf0.write(|w| { + w.plen().variant(match mode { + Mode::Ble1mbit => PreambleLength::_8BIT, + //Mode::Ble2mbit => PreambleLength::_16BIT, + }) + }); + } + + fn set_header_size(&mut self, header_size: HeaderSize) { + let r = T::regs(); + + let s1len: u8 = match header_size { + HeaderSize::TwoBytes => 0, + HeaderSize::ThreeBytes => 8, // bits + }; + + r.pcnf0.write(|w| unsafe { + w + // Configure S0 to 1 byte length, this will represent the Data/Adv header flags + .s0len() + .set_bit() + // 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 + .lflen() + .bits(8) + // Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE. + .s1len() + .bits(s1len) + }); + } + + fn set_channel(&mut self, channel: Channel) { + let r = T::regs(); + + r.frequency + .write(|w| unsafe { w.frequency().bits((channel.central_frequency() - 2400) as u8) }); + r.datawhiteiv + .write(|w| unsafe { w.datawhiteiv().bits(channel.whitening_init()) }); + } + + fn set_access_address(&mut self, access_address: u32) { + 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| unsafe { w.ap0().bits((access_address >> 24) as u8) }); + + // The base address is truncated from the least significant byte (because the BALEN is less than 4) + // So we need to shift the address to the right + r.base0.write(|w| unsafe { w.bits(access_address << 8) }); + + // Don't match tx address + r.txaddress.write(|w| unsafe { w.txaddress().bits(0) }); + + // Match on logical address + // For what I understand, this config only filter the packets + // by the address, so only packages send to the previous address + // will finish the reception + r.rxaddresses.write(|w| { + w.addr0() + .enabled() + .addr1() + .enabled() + .addr2() + .enabled() + .addr3() + .enabled() + .addr4() + .enabled() + }); + } + + fn set_crc_init(&mut self, crc_init: u32) { + let r = T::regs(); + + r.crcinit.write(|w| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) }); + } + + fn set_tx_power(&mut self, power_db: i8) { + let r = T::regs(); + + let tx_power: TxPower = match power_db { + 8..=i8::MAX => TxPower::POS8D_BM, + 7 => TxPower::POS7D_BM, + 6 => TxPower::POS6D_BM, + 5 => TxPower::POS5D_BM, + 4 => TxPower::POS4D_BM, + 3 => TxPower::POS3D_BM, + 1..=2 => TxPower::POS2D_BM, + -3..=0 => TxPower::_0D_BM, + -7..=-4 => TxPower::NEG4D_BM, + -11..=-8 => TxPower::NEG8D_BM, + -15..=-12 => TxPower::NEG12D_BM, + -19..=-16 => TxPower::NEG16D_BM, + -29..=-20 => TxPower::NEG20D_BM, + -39..=-30 => TxPower::NEG30D_BM, + i8::MIN..=-40 => TxPower::NEG40D_BM, + }; + + r.txpower.write(|w| w.txpower().variant(tx_power)); + } + + fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + // Because we are serializing the buffer, we should always have the buffer in RAM + slice_in_ram_or(buffer, Error::BufferNotInRAM)?; + + if buffer.len() > MAX_PDU_LENGTH { + return Err(Error::BufferTooLong); + } + + let r = T::regs(); + + // Here we are considering that the length of the packet is + // correctly set in the buffer, otherwise we will sending + // unowned regions of memory + let ptr = buffer.as_ptr(); + + // Configure the payload + r.packetptr.write(|w| unsafe { w.bits(ptr as u32) }); + + Ok(()) + } + + /// Send packet + async fn transmit(&mut self) { + let r = T::regs(); + + self.trigger_and_wait_end(move || { + // Initialize the transmission + // trace!("txen"); + r.tasks_txen.write(|w| w.tasks_txen().set_bit()); + }) + .await; + } + + /// Send packet + async fn receive(&mut self) { + let r = T::regs(); + + self.trigger_and_wait_end(move || { + // Initialize the transmission + // trace!("rxen"); + r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()); + + // Await until ready + while r.events_ready.read().events_ready().bit_is_clear() {} + + compiler_fence(Ordering::SeqCst); + + // Acknowledge it + r.events_ready.reset(); + + // trace!("radio:start"); + r.tasks_start.write(|w| w.tasks_start().set_bit()); + }) + .await; + } +} + +impl<'d, T: Instance> Drop for Radio<'d, T> { + fn drop(&mut self) { + self.disable(); + } +} diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs new file mode 100644 index 000000000..91cc2c0a7 --- /dev/null +++ b/embassy-nrf/src/radio/mod.rs @@ -0,0 +1,89 @@ +//! Integrated 2.4 GHz Radio +//! +//! The 2.4 GHz radio transceiver is compatible with multiple radio standards +//! such as 1Mbps, 2Mbps and Long Range Bluetooth Low Energy. + +#![macro_use] + +/// Bluetooth Low Energy Radio driver. +pub mod ble; + +use core::marker::PhantomData; + +use crate::{interrupt, pac, Peripheral}; + +/// Interrupt handler +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_end.read().events_end().bit_is_set() { + s.end_waker.wake(); + r.intenclr.write(|w| w.end().clear()); + } + } +} + +pub(crate) mod utils { + use super::*; + + // Check if the HFCLK is XTAL is enabled + pub fn check_xtal() { + // safe: only reading the value + let is_xtal = unsafe { + let r = &*pac::CLOCK::ptr(); + r.hfclkstat.read().src().is_xtal() + }; + assert!(is_xtal, "HFCLK must be XTAL"); + } +} + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + pub struct State { + /// end packet transmission or reception + pub end_waker: AtomicWaker, + } + impl State { + pub const fn new() -> Self { + Self { + end_waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::radio::RegisterBlock; + fn state() -> &'static State; + } +} + +macro_rules! impl_radio { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::radio::sealed::Instance for peripherals::$type { + fn regs() -> &'static pac::radio::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + + fn state() -> &'static crate::radio::sealed::State { + static STATE: crate::radio::sealed::State = crate::radio::sealed::State::new(); + &STATE + } + } + impl crate::radio::Instance for peripherals::$type { + type Interrupt = crate::interrupt::typelevel::$irq; + } + }; +} + +/// Radio peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: interrupt::typelevel::Interrupt; +} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index abb995be6..0239583cd 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-time = { version = "0.3.0", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "radio"]} embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } @@ -35,6 +35,10 @@ embedded-hal-async = { version = "1.0" } embedded-hal-bus = { version = "0.1", features = ["async"] } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" +jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel"} + +[patch.crates-io] +embassy-time = { version = "0.3.0", path = "../../embassy-time"} [profile.release] debug = 2 diff --git a/examples/nrf52840/src/bin/radio_ble_advertising.rs b/examples/nrf52840/src/bin/radio_ble_advertising.rs new file mode 100644 index 000000000..8898c2418 --- /dev/null +++ b/examples/nrf52840/src/bin/radio_ble_advertising.rs @@ -0,0 +1,42 @@ +#![no_std] +#![no_main] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_nrf::{bind_interrupts, peripherals, radio}; +use embassy_time::Timer; +use jewel::phy::Radio; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RADIO => radio::InterruptHandler; +}); + +// For a high-level API look on jewel examples +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_nrf::config::Config::default(); + config.hfclk_source = embassy_nrf::config::HfclkSource::ExternalXtal; + let p = embassy_nrf::init(config); + + info!("Starting BLE radio"); + let mut radio = radio::ble::Radio::new(p.RADIO, Irqs); + + let pdu = [ + 0x46u8, // ADV_NONCONN_IND, Random address, + 0x18, // Length of payload + 0x27, 0xdc, 0xd0, 0xe8, 0xe1, 0xff, // Adress + 0x02, 0x01, 0x06, // Flags + 0x03, 0x03, 0x09, 0x18, // Complete list of 16-bit UUIDs available + 0x0A, 0x09, // Length, Type: Device name + b'H', b'e', b'l', b'l', b'o', b'R', b'u', b's', b't', + ]; + + unwrap!(radio.set_buffer(pdu.as_ref())); + + loop { + info!("Sending packet"); + radio.transmit().await; + Timer::after_millis(500).await; + } +} From d408056a66be2183dbcde6840e69e53beb4a764b Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Wed, 7 Feb 2024 17:16:46 +0100 Subject: [PATCH 02/32] remove default on radio --- embassy-nrf/src/radio/ble.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index a5d9f447b..64428fff5 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -17,10 +17,7 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; -use jewel::phy::{ - AdvertisingChannel, Channel, ChannelTrait, HeaderSize, Mode, Radio as BleRadio, ADV_ADDRESS, ADV_CRC_INIT, - CRC_POLY, MAX_PDU_LENGTH, -}; +use jewel::phy::{Channel, ChannelTrait, HeaderSize, Mode, Radio as BleRadio, CRC_POLY, MAX_PDU_LENGTH}; use pac::radio::mode::MODE_A as PacMode; use pac::radio::pcnf0::PLEN_A as PreambleLength; // Re-export SVD variants to allow user to directly set values. @@ -145,14 +142,6 @@ impl<'d, T: Instance> Radio<'d, T> { let mut radio = Self { _p: radio }; - // set defaults - radio.set_mode(Mode::Ble1mbit); - radio.set_tx_power(0); - radio.set_header_size(HeaderSize::TwoBytes); - radio.set_access_address(ADV_ADDRESS); - radio.set_crc_init(ADV_CRC_INIT); - radio.set_channel(AdvertisingChannel::Ch39.into()); - radio } From 5f1b80d40b6d2ae1636fa46e8e5334c03adeb67d Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Wed, 7 Feb 2024 18:04:29 +0100 Subject: [PATCH 03/32] remove jewel dependency --- embassy-nrf/Cargo.toml | 4 +- embassy-nrf/src/radio/ble.rs | 160 ++++++++++-------- embassy-nrf/src/radio/mod.rs | 14 -- examples/nrf52840/Cargo.toml | 4 - .../nrf52840/src/bin/radio_ble_advertising.rs | 42 ----- 5 files changed, 86 insertions(+), 138 deletions(-) delete mode 100644 examples/nrf52840/src/bin/radio_ble_advertising.rs diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index dcdc7f313..9f35bd241 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -58,7 +58,7 @@ unstable-pac = [] gpiote = [] ## Enable radio driver -radio = ["dep:jewel"] +radio = [] ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz time-driver-rtc1 = ["_time-driver"] @@ -153,8 +153,6 @@ embedded-storage-async = "0.4.0" cfg-if = "1.0.0" document-features = "0.2.7" -jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel", optional = true } - nrf51-pac = { version = "0.12.0", optional = true } nrf52805-pac = { version = "0.12.0", optional = true } nrf52810-pac = { version = "0.12.0", optional = true } diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index 64428fff5..def941796 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -17,11 +17,10 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; -use jewel::phy::{Channel, ChannelTrait, HeaderSize, Mode, Radio as BleRadio, CRC_POLY, MAX_PDU_LENGTH}; -use pac::radio::mode::MODE_A as PacMode; +pub use pac::radio::mode::MODE_A as Mode; use pac::radio::pcnf0::PLEN_A as PreambleLength; -// Re-export SVD variants to allow user to directly set values. -pub use pac::radio::{state::STATE_A as RadioState, txpower::TXPOWER_A as TxPower}; +use pac::radio::state::STATE_A as RadioState; +pub use pac::radio::txpower::TXPOWER_A as TxPower; use crate::interrupt::typelevel::Interrupt; use crate::radio::*; @@ -51,11 +50,6 @@ impl<'d, T: Instance> Radio<'d, T> { radio: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, ) -> Self { - // From 5.4.1 of the nRF52840 Product Specification: - // > The HFXO must be running to use the RADIO or the calibration mechanism associated with the 32.768 kHz RC oscillator. - // Currently the jewel crate don't implement the calibration mechanism, so we need to ensure that the HFXO is running - utils::check_xtal(); - into_ref!(radio); let r = T::regs(); @@ -113,18 +107,6 @@ impl<'d, T: Instance> Radio<'d, T> { .three() }); - r.crcpoly.write(|w| unsafe { - // 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.crcpoly().bits(CRC_POLY & 0xFFFFFF) - }); - // The CRC initial value varies depending of the PDU type - // Ch map between 2400 MHZ .. 2500 MHz // All modes use this range r.frequency.write(|w| w.map().default()); @@ -140,9 +122,7 @@ impl<'d, T: Instance> Radio<'d, T> { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - let mut radio = Self { _p: radio }; - - radio + Self { _p: radio } } #[allow(dead_code)] @@ -186,7 +166,6 @@ impl<'d, T: Instance> Radio<'d, T> { trace!("radio drop: stopped"); }); - /* Config interrupt */ // trace!("radio:enable interrupt"); // Clear some remnant side-effects (I'm unsure if this is needed) r.events_end.reset(); @@ -238,34 +217,34 @@ impl<'d, T: Instance> Radio<'d, T> { r.events_disabled.reset(); } } -} -impl<'d, T: Instance> BleRadio for Radio<'d, T> { - type Error = Error; - - fn set_mode(&mut self, mode: Mode) { + /// Set the radio mode + /// + /// The radio must be disabled before calling this function + pub fn set_mode(&mut self, mode: Mode) { let r = T::regs(); - r.mode.write(|w| { - w.mode().variant(match mode { - Mode::Ble1mbit => PacMode::BLE_1MBIT, - //Mode::Ble2mbit => PacMode::BLE_2MBIT, - }) - }); + r.mode.write(|w| w.mode().variant(mode)); r.pcnf0.write(|w| { w.plen().variant(match mode { - Mode::Ble1mbit => PreambleLength::_8BIT, - //Mode::Ble2mbit => PreambleLength::_16BIT, + Mode::BLE_1MBIT => PreambleLength::_8BIT, + Mode::BLE_2MBIT => PreambleLength::_16BIT, + Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE, + _ => unimplemented!(), }) }); } - fn set_header_size(&mut self, header_size: HeaderSize) { + /// Set the header size changing the S1 field + /// + /// The radio must be disabled before calling this function + pub fn set_header_expansion(&mut self, use_s1_field: bool) { let r = T::regs(); - let s1len: u8 = match header_size { - HeaderSize::TwoBytes => 0, - HeaderSize::ThreeBytes => 8, // bits + // s1 len in bits + let s1len: u8 = match use_s1_field { + false => 0, + true => 8, }; r.pcnf0.write(|w| unsafe { @@ -283,16 +262,36 @@ impl<'d, T: Instance> BleRadio for Radio<'d, T> { }); } - fn set_channel(&mut self, channel: Channel) { + /// 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) { + let r = T::regs(); + + r.datawhiteiv.write(|w| unsafe { w.datawhiteiv().bits(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 + pub fn set_frequency(&mut self, frequency: u32) { + assert!(2400 <= frequency && frequency <= 2500); let r = T::regs(); r.frequency - .write(|w| unsafe { w.frequency().bits((channel.central_frequency() - 2400) as u8) }); - r.datawhiteiv - .write(|w| unsafe { w.datawhiteiv().bits(channel.whitening_init()) }); + .write(|w| unsafe { w.frequency().bits((frequency - 2400) as u8) }); } - fn set_access_address(&mut self, access_address: u32) { + /// 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) { let r = T::regs(); // Configure logical address @@ -327,44 +326,55 @@ impl<'d, T: Instance> BleRadio for Radio<'d, T> { }); } - fn set_crc_init(&mut self, crc_init: u32) { + /// 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) { + let r = T::regs(); + + r.crcpoly.write(|w| unsafe { + // 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.crcpoly().bits(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) { let r = T::regs(); r.crcinit.write(|w| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) }); } - fn set_tx_power(&mut self, power_db: i8) { + /// Set the radio tx power + /// + /// The radio must be disabled before calling this function + pub fn set_tx_power(&mut self, tx_power: TxPower) { let r = T::regs(); - let tx_power: TxPower = match power_db { - 8..=i8::MAX => TxPower::POS8D_BM, - 7 => TxPower::POS7D_BM, - 6 => TxPower::POS6D_BM, - 5 => TxPower::POS5D_BM, - 4 => TxPower::POS4D_BM, - 3 => TxPower::POS3D_BM, - 1..=2 => TxPower::POS2D_BM, - -3..=0 => TxPower::_0D_BM, - -7..=-4 => TxPower::NEG4D_BM, - -11..=-8 => TxPower::NEG8D_BM, - -15..=-12 => TxPower::NEG12D_BM, - -19..=-16 => TxPower::NEG16D_BM, - -29..=-20 => TxPower::NEG20D_BM, - -39..=-30 => TxPower::NEG30D_BM, - i8::MIN..=-40 => TxPower::NEG40D_BM, - }; - r.txpower.write(|w| w.txpower().variant(tx_power)); } - fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + /// 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. + pub fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { // Because we are serializing the buffer, we should always have the buffer in RAM slice_in_ram_or(buffer, Error::BufferNotInRAM)?; - if buffer.len() > MAX_PDU_LENGTH { - return Err(Error::BufferTooLong); - } - let r = T::regs(); // Here we are considering that the length of the packet is @@ -379,7 +389,7 @@ impl<'d, T: Instance> BleRadio for Radio<'d, T> { } /// Send packet - async fn transmit(&mut self) { + pub async fn transmit(&mut self) { let r = T::regs(); self.trigger_and_wait_end(move || { @@ -390,8 +400,8 @@ impl<'d, T: Instance> BleRadio for Radio<'d, T> { .await; } - /// Send packet - async fn receive(&mut self) { + /// Receive packet + pub async fn receive(&mut self) { let r = T::regs(); self.trigger_and_wait_end(move || { diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 91cc2c0a7..03f967f87 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -29,20 +29,6 @@ impl interrupt::typelevel::Handler for InterruptHandl } } -pub(crate) mod utils { - use super::*; - - // Check if the HFCLK is XTAL is enabled - pub fn check_xtal() { - // safe: only reading the value - let is_xtal = unsafe { - let r = &*pac::CLOCK::ptr(); - r.hfclkstat.read().src().is_xtal() - }; - assert!(is_xtal, "HFCLK must be XTAL"); - } -} - pub(crate) mod sealed { use embassy_sync::waitqueue::AtomicWaker; diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 0239583cd..78dabe347 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -35,10 +35,6 @@ embedded-hal-async = { version = "1.0" } embedded-hal-bus = { version = "0.1", features = ["async"] } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" -jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel"} - -[patch.crates-io] -embassy-time = { version = "0.3.0", path = "../../embassy-time"} [profile.release] debug = 2 diff --git a/examples/nrf52840/src/bin/radio_ble_advertising.rs b/examples/nrf52840/src/bin/radio_ble_advertising.rs deleted file mode 100644 index 8898c2418..000000000 --- a/examples/nrf52840/src/bin/radio_ble_advertising.rs +++ /dev/null @@ -1,42 +0,0 @@ -#![no_std] -#![no_main] - -use defmt::{info, unwrap}; -use embassy_executor::Spawner; -use embassy_nrf::{bind_interrupts, peripherals, radio}; -use embassy_time::Timer; -use jewel::phy::Radio; -use {defmt_rtt as _, panic_probe as _}; - -bind_interrupts!(struct Irqs { - RADIO => radio::InterruptHandler; -}); - -// For a high-level API look on jewel examples -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let mut config = embassy_nrf::config::Config::default(); - config.hfclk_source = embassy_nrf::config::HfclkSource::ExternalXtal; - let p = embassy_nrf::init(config); - - info!("Starting BLE radio"); - let mut radio = radio::ble::Radio::new(p.RADIO, Irqs); - - let pdu = [ - 0x46u8, // ADV_NONCONN_IND, Random address, - 0x18, // Length of payload - 0x27, 0xdc, 0xd0, 0xe8, 0xe1, 0xff, // Adress - 0x02, 0x01, 0x06, // Flags - 0x03, 0x03, 0x09, 0x18, // Complete list of 16-bit UUIDs available - 0x0A, 0x09, // Length, Type: Device name - b'H', b'e', b'l', b'l', b'o', b'R', b'u', b's', b't', - ]; - - unwrap!(radio.set_buffer(pdu.as_ref())); - - loop { - info!("Sending packet"); - radio.transmit().await; - Timer::after_millis(500).await; - } -} From add78943146cb929d51830c1f1c74c51dd3a5d57 Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Wed, 7 Feb 2024 18:09:10 +0100 Subject: [PATCH 04/32] remove radio feature --- embassy-nrf/Cargo.toml | 3 --- embassy-nrf/src/chips/nrf52805.rs | 1 - embassy-nrf/src/chips/nrf52810.rs | 1 - embassy-nrf/src/chips/nrf52811.rs | 1 - embassy-nrf/src/chips/nrf52820.rs | 1 - embassy-nrf/src/chips/nrf52832.rs | 1 - embassy-nrf/src/chips/nrf52833.rs | 1 - embassy-nrf/src/chips/nrf52840.rs | 1 - embassy-nrf/src/chips/nrf5340_net.rs | 1 - embassy-nrf/src/lib.rs | 13 ++++++++++--- examples/nrf52840/Cargo.toml | 4 ++-- 11 files changed, 12 insertions(+), 16 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 9f35bd241..7e161df9b 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -57,9 +57,6 @@ unstable-pac = [] ## Enable GPIO tasks and events gpiote = [] -## Enable radio driver -radio = [] - ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz time-driver-rtc1 = ["_time-driver"] diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index b97c85f9e..c172dd107 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -212,7 +212,6 @@ impl_ppi_channel!(PPI_CH31, 31 => static); impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_05, ANALOG_INPUT3); -#[cfg(feature = "radio")] impl_radio!(RADIO, RADIO, RADIO); embassy_hal_internal::interrupt_mod!( diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 03548d03f..c607586db 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -238,7 +238,6 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -#[cfg(feature = "radio")] impl_radio!(RADIO, RADIO, RADIO); embassy_hal_internal::interrupt_mod!( diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 992fbd129..5f70365b4 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -240,7 +240,6 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -#[cfg(feature = "radio")] impl_radio!(RADIO, RADIO, RADIO); embassy_hal_internal::interrupt_mod!( diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index f241f4ea3..82d097407 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -227,7 +227,6 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -#[cfg(feature = "radio")] impl_radio!(RADIO, RADIO, RADIO); embassy_hal_internal::interrupt_mod!( diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 6bbdd9a63..67b32fe5f 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -267,7 +267,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -#[cfg(feature = "radio")] impl_radio!(RADIO, RADIO, RADIO); embassy_hal_internal::interrupt_mod!( diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index e137e4dc6..20f14e2d6 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -309,7 +309,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -#[cfg(feature = "radio")] impl_radio!(RADIO, RADIO, RADIO); embassy_hal_internal::interrupt_mod!( diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 2d805f871..d3272b2e8 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -314,7 +314,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -#[cfg(feature = "radio")] impl_radio!(RADIO, RADIO, RADIO); embassy_hal_internal::interrupt_mod!( diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 3248bde52..65e8f9653 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -348,7 +348,6 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); -#[cfg(feature = "radio")] impl_radio!(RADIO, RADIO, RADIO); embassy_hal_internal::interrupt_mod!( diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 961928d11..0f64f30f2 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -46,10 +46,17 @@ pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -#[cfg(feature = "radio")] +#[cfg(any( + feature = "nrf52805", + feature = "nrf52810", + feature = "nrf52811", + feature = "nrf52820", + feature = "nrf52832", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340-net" +))] pub mod radio; -#[cfg(all(feature = "radio", feature = "_nrf9160"))] -compile_error!("feature `radio` is not valid for nRF91 series chips."); #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] pub mod i2s; diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 78dabe347..abb995be6 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.3.0", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "radio"]} +embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } From 3ad45655ecea36d54f8024b5fbdad461dc07ed69 Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Wed, 7 Feb 2024 18:22:47 +0100 Subject: [PATCH 05/32] ci rerun --- embassy-nrf/src/radio/ble.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index def941796..369b49c55 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -35,7 +35,7 @@ pub enum Error { BufferTooLong, /// Buffer was to short. BufferTooShort, - /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + /// The buffer is not in data RAM. It is most likely in flash, and nRF's DMA cannot access flash. BufferNotInRAM, } From ea8bfb4f382bddda2ca5fbe5ce4c595cd0421bd1 Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Wed, 7 Feb 2024 18:25:06 +0100 Subject: [PATCH 06/32] remove some supports --- embassy-nrf/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 0f64f30f2..767293230 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -47,11 +47,11 @@ pub mod gpio; pub mod gpiote; #[cfg(any( - feature = "nrf52805", - feature = "nrf52810", + //feature = "nrf52805", + //feature = "nrf52810", feature = "nrf52811", feature = "nrf52820", - feature = "nrf52832", + //feature = "nrf52832", feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340-net" From 9527d1d934a46a50bfc8f26bd942c6f6e5c5b31b Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Wed, 7 Feb 2024 18:31:05 +0100 Subject: [PATCH 07/32] remove radio implementation on chips not tested --- embassy-nrf/src/chips/nrf52805.rs | 5 ----- embassy-nrf/src/chips/nrf52810.rs | 5 ----- embassy-nrf/src/chips/nrf52811.rs | 5 ----- embassy-nrf/src/chips/nrf52820.rs | 5 ----- embassy-nrf/src/chips/nrf52832.rs | 5 ----- embassy-nrf/src/chips/nrf52833.rs | 5 ----- embassy-nrf/src/chips/nrf5340_net.rs | 5 ----- embassy-nrf/src/lib.rs | 11 +---------- 8 files changed, 1 insertion(+), 45 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index c172dd107..624d6613d 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -129,9 +129,6 @@ embassy_hal_internal::peripherals! { // QDEC QDEC, - - // RADIO - RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -212,8 +209,6 @@ impl_ppi_channel!(PPI_CH31, 31 => static); impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_05, ANALOG_INPUT3); -impl_radio!(RADIO, RADIO, RADIO); - embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index c607586db..002feab3b 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -135,9 +135,6 @@ embassy_hal_internal::peripherals! { // PDM PDM, - - // Radio - RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -238,8 +235,6 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -impl_radio!(RADIO, RADIO, RADIO); - embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 5f70365b4..5952907f8 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -135,9 +135,6 @@ embassy_hal_internal::peripherals! { // PDM PDM, - - // Radio - RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -240,8 +237,6 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -impl_radio!(RADIO, RADIO, RADIO); - embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 82d097407..c2f792cb9 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -130,9 +130,6 @@ embassy_hal_internal::peripherals! { // QDEC QDEC, - - // Radio - RADIO, } impl_usb!(USBD, USBD, USBD); @@ -227,8 +224,6 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_radio!(RADIO, RADIO, RADIO); - embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 67b32fe5f..65d52364d 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -150,9 +150,6 @@ embassy_hal_internal::peripherals! { // PDM PDM, - - // Radio - RADIO, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -267,8 +264,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -impl_radio!(RADIO, RADIO, RADIO); - embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 20f14e2d6..7c9b66d69 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -170,9 +170,6 @@ embassy_hal_internal::peripherals! { // I2S I2S, - - // Radio - RADIO, } impl_usb!(USBD, USBD, USBD); @@ -309,8 +306,6 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -impl_radio!(RADIO, RADIO, RADIO); - embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 65e8f9653..a7cf82872 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -248,9 +248,6 @@ embassy_hal_internal::peripherals! { P1_13, P1_14, P1_15, - - // Radio - RADIO, } impl_uarte!(SERIAL0, UARTE0, SERIAL0); @@ -348,8 +345,6 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); -impl_radio!(RADIO, RADIO, RADIO); - embassy_hal_internal::interrupt_mod!( CLOCK_POWER, RADIO, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 767293230..5dba6f975 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -46,16 +46,7 @@ pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -#[cfg(any( - //feature = "nrf52805", - //feature = "nrf52810", - feature = "nrf52811", - feature = "nrf52820", - //feature = "nrf52832", - feature = "nrf52833", - feature = "nrf52840", - feature = "_nrf5340-net" -))] +#[cfg(any(feature = "nrf52840"))] // needs to be tested on other chips pub mod radio; #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] From 802bdd1af86829482d6b847f73f02e6973bb68ea Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Sat, 10 Feb 2024 08:09:08 +0100 Subject: [PATCH 08/32] remove first person comments and assert disable state when it's necessary --- embassy-nrf/src/radio/ble.rs | 201 ++++++++++++++++++----------------- 1 file changed, 106 insertions(+), 95 deletions(-) diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index 369b49c55..b0d374579 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -64,13 +64,13 @@ impl<'d, T: Instance> Radio<'d, T> { // 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, I'm setting the max length to the maximum value - // and I'm using only the length field to truncate the payload + // 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 .maxlen() .bits(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 + // The base address is truncated from the least significant byte if the BALEN is less than 4 // // BLE address is always 4 bytes long .balen() @@ -92,14 +92,14 @@ impl<'d, T: Instance> Radio<'d, T> { // Before whitening or de-whitening, the shift register should be // initialized based on the channel index. .whiteen() - .set_bit() // Enable whitening + .set_bit() }); // 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). - // So here we skip the address field + // It skips the address field w.skipaddr() .skip() // In BLE 24-bit CRC = 3 bytes @@ -125,11 +125,18 @@ impl<'d, T: Instance> Radio<'d, T> { Self { _p: radio } } + fn state(&self) -> RadioState { + match T::regs().state.read().state().variant() { + Ok(s) => s, + None => unreachable!(), + } + } + #[allow(dead_code)] fn trace_state(&self) { let r = T::regs(); - match r.state.read().state().variant().unwrap() { + match self.state() { RadioState::DISABLED => trace!("radio:state:DISABLED"), RadioState::RX_RU => trace!("radio:state:RX_RU"), RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"), @@ -142,86 +149,12 @@ impl<'d, T: Instance> Radio<'d, T> { } } - async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce() -> ()) { - //self.trace_state(); - - let r = T::regs(); - let s = T::state(); - - // If the Future is dropped before the end of the transmission - // we need to 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.end().clear()); - r.events_end.reset(); - - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); - - // The docs don't explicitly mention any event to acknowledge the stop task - // So I guess it's the same as end - while r.events_end.read().events_end().bit_is_clear() {} - - trace!("radio drop: stopped"); - }); - - // trace!("radio:enable interrupt"); - // Clear some remnant side-effects (I'm unsure if this is needed) - r.events_end.reset(); - - // Enable interrupt - r.intenset.write(|w| w.end().set()); - - compiler_fence(Ordering::SeqCst); - - // Trigger the transmission - trigger(); - // self.trace_state(); - - // On poll check if interrupt happen - poll_fn(|cx| { - s.end_waker.register(cx.waker()); - if r.events_end.read().events_end().bit_is_set() { - // trace!("radio:end"); - return core::task::Poll::Ready(()); - } - Poll::Pending - }) - .await; - - compiler_fence(Ordering::SeqCst); - r.events_disabled.reset(); // ACK - - // Everthing ends fine, so we can disable the drop - drop.defuse(); - } - - /// Disable the radio. - fn disable(&mut self) { - let r = T::regs(); - - compiler_fence(Ordering::SeqCst); - // If is already disabled, do nothing - if !r.state.read().state().is_disabled() { - trace!("radio:disable"); - // Trigger the disable task - r.tasks_disable.write(|w| w.tasks_disable().set_bit()); - - // Wait until the radio is disabled - while r.events_disabled.read().events_disabled().bit_is_clear() {} - - compiler_fence(Ordering::SeqCst); - - // Acknowledge it - r.events_disabled.reset(); - } - } - /// 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.mode().variant(mode)); @@ -235,10 +168,12 @@ impl<'d, T: Instance> Radio<'d, T> { }); } - /// Set the header size changing the S1 field + /// 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 @@ -268,6 +203,8 @@ impl<'d, T: Instance> Radio<'d, T> { /// /// 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| unsafe { w.datawhiteiv().bits(whitening_init) }); @@ -276,9 +213,11 @@ impl<'d, T: Instance> Radio<'d, T> { /// Set the central frequency to be used /// It should be in the range 2400..2500 /// - /// The radio must be disabled before calling this function + /// [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 <= frequency && frequency <= 2500); + let r = T::regs(); r.frequency @@ -292,6 +231,8 @@ impl<'d, T: Instance> Radio<'d, T> { /// /// 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 @@ -309,9 +250,9 @@ impl<'d, T: Instance> Radio<'d, T> { r.txaddress.write(|w| unsafe { w.txaddress().bits(0) }); // Match on logical address - // For what I understand, this config only filter the packets - // by the address, so only packages send to the previous address - // will finish the reception + // 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.addr0() .enabled() @@ -331,6 +272,8 @@ impl<'d, T: Instance> Radio<'d, T> { /// /// 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| unsafe { @@ -351,6 +294,8 @@ impl<'d, T: Instance> Radio<'d, T> { /// /// 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| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) }); @@ -360,6 +305,8 @@ impl<'d, T: Instance> Radio<'d, T> { /// /// 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.txpower().variant(tx_power)); @@ -408,19 +355,83 @@ impl<'d, T: Instance> Radio<'d, T> { // Initialize the transmission // trace!("rxen"); r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()); + }) + .await; + } - // Await until ready - while r.events_ready.read().events_ready().bit_is_clear() {} + async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) { + //self.trace_state(); + + let r = T::regs(); + let s = T::state(); + + // If the Future is dropped before the end of the transmission + // we need to 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.end().clear()); + r.events_end.reset(); + + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + + // The docs don't explicitly mention any event to acknowledge the stop task + while r.events_end.read().events_end().bit_is_clear() {} + + trace!("radio drop: stopped"); + }); + + // trace!("radio:enable interrupt"); + // Clear some remnant side-effects (TODO: check if this is necessary) + r.events_end.reset(); + + // Enable interrupt + r.intenset.write(|w| w.end().set()); + + compiler_fence(Ordering::SeqCst); + + // Trigger the transmission + trigger(); + // self.trace_state(); + + // On poll check if interrupt happen + poll_fn(|cx| { + s.end_waker.register(cx.waker()); + if r.events_end.read().events_end().bit_is_set() { + // trace!("radio:end"); + return core::task::Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + r.events_disabled.reset(); // ACK + + // Everthing ends fine, so we can 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(|w| w.tasks_disable().set_bit()); + + // Wait until the radio is disabled + while r.events_disabled.read().events_disabled().bit_is_clear() {} compiler_fence(Ordering::SeqCst); // Acknowledge it - r.events_ready.reset(); - - // trace!("radio:start"); - r.tasks_start.write(|w| w.tasks_start().set_bit()); - }) - .await; + r.events_disabled.reset(); + } } } From 37739284253699d3f087fd8e1f9282090e6aaf95 Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Sat, 10 Feb 2024 08:18:32 +0100 Subject: [PATCH 09/32] apply clippy sugestions --- embassy-nrf/src/radio/ble.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index b0d374579..c74676f58 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -127,15 +127,13 @@ impl<'d, T: Instance> Radio<'d, T> { fn state(&self) -> RadioState { match T::regs().state.read().state().variant() { - Ok(s) => s, + Some(s) => s, None => unreachable!(), } } #[allow(dead_code)] fn trace_state(&self) { - let r = T::regs(); - match self.state() { RadioState::DISABLED => trace!("radio:state:DISABLED"), RadioState::RX_RU => trace!("radio:state:RX_RU"), @@ -216,7 +214,7 @@ impl<'d, T: Instance> Radio<'d, T> { /// [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 <= frequency && frequency <= 2500); + assert!((2400..=2500).contains(&frequency)); let r = T::regs(); From 40282c2666f8b8bda5785b30a62ab352ff8e7498 Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Sat, 10 Feb 2024 08:49:14 +0100 Subject: [PATCH 10/32] add buffer input on transmit/receive --- embassy-nrf/src/radio/ble.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index c74676f58..7718cfe14 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -316,7 +316,7 @@ impl<'d, T: Instance> Radio<'d, T> { /// 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. - pub fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { + fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { // Because we are serializing the buffer, we should always have the buffer in RAM slice_in_ram_or(buffer, Error::BufferNotInRAM)?; @@ -334,27 +334,33 @@ impl<'d, T: Instance> Radio<'d, T> { } /// Send packet - pub async fn transmit(&mut self) { - let r = T::regs(); + 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(|w| w.tasks_txen().set_bit()); }) .await; + + Ok(()) } /// Receive packet - pub async fn receive(&mut self) { - let r = T::regs(); + 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(|w| w.tasks_rxen().set_bit()); }) .await; + + Ok(()) } async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) { From fdb15b205424e71107f18b43553d43691f3d8fb7 Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Sat, 10 Feb 2024 08:58:31 +0100 Subject: [PATCH 11/32] add comments about buffer unsound --- embassy-nrf/src/radio/ble.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index 7718cfe14..ba9ac5f2e 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -334,6 +334,8 @@ impl<'d, T: Instance> Radio<'d, T> { } /// 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)?; @@ -349,6 +351,8 @@ impl<'d, T: Instance> Radio<'d, T> { } /// 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)?; From b4f0f575388df405e4e7330f2e8cdac30c4d60d9 Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Mon, 12 Feb 2024 13:21:01 +0100 Subject: [PATCH 12/32] remove first person comments --- embassy-nrf/src/lib.rs | 3 ++- embassy-nrf/src/radio/ble.rs | 22 +++++----------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 5dba6f975..04a6293a4 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -46,7 +46,8 @@ pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -#[cfg(any(feature = "nrf52840"))] // needs to be tested on other chips +// TODO: tested on other chips +#[cfg(any(feature = "nrf52840"))] pub mod radio; #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index ba9ac5f2e..81ef96b65 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -1,15 +1,4 @@ //! Radio driver implementation focused on Bluetooth Low-Energy transmission. -//! -//! The radio can calculate the CRC, perform data whitening, -//! automatically send the right preamble. -//! Most of the configuration is done automatically when you choose the mode and this driver. -//! -//! Some configuration can just be done when de device is disabled, -//! and the configuration varies depending if is a transmitter or a receiver. -//! Because of that we have a state machine to keep track of the state of the radio. -//! The Radio is the disable radio which configure the common parameters between -//! the bluetooth protocols, like the package format, the CRC and the whitening. -//! The TxRadio radio enable and configured as a transmitter with the specific parameters. use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; @@ -241,7 +230,7 @@ impl<'d, T: Instance> Radio<'d, T> { .write(|w| unsafe { w.ap0().bits((access_address >> 24) as u8) }); // The base address is truncated from the least significant byte (because the BALEN is less than 4) - // So we need to shift the address to the right + // So it shifts the address to the right r.base0.write(|w| unsafe { w.bits(access_address << 8) }); // Don't match tx address @@ -317,13 +306,12 @@ impl<'d, T: Instance> Radio<'d, T> { /// 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> { - // Because we are serializing the buffer, we should always have the buffer in RAM slice_in_ram_or(buffer, Error::BufferNotInRAM)?; let r = T::regs(); - // Here we are considering that the length of the packet is - // correctly set in the buffer, otherwise we will sending + // 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(); @@ -374,7 +362,7 @@ impl<'d, T: Instance> Radio<'d, T> { let s = T::state(); // If the Future is dropped before the end of the transmission - // we need to disable the interrupt and stop the transmission + // it disable the interrupt and stop the transmission // to keep the state consistent let drop = OnDrop::new(|| { trace!("radio drop: stopping"); @@ -417,7 +405,7 @@ impl<'d, T: Instance> Radio<'d, T> { compiler_fence(Ordering::SeqCst); r.events_disabled.reset(); // ACK - // Everthing ends fine, so we can disable the drop + // Everthing ends fine, so it disable the drop drop.defuse(); } From a11e3146f8f5c00c770c6ef913bca676bbc9a685 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Thu, 22 Feb 2024 16:55:00 +0000 Subject: [PATCH 13/32] stm32: time_driver: allow use of TIM1 for driver --- embassy-stm32/Cargo.toml | 2 ++ embassy-stm32/src/time_driver.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d585d2cd6..0defde033 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -130,6 +130,8 @@ _time-driver = ["dep:embassy-time-driver", "time"] ## Use any time driver time-driver-any = ["_time-driver"] +## Use TIM1 as time driver +time-driver-tim1 = ["_time-driver"] ## Use TIM2 as time driver time-driver-tim2 = ["_time-driver"] ## Use TIM3 as time driver diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index a1f54307d..4129e4f30 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -38,7 +38,7 @@ cfg_if::cfg_if! { } } -#[cfg(time_drvier_tim1)] +#[cfg(time_driver_tim1)] type T = peripherals::TIM1; #[cfg(time_driver_tim2)] type T = peripherals::TIM2; From 44534abf32f556533c1183640e87bc2d45161d54 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Thu, 22 Feb 2024 17:26:44 +0000 Subject: [PATCH 14/32] stm32: sync available TIMs in Cargo.toml, build.rs --- embassy-stm32/Cargo.toml | 8 ++++++++ embassy-stm32/build.rs | 1 + 2 files changed, 9 insertions(+) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0defde033..c4b3ece1a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -140,6 +140,8 @@ time-driver-tim3 = ["_time-driver"] time-driver-tim4 = ["_time-driver"] ## Use TIM5 as time driver time-driver-tim5 = ["_time-driver"] +## Use TIM8 as time driver +time-driver-tim8 = ["_time-driver"] ## Use TIM9 as time driver time-driver-tim9 = ["_time-driver"] ## Use TIM11 as time driver @@ -148,10 +150,16 @@ time-driver-tim11 = ["_time-driver"] time-driver-tim12 = ["_time-driver"] ## Use TIM15 as time driver time-driver-tim15 = ["_time-driver"] +## Use TIM20 as time driver +time-driver-tim20 = ["_time-driver"] ## Use TIM21 as time driver time-driver-tim21 = ["_time-driver"] ## Use TIM22 as time driver time-driver-tim22 = ["_time-driver"] +## Use TIM23 as time driver +time-driver-tim23 = ["_time-driver"] +## Use TIM24 as time driver +time-driver-tim24 = ["_time-driver"] #! ## Analog Switch Pins (Pxy_C) on STM32H7 series diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 068a74733..75a2055eb 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -189,6 +189,7 @@ fn main() { Some("tim5") => "TIM5", Some("tim8") => "TIM8", Some("tim9") => "TIM9", + Some("tim11") => "TIM11", Some("tim12") => "TIM12", Some("tim15") => "TIM15", Some("tim20") => "TIM20", From 86ccf0bc3e0a9908402405aebde6755df0e1b4f6 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Thu, 22 Feb 2024 17:42:08 +0000 Subject: [PATCH 15/32] stm32: remove TIM11 as time driver candidate (only 1 CC channel) --- embassy-stm32/Cargo.toml | 2 -- embassy-stm32/build.rs | 1 - embassy-stm32/src/time_driver.rs | 2 -- 3 files changed, 5 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index c4b3ece1a..abcc8ac4e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -144,8 +144,6 @@ time-driver-tim5 = ["_time-driver"] time-driver-tim8 = ["_time-driver"] ## Use TIM9 as time driver time-driver-tim9 = ["_time-driver"] -## Use TIM11 as time driver -time-driver-tim11 = ["_time-driver"] ## Use TIM12 as time driver time-driver-tim12 = ["_time-driver"] ## Use TIM15 as time driver diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 75a2055eb..068a74733 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -189,7 +189,6 @@ fn main() { Some("tim5") => "TIM5", Some("tim8") => "TIM8", Some("tim9") => "TIM9", - Some("tim11") => "TIM11", Some("tim12") => "TIM12", Some("tim15") => "TIM15", Some("tim20") => "TIM20", diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 4129e4f30..8e4e606a4 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -52,8 +52,6 @@ type T = peripherals::TIM5; type T = peripherals::TIM8; #[cfg(time_driver_tim9)] type T = peripherals::TIM9; -#[cfg(time_driver_tim11)] -type T = peripherals::TIM11; #[cfg(time_driver_tim12)] type T = peripherals::TIM12; #[cfg(time_driver_tim15)] From a0afd378f4f3bb0032a1f845c2883334b433dd79 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Mon, 26 Feb 2024 17:28:29 +0800 Subject: [PATCH 16/32] update usbd-hid to latest Signed-off-by: Haobo Gu --- embassy-usb/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 1b31b6145..fe5e36b32 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -56,5 +56,5 @@ log = { version = "0.4.14", optional = true } heapless = "0.8" # for HID -usbd-hid = { version = "0.6.0", optional = true } +usbd-hid = { version = "0.7.0", optional = true } ssmarshal = { version = "1.0", default-features = false, optional = true } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index abb995be6..4ab5c7b7c 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -28,7 +28,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.1" -usbd-hid = "0.6.0" +usbd-hid = "0.7.0" serde = { version = "1.0.136", default-features = false } embedded-hal = { version = "1.0" } embedded-hal-async = { version = "1.0" } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 56b9c8018..24aa560d5 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -24,7 +24,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.1" -usbd-hid = "0.6.0" +usbd-hid = "0.7.0" serde = { version = "1.0.136", default-features = false } [profile.release] diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index e1092dba4..585349506 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -36,7 +36,7 @@ display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.3.0" heapless = "0.8" -usbd-hid = "0.6.1" +usbd-hid = "0.7.0" embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = "1.0" diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 74ccfa3b0..64c749b9b 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -12,7 +12,7 @@ embassy-executor = { version = "0.5.0", path = "../../embassy-executor", feature embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -usbd-hid = "0.6.0" +usbd-hid = "0.7.0" defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 0c6beb72c..5bcee178f 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -13,7 +13,7 @@ embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["de embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -usbd-hid = "0.6.0" +usbd-hid = "0.7.0" defmt = "0.3" defmt-rtt = "0.4" From 5c45723777dbd7d9d23188f5b5a2adc417e29292 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Fri, 23 Feb 2024 14:33:25 +0000 Subject: [PATCH 17/32] stm32: timers: use TIMx_CC interrupt source for advanced timers fixes (hopefully) time driver when using TIM1/8/20 --- embassy-stm32/src/time_driver.rs | 34 ++++++++++++++++++++++++++++++++ embassy-stm32/src/timer/mod.rs | 19 ++++++++++++++---- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 8e4e606a4..37b2e7526 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -16,6 +16,8 @@ use crate::pac::timer::vals; use crate::rcc::sealed::RccPeripheral; #[cfg(feature = "low-power")] use crate::rtc::Rtc; +#[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))] +use crate::timer::sealed::AdvancedControlInstance; use crate::timer::sealed::{CoreInstance, GeneralPurpose16bitInstance as Instance}; use crate::{interrupt, peripherals}; @@ -76,6 +78,14 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; + (TIM1, timer, $block:ident, CC, $irq:ident) => { + #[cfg(time_driver_tim1)] + #[cfg(feature = "rt")] + #[interrupt] + fn $irq() { + DRIVER.on_interrupt() + } + }; (TIM2, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim2)] #[cfg(feature = "rt")] @@ -116,6 +126,14 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; + (TIM8, timer, $block:ident, CC, $irq:ident) => { + #[cfg(time_driver_tim8)] + #[cfg(feature = "rt")] + #[interrupt] + fn $irq() { + DRIVER.on_interrupt() + } + }; (TIM9, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim9)] #[cfg(feature = "rt")] @@ -148,6 +166,14 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; + (TIM20, timer, $block:ident, CC, $irq:ident) => { + #[cfg(time_driver_tim20)] + #[cfg(feature = "rt")] + #[interrupt] + fn $irq() { + DRIVER.on_interrupt() + } + }; (TIM21, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim21)] #[cfg(feature = "rt")] @@ -281,6 +307,14 @@ impl RtcDriver { ::Interrupt::unpend(); unsafe { ::Interrupt::enable() }; + #[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))] + { + ::CaptureCompareInterrupt::unpend(); + unsafe { + ::CaptureCompareInterrupt::enable(); + } + } + r.cr1().modify(|w| w.set_cen(true)); } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 9397da2a1..c8f580101 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -464,6 +464,9 @@ pub(crate) mod sealed { pub trait AdvancedControlInstance: GeneralPurpose2ChannelComplementaryInstance + GeneralPurpose16bitInstance { + /// Capture compare interrupt for this timer. + type CaptureCompareInterrupt: interrupt::typelevel::Interrupt; + /// Get access to the advanced timer registers. fn regs_advanced() -> crate::pac::timer::TimAdv; @@ -831,8 +834,10 @@ macro_rules! impl_2ch_cmp_timer { #[allow(unused)] macro_rules! impl_adv_timer { - ($inst:ident) => { + ($inst:ident, $irq:ident) => { impl sealed::AdvancedControlInstance for crate::peripherals::$inst { + type CaptureCompareInterrupt = crate::interrupt::typelevel::$irq; + fn regs_advanced() -> crate::pac::timer::TimAdv { unsafe { crate::pac::timer::TimAdv::from_ptr(crate::pac::$inst.as_ptr()) } } @@ -905,11 +910,13 @@ foreach_interrupt! { impl_gp16_timer!($inst); impl_1ch_cmp_timer!($inst); impl_2ch_cmp_timer!($inst); - impl_adv_timer!($inst); impl BasicInstance for crate::peripherals::$inst {} impl CaptureCompare16bitInstance for crate::peripherals::$inst {} impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} }; + ($inst:ident, timer, TIM_1CH_CMP, CC, $irq:ident) => { + impl_adv_timer!($inst, $irq); + }; ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { @@ -921,11 +928,13 @@ foreach_interrupt! { impl_gp16_timer!($inst); impl_1ch_cmp_timer!($inst); impl_2ch_cmp_timer!($inst); - impl_adv_timer!($inst); impl BasicInstance for crate::peripherals::$inst {} impl CaptureCompare16bitInstance for crate::peripherals::$inst {} impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} }; + ($inst:ident, timer, TIM_2CH_CMP, CC, $irq:ident) => { + impl_adv_timer!($inst, $irq); + }; ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { @@ -937,11 +946,13 @@ foreach_interrupt! { impl_gp16_timer!($inst); impl_1ch_cmp_timer!($inst); impl_2ch_cmp_timer!($inst); - impl_adv_timer!($inst); impl BasicInstance for crate::peripherals::$inst {} impl CaptureCompare16bitInstance for crate::peripherals::$inst {} impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} }; + ($inst:ident, timer, TIM_ADV, CC, $irq:ident) => { + impl_adv_timer!($inst, $irq); + }; } // Update Event trigger DMA for every timer From bf44adc4bcd4725f19ec99428acd70700b48e8cd Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Tue, 27 Feb 2024 14:20:58 +0800 Subject: [PATCH 18/32] allow higher psc value for iwdg_v3 --- embassy-stm32/src/wdg/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index dc701ef64..2ff0db09e 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -42,9 +42,13 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { // Prescaler value let psc = 2u16.pow(psc_power); + #[cfg(not(iwdg_v3))] + assert!(psc <= 256, "IWDG prescaler should be no more than 256"); + #[cfg(iwdg_v3)] // H5, U5, WBA + assert!(psc <= 1024, "IWDG prescaler should be no more than 1024"); + // Convert prescaler power to PR register value let pr = psc_power as u8 - 2; - assert!(pr <= 0b110); // Reload value let rl = reload_value(psc, timeout_us); From 0bcbca9e5bc506c846368d32db049c7cbb248997 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Tue, 27 Feb 2024 10:32:52 +0200 Subject: [PATCH 19/32] nrf: spim: Hide the "Copying SPIM tx buffer into RAM for DMA" traces Now that SPIM driver seems to be properly working, hide the trace logs which occur whenever tx buffer needs to be copied into RAM. --- embassy-nrf/src/spim.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 3538afa2b..c45d45e68 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -312,7 +312,7 @@ impl<'d, T: Instance> Spim<'d, T> { match self.blocking_inner_from_ram(rx, tx) { Ok(_) => Ok(()), Err(Error::BufferNotInRAM) => { - trace!("Copying SPIM tx buffer into RAM for DMA"); + // trace!("Copying SPIM tx buffer into RAM for DMA"); let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; tx_ram_buf.copy_from_slice(tx); self.blocking_inner_from_ram(rx, tx_ram_buf) @@ -366,7 +366,7 @@ impl<'d, T: Instance> Spim<'d, T> { match self.async_inner_from_ram(rx, tx).await { Ok(_) => Ok(()), Err(Error::BufferNotInRAM) => { - trace!("Copying SPIM tx buffer into RAM for DMA"); + // trace!("Copying SPIM tx buffer into RAM for DMA"); let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; tx_ram_buf.copy_from_slice(tx); self.async_inner_from_ram(rx, tx_ram_buf).await From 137855418aa0a3596b7469d6c8ad8ffbf4e76584 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Tue, 27 Feb 2024 10:35:39 +0200 Subject: [PATCH 20/32] nrf: Bump embedded-storage-async to 0.4.1 --- embassy-nrf/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 7e161df9b..0045d9f97 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -146,7 +146,7 @@ critical-section = "1.1" rand_core = "0.6.3" fixed = "1.10.0" embedded-storage = "0.3.1" -embedded-storage-async = "0.4.0" +embedded-storage-async = "0.4.1" cfg-if = "1.0.0" document-features = "0.2.7" From e63b0d7a2f89e3b210569ce77f45e9cb14ac6ae8 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Tue, 27 Feb 2024 10:38:07 +0000 Subject: [PATCH 21/32] stm32: can: fd: fix SID read/write from buf elems --- embassy-stm32/src/can/fd/peripheral.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 0771d6fbb..8e4ecf4a5 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -721,13 +721,15 @@ fn make_id(id: u32, extended: bool) -> embedded_can::Id { if extended { embedded_can::Id::from(unsafe { embedded_can::ExtendedId::new_unchecked(id & 0x1FFFFFFF) }) } else { - embedded_can::Id::from(unsafe { embedded_can::StandardId::new_unchecked((id & 0x000007FF) as u16) }) + // A standard identifier is stored into ID[28:18]. + embedded_can::Id::from(unsafe { embedded_can::StandardId::new_unchecked(((id >> 18) & 0x000007FF) as u16) }) } } fn put_tx_header(mailbox: &mut TxBufferElement, header: &Header) { let (id, id_type) = match header.id() { - embedded_can::Id::Standard(id) => (id.as_raw() as u32, IdType::StandardId), + // A standard identifier has to be written to ID[28:18]. + embedded_can::Id::Standard(id) => ((id.as_raw() as u32) << 18, IdType::StandardId), embedded_can::Id::Extended(id) => (id.as_raw() as u32, IdType::ExtendedId), }; From 9a4f58fe158551bd0c38d23a71f6b719d4ce5130 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Tue, 27 Feb 2024 10:38:40 +0000 Subject: [PATCH 22/32] stm32: can: fd: only TX with BRS if also TXing with FDF --- embassy-stm32/src/can/fd/peripheral.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 8e4ecf4a5..9c29e4887 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -739,7 +739,7 @@ fn put_tx_header(mailbox: &mut TxBufferElement, header: &Header) { } else { FrameFormat::Classic }; - let brs = header.len() > 8 || header.bit_rate_switching(); + let brs = (frame_format == FrameFormat::Fdcan) && header.bit_rate_switching(); mailbox.header.write(|w| { unsafe { w.id().bits(id) } From b7e0964a078d1760263df586953fbd1d29aabe2b Mon Sep 17 00:00:00 2001 From: Maia Date: Tue, 27 Feb 2024 11:07:05 -0800 Subject: [PATCH 23/32] added FDCANSEL logic for H7 --- embassy-stm32/src/rcc/h.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index c6da79afb..7b2255cc6 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -557,6 +557,9 @@ pub(crate) unsafe fn init(config: Config) { RCC.d3ccipr().modify(|w| { w.set_adcsel(config.adc_clock_source); }); + RCC.d2ccip1r().modify(|w| { + w.set_fdcansel(config.fdcan_clock_source); + }); } #[cfg(stm32h5)] { From 0ed402fd79a6dfbe4f3b2187a97d160ce7c3140b Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Tue, 27 Feb 2024 23:42:50 +0000 Subject: [PATCH 24/32] stm32: can: fd: refactor out some duplicate code --- embassy-stm32/src/can/fd/peripheral.rs | 122 +++---------------------- embassy-stm32/src/can/fdcan.rs | 66 ++++++------- embassy-stm32/src/can/frame.rs | 30 ++++++ 3 files changed, 68 insertions(+), 150 deletions(-) diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 9c29e4887..7d26a5fe0 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -37,7 +37,7 @@ impl Registers { &mut self.msg_ram_mut().receive[fifonr].fxsa[bufnum] } - pub fn read_classic(&self, fifonr: usize) -> Option<(ClassicFrame, u16)> { + pub fn read(&self, fifonr: usize) -> Option<(F, u16)> { // Fill level - do we have a msg? if self.regs.rxfs(fifonr).read().ffl() < 1 { return None; @@ -54,32 +54,8 @@ impl Registers { match maybe_header { Some((header, ts)) => { - let data = ClassicData::new(&buffer[0..header.len() as usize]); - Some((ClassicFrame::new(header, data.unwrap()), ts)) - } - None => None, - } - } - - pub fn read_fd(&self, fifonr: usize) -> Option<(FdFrame, u16)> { - // Fill level - do we have a msg? - if self.regs.rxfs(fifonr).read().ffl() < 1 { - return None; - } - - let read_idx = self.regs.rxfs(fifonr).read().fgi(); - let mailbox = self.rx_fifo_element(fifonr, read_idx as usize); - - let mut buffer: [u8; 64] = [0; 64]; - let maybe_header = extract_frame(mailbox, &mut buffer); - - // Clear FIFO, reduces count and increments read buf - self.regs.rxfa(fifonr).modify(|w| w.set_fai(read_idx)); - - match maybe_header { - Some((header, ts)) => { - let data = FdData::new(&buffer[0..header.len() as usize]); - Some((FdFrame::new(header, data.unwrap()), ts)) + let data = &buffer[0..header.len() as usize]; + Some((F::from_header(header, data)?, ts)) } None => None, } @@ -194,10 +170,9 @@ impl Registers { #[inline] //fn abort_pending_mailbox(&mut self, idx: Mailbox, pending: PTX) -> Option - pub fn abort_pending_mailbox(&self, bufidx: usize) -> Option //where // PTX: FnOnce(Mailbox, TxFrameHeader, &[u32]) -> R, - { + pub fn abort_pending_mailbox_generic(&self, bufidx: usize) -> Option { if self.abort(bufidx) { let mailbox = self.tx_buffer_element(bufidx); @@ -216,50 +191,11 @@ impl Registers { let mut data = [0u8; 64]; data_from_tx_buffer(&mut data, mailbox, len as usize); - let cd = ClassicData::new(&data).unwrap(); - Some(ClassicFrame::new(Header::new(id, len, header_reg.rtr().bit()), cd)) - } else { - // Abort request failed because the frame was already sent (or being sent) on - // the bus. All mailboxes are now free. This can happen for small prescaler - // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR - // has preempted the execution. - None - } - } - - #[inline] - //fn abort_pending_mailbox(&mut self, idx: Mailbox, pending: PTX) -> Option - pub fn abort_pending_fd_mailbox(&self, bufidx: usize) -> Option -//where - // PTX: FnOnce(Mailbox, TxFrameHeader, &[u32]) -> R, - { - if self.abort(bufidx) { - let mailbox = self.tx_buffer_element(bufidx); - - let header_reg = mailbox.header.read(); - let id = make_id(header_reg.id().bits(), header_reg.xtd().bits()); - - let len = match header_reg.to_data_length() { - DataLength::Fdcan(len) => len, - DataLength::Classic(len) => len, - }; - if len as usize > FdFrame::MAX_DATA_LEN { - return None; - } - - //let tx_ram = self.tx_msg_ram(); - let mut data = [0u8; 64]; - data_from_tx_buffer(&mut data, mailbox, len as usize); - - let cd = FdData::new(&data).unwrap(); - - let header = if header_reg.fdf().frame_format() == FrameFormat::Fdcan { - Header::new_fd(id, len, header_reg.rtr().bit(), header_reg.brs().bit()) + if header_reg.rtr().bit() { + F::new_remote(id, len as usize) } else { - Header::new(id, len, header_reg.rtr().bit()) - }; - - Some(FdFrame::new(header, cd)) + F::new(id, &data) + } } else { // Abort request failed because the frame was already sent (or being sent) on // the bus. All mailboxes are now free. This can happen for small prescaler @@ -272,7 +208,7 @@ impl Registers { /// As Transmit, but if there is a pending frame, `pending` will be called so that the frame can /// be preserved. //pub fn transmit_preserve( - pub fn write_classic(&self, frame: &ClassicFrame) -> nb::Result, Infallible> { + pub fn write(&self, frame: &F) -> nb::Result, Infallible> { let queue_is_full = self.tx_queue_is_full(); let id = frame.header().id(); @@ -281,45 +217,11 @@ impl Registers { // Discard the first slot with a lower priority message let (idx, pending_frame) = if queue_is_full { if self.is_available(0, id) { - (0, self.abort_pending_mailbox(0)) + (0, self.abort_pending_mailbox_generic(0)) } else if self.is_available(1, id) { - (1, self.abort_pending_mailbox(1)) + (1, self.abort_pending_mailbox_generic(1)) } else if self.is_available(2, id) { - (2, self.abort_pending_mailbox(2)) - } else { - // For now we bail when there is no lower priority slot available - // Can this lead to priority inversion? - return Err(nb::Error::WouldBlock); - } - } else { - // Read the Write Pointer - let idx = self.regs.txfqs().read().tfqpi(); - - (idx, None) - }; - - self.put_tx_frame(idx as usize, frame.header(), frame.data()); - - Ok(pending_frame) - } - - /// As Transmit, but if there is a pending frame, `pending` will be called so that the frame can - /// be preserved. - //pub fn transmit_preserve( - pub fn write_fd(&self, frame: &FdFrame) -> nb::Result, Infallible> { - let queue_is_full = self.tx_queue_is_full(); - - let id = frame.header().id(); - - // If the queue is full, - // Discard the first slot with a lower priority message - let (idx, pending_frame) = if queue_is_full { - if self.is_available(0, id) { - (0, self.abort_pending_fd_mailbox(0)) - } else if self.is_available(1, id) { - (1, self.abort_pending_fd_mailbox(1)) - } else if self.is_available(2, id) { - (2, self.abort_pending_fd_mailbox(2)) + (2, self.abort_pending_mailbox_generic(2)) } else { // For now we bail when there is no lower priority slot available // Can this lead to priority inversion? diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 744d756f5..6a4a25cb7 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -58,7 +58,7 @@ impl interrupt::typelevel::Handler for IT0Interrup if !T::registers().tx_queue_is_full() { match buf.tx_receiver.try_receive() { Ok(frame) => { - _ = T::registers().write_classic(&frame); + _ = T::registers().write(&frame); } Err(_) => {} } @@ -68,7 +68,7 @@ impl interrupt::typelevel::Handler for IT0Interrup if !T::registers().tx_queue_is_full() { match buf.tx_receiver.try_receive() { Ok(frame) => { - _ = T::registers().write_fd(&frame); + _ = T::registers().write(&frame); } Err(_) => {} } @@ -359,7 +359,7 @@ impl<'d, T: Instance> Fdcan<'d, T> { /// Returns the next received message frame pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { - T::state().rx_mode.read::().await + T::state().rx_mode.read_classic::().await } /// Queues the message to be sent but exerts backpressure. If a lower-priority @@ -633,7 +633,7 @@ impl<'c, 'd, T: Instance> FdcanTx<'d, T> { impl<'c, 'd, T: Instance> FdcanRx<'d, T> { /// Returns the next received message frame pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { - T::state().rx_mode.read::().await + T::state().rx_mode.read_classic::().await } /// Returns the next received message frame @@ -649,6 +649,7 @@ pub(crate) mod sealed { use embassy_sync::channel::{DynamicReceiver, DynamicSender}; use embassy_sync::waitqueue::AtomicWaker; + use super::CanHeader; use crate::can::_version::{BusError, Timestamp}; use crate::can::frame::{ClassicFrame, FdFrame}; @@ -689,13 +690,13 @@ pub(crate) mod sealed { waker.wake(); } RxMode::ClassicBuffered(buf) => { - if let Some(r) = T::registers().read_classic(fifonr) { + if let Some(r) = T::registers().read(fifonr) { let ts = T::calc_timestamp(T::state().ns_per_timer_tick, r.1); let _ = buf.rx_sender.try_send((r.0, ts)); } } RxMode::FdBuffered(buf) => { - if let Some(r) = T::registers().read_fd(fifonr) { + if let Some(r) = T::registers().read(fifonr) { let ts = T::calc_timestamp(T::state().ns_per_timer_tick, r.1); let _ = buf.rx_sender.try_send((r.0, ts)); } @@ -703,15 +704,15 @@ pub(crate) mod sealed { } } - pub async fn read(&self) -> Result<(ClassicFrame, Timestamp), BusError> { + async fn read(&self) -> Result<(F, Timestamp), BusError> { poll_fn(|cx| { T::state().err_waker.register(cx.waker()); self.register(cx.waker()); - if let Some((msg, ts)) = T::registers().read_classic(0) { + if let Some((msg, ts)) = T::registers().read(0) { let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); return Poll::Ready(Ok((msg, ts))); - } else if let Some((msg, ts)) = T::registers().read_classic(1) { + } else if let Some((msg, ts)) = T::registers().read(1) { let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); return Poll::Ready(Ok((msg, ts))); } else if let Some(err) = T::registers().curr_error() { @@ -723,24 +724,12 @@ pub(crate) mod sealed { .await } - pub async fn read_fd(&self) -> Result<(FdFrame, Timestamp), BusError> { - poll_fn(|cx| { - T::state().err_waker.register(cx.waker()); - self.register(cx.waker()); + pub async fn read_classic(&self) -> Result<(ClassicFrame, Timestamp), BusError> { + self.read::().await + } - if let Some((msg, ts)) = T::registers().read_fd(0) { - let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); - return Poll::Ready(Ok((msg, ts))); - } else if let Some((msg, ts)) = T::registers().read_fd(1) { - let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); - return Poll::Ready(Ok((msg, ts))); - } else if let Some(err) = T::registers().curr_error() { - // TODO: this is probably wrong - return Poll::Ready(Err(err)); - } - Poll::Pending - }) - .await + pub async fn read_fd(&self) -> Result<(FdFrame, Timestamp), BusError> { + self.read::().await } } @@ -766,11 +755,11 @@ pub(crate) mod sealed { /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. - pub async fn write(&self, frame: &ClassicFrame) -> Option { + async fn write_generic(&self, frame: &F) -> Option { poll_fn(|cx| { self.register(cx.waker()); - if let Ok(dropped) = T::registers().write_classic(frame) { + if let Ok(dropped) = T::registers().write(frame) { return Poll::Ready(dropped); } @@ -781,23 +770,20 @@ pub(crate) mod sealed { .await } + /// Queues the message to be sent but exerts backpressure. If a lower-priority + /// frame is dropped from the mailbox, it is returned. If no lower-priority frames + /// can be replaced, this call asynchronously waits for a frame to be successfully + /// transmitted, then tries again. + pub async fn write(&self, frame: &ClassicFrame) -> Option { + self.write_generic::(frame).await + } + /// Queues the message to be sent but exerts backpressure. If a lower-priority /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. pub async fn write_fd(&self, frame: &FdFrame) -> Option { - poll_fn(|cx| { - self.register(cx.waker()); - - if let Ok(dropped) = T::registers().write_fd(frame) { - return Poll::Ready(dropped); - } - - // Couldn't replace any lower priority frames. Need to wait for some mailboxes - // to clear. - Poll::Pending - }) - .await + self.write_generic::(frame).await } } diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs index 725a9b1ab..59b9fb08c 100644 --- a/embassy-stm32/src/can/frame.rs +++ b/embassy-stm32/src/can/frame.rs @@ -56,6 +56,16 @@ impl Header { } } +/// Trait for FDCAN frame types, providing ability to construct from a Header +/// and to retrieve the Header from a frame +pub trait CanHeader: Sized { + /// Construct frame from header and payload + fn from_header(header: Header, data: &[u8]) -> Option; + + /// Get this frame's header struct + fn header(&self) -> &Header; +} + /// Payload of a classic CAN data frame. /// /// Contains 0 to 8 Bytes of data. @@ -213,6 +223,16 @@ impl embedded_can::Frame for ClassicFrame { } } +impl CanHeader for ClassicFrame { + fn from_header(header: Header, data: &[u8]) -> Option { + Some(Self::new(header, ClassicData::new(data)?)) + } + + fn header(&self) -> &Header { + self.header() + } +} + /// Payload of a (FD)CAN data frame. /// /// Contains 0 to 64 Bytes of data. @@ -368,3 +388,13 @@ impl embedded_can::Frame for FdFrame { &self.data.raw() } } + +impl CanHeader for FdFrame { + fn from_header(header: Header, data: &[u8]) -> Option { + Some(Self::new(header, FdData::new(data)?)) + } + + fn header(&self) -> &Header { + self.header() + } +} From a8da42943fdd57ded612399d18846398cbd011d3 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Tue, 27 Feb 2024 23:45:53 +0000 Subject: [PATCH 25/32] stm32: can: fd: rm some irrelevant commented code and dead code --- embassy-stm32/src/can/fd/peripheral.rs | 42 -------------------------- embassy-stm32/src/can/frame.rs | 2 -- 2 files changed, 44 deletions(-) diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 7d26a5fe0..c5606b883 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -62,11 +62,6 @@ impl Registers { } pub fn put_tx_frame(&self, bufidx: usize, header: &Header, buffer: &[u8]) { - // Fill level - do we have a msg? - //if self.regs.rxfs(fifonr).read().ffl() < 1 { return None; } - - //let read_idx = self.regs.rxfs(fifonr).read().fgi(); - let mailbox = self.tx_buffer_element(bufidx); mailbox.reset(); @@ -169,9 +164,6 @@ impl Registers { } #[inline] - //fn abort_pending_mailbox(&mut self, idx: Mailbox, pending: PTX) -> Option -//where - // PTX: FnOnce(Mailbox, TxFrameHeader, &[u32]) -> R, pub fn abort_pending_mailbox_generic(&self, bufidx: usize) -> Option { if self.abort(bufidx) { let mailbox = self.tx_buffer_element(bufidx); @@ -187,7 +179,6 @@ impl Registers { return None; } - //let tx_ram = self.tx_msg_ram(); let mut data = [0u8; 64]; data_from_tx_buffer(&mut data, mailbox, len as usize); @@ -205,9 +196,6 @@ impl Registers { } } - /// As Transmit, but if there is a pending frame, `pending` will be called so that the frame can - /// be preserved. - //pub fn transmit_preserve( pub fn write(&self, frame: &F) -> nb::Result, Infallible> { let queue_is_full = self.tx_queue_is_full(); @@ -459,8 +447,6 @@ impl Registers { /// parameter to this method. #[inline] pub fn set_nominal_bit_timing(&mut self, btr: NominalBitTiming) { - //self.control.config.nbtr = btr; - self.regs.nbtp().write(|w| { w.set_nbrp(btr.nbrp() - 1); w.set_ntseg1(btr.ntseg1() - 1); @@ -473,8 +459,6 @@ impl Registers { /// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS. #[inline] pub fn set_data_bit_timing(&mut self, btr: DataBitTiming) { - //self.control.config.dbtr = btr; - self.regs.dbtp().write(|w| { w.set_dbrp(btr.dbrp() - 1); w.set_dtseg1(btr.dtseg1() - 1); @@ -492,7 +476,6 @@ impl Registers { #[inline] pub fn set_automatic_retransmit(&mut self, enabled: bool) { self.regs.cccr().modify(|w| w.set_dar(!enabled)); - //self.control.config.automatic_retransmit = enabled; } /// Configures the transmit pause feature. See @@ -500,21 +483,18 @@ impl Registers { #[inline] pub fn set_transmit_pause(&mut self, enabled: bool) { self.regs.cccr().modify(|w| w.set_txp(!enabled)); - //self.control.config.transmit_pause = enabled; } /// Configures non-iso mode. See [`FdCanConfig::set_non_iso_mode`] #[inline] pub fn set_non_iso_mode(&mut self, enabled: bool) { self.regs.cccr().modify(|w| w.set_niso(enabled)); - //self.control.config.non_iso_mode = enabled; } /// Configures edge filtering. See [`FdCanConfig::set_edge_filtering`] #[inline] pub fn set_edge_filtering(&mut self, enabled: bool) { self.regs.cccr().modify(|w| w.set_efbi(enabled)); - //self.control.config.edge_filtering = enabled; } /// Configures frame transmission mode. See @@ -534,16 +514,12 @@ impl Registers { #[cfg(not(stm32h7))] w.set_brse(brse); }); - - //self.control.config.frame_transmit = fts; } /// Sets the protocol exception handling on/off #[inline] pub fn set_protocol_exception_handling(&mut self, enabled: bool) { self.regs.cccr().modify(|w| w.set_pxhd(!enabled)); - - //self.control.config.protocol_exception_handling = enabled; } /// Configures and resets the timestamp counter @@ -567,8 +543,6 @@ impl Registers { w.set_tcp(tcp); w.set_tss(tss); }); - - //self.control.config.timestamp_source = select; } #[cfg(not(stm32h7))] @@ -696,22 +670,6 @@ fn data_from_tx_buffer(buffer: &mut [u8], mailbox: &TxBufferElement, len: usize) } } -impl From<&RxFifoElement> for ClassicFrame { - fn from(mailbox: &RxFifoElement) -> Self { - let header_reg = mailbox.header.read(); - - let id = make_id(header_reg.id().bits(), header_reg.xtd().bits()); - let dlc = header_reg.to_data_length().len(); - let len = dlc as usize; - - let mut buffer: [u8; 64] = [0; 64]; - data_from_fifo(&mut buffer, mailbox, len); - let data = ClassicData::new(&buffer[0..len]); - let header = Header::new(id, dlc, header_reg.rtr().bits()); - ClassicFrame::new(header, data.unwrap()) - } -} - fn extract_frame(mailbox: &RxFifoElement, buffer: &mut [u8]) -> Option<(Header, u16)> { let header_reg = mailbox.header.read(); diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs index 59b9fb08c..00a441db6 100644 --- a/embassy-stm32/src/can/frame.rs +++ b/embassy-stm32/src/can/frame.rs @@ -292,8 +292,6 @@ pub struct FdFrame { } impl FdFrame { - pub(crate) const MAX_DATA_LEN: usize = 64; - /// Create a new CAN classic Frame pub fn new(can_header: Header, data: FdData) -> FdFrame { FdFrame { can_header, data } From 1353a343b8e9d48b8f1f537dbf4e38e32a39c06d Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Wed, 28 Feb 2024 18:03:53 +1000 Subject: [PATCH 26/32] Buffer is not big enough for FD frames. --- embassy-stm32/src/can/fd/peripheral.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index c5606b883..abb546895 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -46,7 +46,7 @@ impl Registers { let read_idx = self.regs.rxfs(fifonr).read().fgi(); let mailbox = self.rx_fifo_element(fifonr, read_idx as usize); - let mut buffer: [u8; 8] = [0; 8]; + let mut buffer = [0u8; 64]; let maybe_header = extract_frame(mailbox, &mut buffer); // Clear FIFO, reduces count and increments read buf From 368b3a9aaf97a7662217b9ff2784e3648368586c Mon Sep 17 00:00:00 2001 From: "Guilherme S. Salustiano" Date: Wed, 28 Feb 2024 14:54:54 +0100 Subject: [PATCH 27/32] fix: radio error --- embassy-nrf/src/radio/ble.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index 81ef96b65..24dba582f 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -15,7 +15,7 @@ use crate::interrupt::typelevel::Interrupt; use crate::radio::*; use crate::util::slice_in_ram_or; -/// UART error. +/// RADIO error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] From 990b44566ce1393a4a553a65d5a6c412cb656805 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Wed, 28 Feb 2024 15:11:30 +0100 Subject: [PATCH 28/32] [docs] Added some failure modes to watch out for * Linked to probe.rs website rather than the crates.io page * Fixed some formatting errors (>:( grrr asciidoc) * Added cargo add probe-rs failure mode * Added pico-w vs pico blinky failure mode --- docs/modules/ROOT/pages/getting_started.adoc | 28 +++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/modules/ROOT/pages/getting_started.adoc b/docs/modules/ROOT/pages/getting_started.adoc index 24bde1c1f..be2b868eb 100644 --- a/docs/modules/ROOT/pages/getting_started.adoc +++ b/docs/modules/ROOT/pages/getting_started.adoc @@ -3,7 +3,7 @@ So you want to try Embassy, great! To get started, there are a few tools you need to install: * link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code. -* link:https://crates.io/crates/probe-rs[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well. +* link:https://probe.rs/[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well. If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples. @@ -82,19 +82,19 @@ If everything worked correctly, you should see a blinking LED on your board, and └─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:27 ---- -NOTE: How does the `cargo run` command know how to connect to our board and program it? In each `examples` folder, there’s a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip it’s programming, so you’ll have to edit this file if you want to run examples on other chips. +NOTE: How does the `+cargo run+` command know how to connect to our board and program it? In each `examples` folder, there’s a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip it’s programming, so you’ll have to edit this file if you want to run examples on other chips. === It didn’t work! -If you hare having issues when running `cargo run --release`, please check the following: +If you hare having issues when running `+cargo run --release+`, please check the following: -* You are specifying the correct `--chip` on the command line, OR -* You have set `.cargo/config.toml`'s run line to the correct chip, AND -* You have changed `examples/Cargo.toml`'s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature) +* You are specifying the correct `+--chip+` on the command line, OR +* You have set `+.cargo/config.toml+`’s run line to the correct chip, AND +* You have changed `+examples/Cargo.toml+`’s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature) At this point the project should run. If you do not see a blinky LED for blinky, for example, be sure to check the code is toggling your board's LED pin. -If you are trying to run an example with `cargo run --release` and you see the following output: +If you are trying to run an example with `+cargo run --release+` and you see the following output: [source] ---- 0.000000 INFO Hello World! @@ -115,6 +115,20 @@ To get rid of the frame-index error add the following to your `Cargo.toml`: debug = 2 ---- +If you’re get an extremely long error message containing something like the following: + +[source] +---- +error[E0463]: can't find crate for `std` + | + = note: the `thumbv6m-none-eabi` target may not support the standard library + = note: `std` is required by `stable_deref_trait` because it does not declare `#![no_std]` +---- + +Make sure that you didn’t accidentally run `+cargo add probe-rs+` (which adds it as a dependency) instead of link:https://probe.rs/docs/getting-started/installation/[correctly installing probe-rs]. + +If you’re using a raspberry pi pico-w, make sure you’re running `+cargo run --bin wifi_blinky --release+` rather than the regular blinky. The pico-w’s on-board LED is connected to the WiFi chip, which needs to be initialized before the LED can be blinked. + If you’re still having problems, check the link:https://embassy.dev/book/dev/faq.html[FAQ], or ask for help in the link:https://matrix.to/#/#embassy-rs:matrix.org[Embassy Chat Room]. == What's next? From 2787164ea9ba30f806d6c38443d2eca2984b1e62 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Wed, 28 Feb 2024 15:15:43 +0100 Subject: [PATCH 29/32] grammar fix --- docs/modules/ROOT/pages/getting_started.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/getting_started.adoc b/docs/modules/ROOT/pages/getting_started.adoc index be2b868eb..e8bc84255 100644 --- a/docs/modules/ROOT/pages/getting_started.adoc +++ b/docs/modules/ROOT/pages/getting_started.adoc @@ -115,7 +115,7 @@ To get rid of the frame-index error add the following to your `Cargo.toml`: debug = 2 ---- -If you’re get an extremely long error message containing something like the following: +If you’re getting an extremely long error message containing something like the following: [source] ---- From d07a0148d759fa09ba1ee8dc4810bb4edd8bda49 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Wed, 28 Feb 2024 15:48:37 +0100 Subject: [PATCH 30/32] Documented rp2040 probe-rs info bug, linked to new_project page --- docs/modules/ROOT/pages/getting_started.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/modules/ROOT/pages/getting_started.adoc b/docs/modules/ROOT/pages/getting_started.adoc index e8bc84255..73cb5530d 100644 --- a/docs/modules/ROOT/pages/getting_started.adoc +++ b/docs/modules/ROOT/pages/getting_started.adoc @@ -129,6 +129,8 @@ Make sure that you didn’t accidentally run `+cargo add probe-rs+` (which adds If you’re using a raspberry pi pico-w, make sure you’re running `+cargo run --bin wifi_blinky --release+` rather than the regular blinky. The pico-w’s on-board LED is connected to the WiFi chip, which needs to be initialized before the LED can be blinked. +If you’re using an rp2040 debug probe (e.g. the pico probe) and are having issues after running `probe-rs info`, unplug and reconnect the probe, letting it power cycle. Running `probe-rs info` is link:https://github.com/probe-rs/probe-rs/issues/1849[known to put the pico probe into an unusable state]. + If you’re still having problems, check the link:https://embassy.dev/book/dev/faq.html[FAQ], or ask for help in the link:https://matrix.to/#/#embassy-rs:matrix.org[Embassy Chat Room]. == What's next? @@ -138,3 +140,4 @@ Congratulations, you have your first Embassy application running! Here are some * Read more about the xref:runtime.adoc[executor]. * Read more about the xref:hal.adoc[HAL]. * Start xref:basic_application.adoc[writing your application]. +* Learn how to xref:new_project.adoc[start a new embassy project by adapting an example]. From 47c579eba2a7b4372a891bcd747f2bb0c56ce8a4 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Wed, 28 Feb 2024 18:08:41 +0800 Subject: [PATCH 31/32] update metapac --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/can/fd/peripheral.rs | 4 +++- embassy-stm32/src/opamp.rs | 2 ++ embassy-stm32/src/timer/mod.rs | 12 ++++++------ examples/stm32h7/src/bin/low_level_timer_api.rs | 4 ++-- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 81b8e2f94..08ccd35ae 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -70,7 +70,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7c8b53413499acc3273b706318777a60f932d77a" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a0bcec33362449fb733c066936d25cbabab396a" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -94,7 +94,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7c8b53413499acc3273b706318777a60f932d77a", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a0bcec33362449fb733c066936d25cbabab396a", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index c5606b883..9b8802ff9 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -303,7 +303,9 @@ impl Registers { // Framework specific settings are set here // set TxBuffer to Queue Mode - self.regs.txbc().write(|w| w.set_tfqm(true)); + self.regs + .txbc() + .write(|w| w.set_tfqm(crate::pac::can::vals::Tfqm::QUEUE)); // set standard filters list size to 28 // set extended filters list size to 8 diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index ae8b3cacc..cf531e266 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -90,6 +90,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { in_pin.set_as_analog(); out_pin.set_as_analog(); + // PGA_GAIN value may have different meaning in different MCU serials, use with caution. let (vm_sel, pga_gain) = match gain { OpAmpGain::Mul1 => (0b11, 0b00), OpAmpGain::Mul2 => (0b10, 0b00), @@ -127,6 +128,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { into_ref!(pin); pin.set_as_analog(); + // PGA_GAIN value may have different meaning in different MCU serials, use with caution. let (vm_sel, pga_gain) = match gain { OpAmpGain::Mul1 => (0b11, 0b00), OpAmpGain::Mul2 => (0b10, 0b00), diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index c8f580101..8530c5229 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -379,7 +379,7 @@ pub(crate) mod sealed { let regs = Self::regs_gp32(); regs.psc().write(|r| r.set_psc(psc)); - regs.arr().write(|r| r.set_arr(arr)); + regs.arr().write_value(arr); regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); regs.egr().write(|r| r.set_ug(true)); @@ -391,7 +391,7 @@ pub(crate) mod sealed { let timer_f = Self::frequency(); let regs = Self::regs_gp32(); - let arr = regs.arr().read().arr(); + let arr = regs.arr().read(); let psc = regs.psc().read().psc(); timer_f / arr / (psc + 1) @@ -399,22 +399,22 @@ pub(crate) mod sealed { /// Set comapre value for a channel. fn set_compare_value(&self, channel: Channel, value: u32) { - Self::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(value)); + Self::regs_gp32().ccr(channel.index()).write_value(value); } /// Get capture value for a channel. fn get_capture_value(&self, channel: Channel) -> u32 { - Self::regs_gp32().ccr(channel.index()).read().ccr() + Self::regs_gp32().ccr(channel.index()).read() } /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. fn get_max_compare_value(&self) -> u32 { - Self::regs_gp32().arr().read().arr() + Self::regs_gp32().arr().read() } /// Get compare value for a channel. fn get_compare_value(&self, channel: Channel) -> u32 { - Self::regs_gp32().ccr(channel.index()).read().ccr() + Self::regs_gp32().ccr(channel.index()).read() } } diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index cc508c3cf..049d9967d 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -113,11 +113,11 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { } pub fn get_max_duty(&self) -> u32 { - T::regs_gp32().arr().read().arr() + T::regs_gp32().arr().read() } pub fn set_duty(&mut self, channel: Channel, duty: u32) { defmt::assert!(duty < self.get_max_duty()); - T::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(duty)) + T::regs_gp32().ccr(channel.index()).write_value(duty) } } From e954d1716a229bae94cd4739d1349a487bdaa8ae Mon Sep 17 00:00:00 2001 From: Andreas Schmidt Date: Thu, 29 Feb 2024 20:27:35 +0100 Subject: [PATCH 32/32] docs: update basic example references in basic_application.adoc --- docs/modules/ROOT/pages/basic_application.adoc | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/docs/modules/ROOT/pages/basic_application.adoc b/docs/modules/ROOT/pages/basic_application.adoc index 95792d5a0..02b8981c9 100644 --- a/docs/modules/ROOT/pages/basic_application.adoc +++ b/docs/modules/ROOT/pages/basic_application.adoc @@ -17,15 +17,6 @@ The first thing you’ll notice are two attributes at the top of the file. These include::example$basic/src/main.rs[lines="1..2"] ---- -=== Rust Nightly - -The next declaration is a Rust Unstable feature, which means that Embassy requires Rust Nightly: - -[source,rust] ----- -include::example$basic/src/main.rs[lines="3"] ----- - === Dealing with errors Then, what follows are some declarations on how to deal with panics and faults. During development, a good practice is to rely on `defmt-rtt` and `panic-probe` to print diagnostics to the terminal: @@ -41,7 +32,7 @@ After a bit of import declaration, the tasks run by the application should be de [source,rust] ---- -include::example$basic/src/main.rs[lines="12..20"] +include::example$basic/src/main.rs[lines="10..18"] ---- An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking. @@ -56,7 +47,7 @@ We then initialize the HAL with a default config, which gives us a `Peripherals` [source,rust] ---- -include::example$basic/src/main.rs[lines="22..-1"] +include::example$basic/src/main.rs[lines="20..-1"] ---- What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following: