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; - } -}