diff options
| author | Ulf Lilleengen <[email protected]> | 2024-02-28 14:40:18 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-02-28 14:40:18 +0000 |
| commit | b806800f237a7951a86fc3eeb9785a74cbc2169e (patch) | |
| tree | 776f140fcb64e4658cde033475dfd6c6f971aba7 /embassy-nrf | |
| parent | 5ced938184e141471e921d235975e95725d6be53 (diff) | |
| parent | 368b3a9aaf97a7662217b9ff2784e3648368586c (diff) | |
Merge pull request #2351 from jewel-rs/feat/radio
[embassry_nrf]: add BLE Radio driver
Diffstat (limited to 'embassy-nrf')
| -rw-r--r-- | embassy-nrf/src/chips/nrf52840.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/lib.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/ble.rs | 438 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/mod.rs | 75 |
4 files changed, 523 insertions, 0 deletions
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 51c55cd4d..d3272b2e8 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs | |||
| @@ -173,6 +173,9 @@ embassy_hal_internal::peripherals! { | |||
| 173 | 173 | ||
| 174 | // I2S | 174 | // I2S |
| 175 | I2S, | 175 | I2S, |
| 176 | |||
| 177 | // Radio | ||
| 178 | RADIO, | ||
| 176 | } | 179 | } |
| 177 | 180 | ||
| 178 | impl_usb!(USBD, USBD, USBD); | 181 | impl_usb!(USBD, USBD, USBD); |
| @@ -311,6 +314,8 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); | |||
| 311 | 314 | ||
| 312 | impl_i2s!(I2S, I2S, I2S); | 315 | impl_i2s!(I2S, I2S, I2S); |
| 313 | 316 | ||
| 317 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 318 | |||
| 314 | embassy_hal_internal::interrupt_mod!( | 319 | embassy_hal_internal::interrupt_mod!( |
| 315 | POWER_CLOCK, | 320 | POWER_CLOCK, |
| 316 | RADIO, | 321 | RADIO, |
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 358a7cc27..04a6293a4 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -45,6 +45,11 @@ pub mod buffered_uarte; | |||
| 45 | pub mod gpio; | 45 | pub mod gpio; |
| 46 | #[cfg(feature = "gpiote")] | 46 | #[cfg(feature = "gpiote")] |
| 47 | pub mod gpiote; | 47 | pub mod gpiote; |
| 48 | |||
| 49 | // TODO: tested on other chips | ||
| 50 | #[cfg(any(feature = "nrf52840"))] | ||
| 51 | pub mod radio; | ||
| 52 | |||
| 48 | #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] | 53 | #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] |
| 49 | pub mod i2s; | 54 | pub mod i2s; |
| 50 | pub mod nvmc; | 55 | 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..24dba582f --- /dev/null +++ b/embassy-nrf/src/radio/ble.rs | |||
| @@ -0,0 +1,438 @@ | |||
| 1 | //! Radio driver implementation focused on Bluetooth Low-Energy transmission. | ||
| 2 | |||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 5 | use core::task::Poll; | ||
| 6 | |||
| 7 | use embassy_hal_internal::drop::OnDrop; | ||
| 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 9 | pub use pac::radio::mode::MODE_A as Mode; | ||
| 10 | use pac::radio::pcnf0::PLEN_A as PreambleLength; | ||
| 11 | use pac::radio::state::STATE_A as RadioState; | ||
| 12 | pub use pac::radio::txpower::TXPOWER_A as TxPower; | ||
| 13 | |||
| 14 | use crate::interrupt::typelevel::Interrupt; | ||
| 15 | use crate::radio::*; | ||
| 16 | use crate::util::slice_in_ram_or; | ||
| 17 | |||
| 18 | /// RADIO error. | ||
| 19 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 20 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 21 | #[non_exhaustive] | ||
| 22 | pub enum Error { | ||
| 23 | /// Buffer was too long. | ||
| 24 | BufferTooLong, | ||
| 25 | /// Buffer was to short. | ||
| 26 | BufferTooShort, | ||
| 27 | /// The buffer is not in data RAM. It is most likely in flash, and nRF's DMA cannot access flash. | ||
| 28 | BufferNotInRAM, | ||
| 29 | } | ||
| 30 | |||
| 31 | /// Radio driver. | ||
| 32 | pub struct Radio<'d, T: Instance> { | ||
| 33 | _p: PeripheralRef<'d, T>, | ||
| 34 | } | ||
| 35 | |||
| 36 | impl<'d, T: Instance> Radio<'d, T> { | ||
| 37 | /// Create a new radio driver. | ||
| 38 | pub fn new( | ||
| 39 | radio: impl Peripheral<P = T> + 'd, | ||
| 40 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 41 | ) -> Self { | ||
| 42 | into_ref!(radio); | ||
| 43 | |||
| 44 | let r = T::regs(); | ||
| 45 | |||
| 46 | r.pcnf1.write(|w| unsafe { | ||
| 47 | // It is 0 bytes long in a standard BLE packet | ||
| 48 | w.statlen() | ||
| 49 | .bits(0) | ||
| 50 | // MaxLen configures the maximum packet payload plus add-on size in | ||
| 51 | // number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure | ||
| 52 | // that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means | ||
| 53 | // that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a | ||
| 54 | // packet larger than MAXLEN, the payload will be truncated at MAXLEN | ||
| 55 | // | ||
| 56 | // To simplify the implementation, It is setted as the maximum value | ||
| 57 | // and the length of the packet is controlled only by the LENGTH field in the packet | ||
| 58 | .maxlen() | ||
| 59 | .bits(255) | ||
| 60 | // Configure the length of the address field in the packet | ||
| 61 | // The prefix after the address fields is always appended, so is always 1 byte less than the size of the address | ||
| 62 | // The base address is truncated from the least significant byte if the BALEN is less than 4 | ||
| 63 | // | ||
| 64 | // BLE address is always 4 bytes long | ||
| 65 | .balen() | ||
| 66 | .bits(3) // 3 bytes base address (+ 1 prefix); | ||
| 67 | // Configure the endianess | ||
| 68 | // For BLE is always little endian (LSB first) | ||
| 69 | .endian() | ||
| 70 | .little() | ||
| 71 | // Data whitening is used to avoid long sequences of zeros or | ||
| 72 | // ones, e.g., 0b0000000 or 0b1111111, in the data bit stream. | ||
| 73 | // The whitener and de-whitener are defined the same way, | ||
| 74 | // using a 7-bit linear feedback shift register with the | ||
| 75 | // polynomial x7 + x4 + 1. | ||
| 76 | // | ||
| 77 | // In BLE Whitening shall be applied on the PDU and CRC of all | ||
| 78 | // Link Layer packets and is performed after the CRC generation | ||
| 79 | // in the transmitter. No other parts of the packets are whitened. | ||
| 80 | // De-whitening is performed before the CRC checking in the receiver | ||
| 81 | // Before whitening or de-whitening, the shift register should be | ||
| 82 | // initialized based on the channel index. | ||
| 83 | .whiteen() | ||
| 84 | .set_bit() | ||
| 85 | }); | ||
| 86 | |||
| 87 | // Configure CRC | ||
| 88 | r.crccnf.write(|w| { | ||
| 89 | // In BLE the CRC shall be calculated on the PDU of all Link Layer | ||
| 90 | // packets (even if the packet is encrypted). | ||
| 91 | // It skips the address field | ||
| 92 | w.skipaddr() | ||
| 93 | .skip() | ||
| 94 | // In BLE 24-bit CRC = 3 bytes | ||
| 95 | .len() | ||
| 96 | .three() | ||
| 97 | }); | ||
| 98 | |||
| 99 | // Ch map between 2400 MHZ .. 2500 MHz | ||
| 100 | // All modes use this range | ||
| 101 | r.frequency.write(|w| w.map().default()); | ||
| 102 | |||
| 103 | // Configure shortcuts to simplify and speed up sending and receiving packets. | ||
| 104 | r.shorts.write(|w| { | ||
| 105 | // start transmission/recv immediately after ramp-up | ||
| 106 | // disable radio when transmission/recv is done | ||
| 107 | w.ready_start().enabled().end_disable().enabled() | ||
| 108 | }); | ||
| 109 | |||
| 110 | // Enable NVIC interrupt | ||
| 111 | T::Interrupt::unpend(); | ||
| 112 | unsafe { T::Interrupt::enable() }; | ||
| 113 | |||
| 114 | Self { _p: radio } | ||
| 115 | } | ||
| 116 | |||
| 117 | fn state(&self) -> RadioState { | ||
| 118 | match T::regs().state.read().state().variant() { | ||
| 119 | Some(s) => s, | ||
| 120 | None => unreachable!(), | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | #[allow(dead_code)] | ||
| 125 | fn trace_state(&self) { | ||
| 126 | match self.state() { | ||
| 127 | RadioState::DISABLED => trace!("radio:state:DISABLED"), | ||
| 128 | RadioState::RX_RU => trace!("radio:state:RX_RU"), | ||
| 129 | RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"), | ||
| 130 | RadioState::RX => trace!("radio:state:RX"), | ||
| 131 | RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"), | ||
| 132 | RadioState::TX_RU => trace!("radio:state:TX_RU"), | ||
| 133 | RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"), | ||
| 134 | RadioState::TX => trace!("radio:state:TX"), | ||
| 135 | RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"), | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Set the radio mode | ||
| 140 | /// | ||
| 141 | /// The radio must be disabled before calling this function | ||
| 142 | pub fn set_mode(&mut self, mode: Mode) { | ||
| 143 | assert!(self.state() == RadioState::DISABLED); | ||
| 144 | |||
| 145 | let r = T::regs(); | ||
| 146 | r.mode.write(|w| w.mode().variant(mode)); | ||
| 147 | |||
| 148 | r.pcnf0.write(|w| { | ||
| 149 | w.plen().variant(match mode { | ||
| 150 | Mode::BLE_1MBIT => PreambleLength::_8BIT, | ||
| 151 | Mode::BLE_2MBIT => PreambleLength::_16BIT, | ||
| 152 | Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE, | ||
| 153 | _ => unimplemented!(), | ||
| 154 | }) | ||
| 155 | }); | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Set the header size changing the S1's len field | ||
| 159 | /// | ||
| 160 | /// The radio must be disabled before calling this function | ||
| 161 | pub fn set_header_expansion(&mut self, use_s1_field: bool) { | ||
| 162 | assert!(self.state() == RadioState::DISABLED); | ||
| 163 | |||
| 164 | let r = T::regs(); | ||
| 165 | |||
| 166 | // s1 len in bits | ||
| 167 | let s1len: u8 = match use_s1_field { | ||
| 168 | false => 0, | ||
| 169 | true => 8, | ||
| 170 | }; | ||
| 171 | |||
| 172 | r.pcnf0.write(|w| unsafe { | ||
| 173 | w | ||
| 174 | // Configure S0 to 1 byte length, this will represent the Data/Adv header flags | ||
| 175 | .s0len() | ||
| 176 | .set_bit() | ||
| 177 | // Configure the length (in bits) field to 1 byte length, this will represent the length of the payload | ||
| 178 | // and also be used to know how many bytes to read/write from/to the buffer | ||
| 179 | .lflen() | ||
| 180 | .bits(8) | ||
| 181 | // Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE. | ||
| 182 | .s1len() | ||
| 183 | .bits(s1len) | ||
| 184 | }); | ||
| 185 | } | ||
| 186 | |||
| 187 | /// Set initial data whitening value | ||
| 188 | /// Data whitening is used to avoid long sequences of zeros or ones, e.g., 0b0000000 or 0b1111111, in the data bit stream | ||
| 189 | /// On BLE the initial value is the channel index | 0x40 | ||
| 190 | /// | ||
| 191 | /// The radio must be disabled before calling this function | ||
| 192 | pub fn set_whitening_init(&mut self, whitening_init: u8) { | ||
| 193 | assert!(self.state() == RadioState::DISABLED); | ||
| 194 | |||
| 195 | let r = T::regs(); | ||
| 196 | |||
| 197 | r.datawhiteiv.write(|w| unsafe { w.datawhiteiv().bits(whitening_init) }); | ||
| 198 | } | ||
| 199 | |||
| 200 | /// Set the central frequency to be used | ||
| 201 | /// It should be in the range 2400..2500 | ||
| 202 | /// | ||
| 203 | /// [The radio must be disabled before calling this function](https://devzone.nordicsemi.com/f/nordic-q-a/15829/radio-frequency-change) | ||
| 204 | pub fn set_frequency(&mut self, frequency: u32) { | ||
| 205 | assert!(self.state() == RadioState::DISABLED); | ||
| 206 | assert!((2400..=2500).contains(&frequency)); | ||
| 207 | |||
| 208 | let r = T::regs(); | ||
| 209 | |||
| 210 | r.frequency | ||
| 211 | .write(|w| unsafe { w.frequency().bits((frequency - 2400) as u8) }); | ||
| 212 | } | ||
| 213 | |||
| 214 | /// Set the acess address | ||
| 215 | /// This address is always constants for advertising | ||
| 216 | /// And a random value generate on each connection | ||
| 217 | /// It is used to filter the packages | ||
| 218 | /// | ||
| 219 | /// The radio must be disabled before calling this function | ||
| 220 | pub fn set_access_address(&mut self, access_address: u32) { | ||
| 221 | assert!(self.state() == RadioState::DISABLED); | ||
| 222 | |||
| 223 | let r = T::regs(); | ||
| 224 | |||
| 225 | // Configure logical address | ||
| 226 | // The byte ordering on air is always least significant byte first for the address | ||
| 227 | // So for the address 0xAA_BB_CC_DD, the address on air will be DD CC BB AA | ||
| 228 | // The package order is BASE, PREFIX so BASE=0xBB_CC_DD and PREFIX=0xAA | ||
| 229 | r.prefix0 | ||
| 230 | .write(|w| unsafe { w.ap0().bits((access_address >> 24) as u8) }); | ||
| 231 | |||
| 232 | // The base address is truncated from the least significant byte (because the BALEN is less than 4) | ||
| 233 | // So it shifts the address to the right | ||
| 234 | r.base0.write(|w| unsafe { w.bits(access_address << 8) }); | ||
| 235 | |||
| 236 | // Don't match tx address | ||
| 237 | r.txaddress.write(|w| unsafe { w.txaddress().bits(0) }); | ||
| 238 | |||
| 239 | // Match on logical address | ||
| 240 | // This config only filter the packets by the address, | ||
| 241 | // so only packages send to the previous address | ||
| 242 | // will finish the reception (TODO: check the explanation) | ||
| 243 | r.rxaddresses.write(|w| { | ||
| 244 | w.addr0() | ||
| 245 | .enabled() | ||
| 246 | .addr1() | ||
| 247 | .enabled() | ||
| 248 | .addr2() | ||
| 249 | .enabled() | ||
| 250 | .addr3() | ||
| 251 | .enabled() | ||
| 252 | .addr4() | ||
| 253 | .enabled() | ||
| 254 | }); | ||
| 255 | } | ||
| 256 | |||
| 257 | /// Set the CRC polynomial | ||
| 258 | /// It only uses the 24 least significant bits | ||
| 259 | /// | ||
| 260 | /// The radio must be disabled before calling this function | ||
| 261 | pub fn set_crc_poly(&mut self, crc_poly: u32) { | ||
| 262 | assert!(self.state() == RadioState::DISABLED); | ||
| 263 | |||
| 264 | let r = T::regs(); | ||
| 265 | |||
| 266 | r.crcpoly.write(|w| unsafe { | ||
| 267 | // Configure the CRC polynomial | ||
| 268 | // Each term in the CRC polynomial is mapped to a bit in this | ||
| 269 | // register which index corresponds to the term's exponent. | ||
| 270 | // The least significant term/bit is hard-wired internally to | ||
| 271 | // 1, and bit number 0 of the register content is ignored by | ||
| 272 | // the hardware. The following example is for an 8 bit CRC | ||
| 273 | // polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 . | ||
| 274 | w.crcpoly().bits(crc_poly & 0xFFFFFF) | ||
| 275 | }); | ||
| 276 | } | ||
| 277 | |||
| 278 | /// Set the CRC init value | ||
| 279 | /// It only uses the 24 least significant bits | ||
| 280 | /// The CRC initial value varies depending of the PDU type | ||
| 281 | /// | ||
| 282 | /// The radio must be disabled before calling this function | ||
| 283 | pub fn set_crc_init(&mut self, crc_init: u32) { | ||
| 284 | assert!(self.state() == RadioState::DISABLED); | ||
| 285 | |||
| 286 | let r = T::regs(); | ||
| 287 | |||
| 288 | r.crcinit.write(|w| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) }); | ||
| 289 | } | ||
| 290 | |||
| 291 | /// Set the radio tx power | ||
| 292 | /// | ||
| 293 | /// The radio must be disabled before calling this function | ||
| 294 | pub fn set_tx_power(&mut self, tx_power: TxPower) { | ||
| 295 | assert!(self.state() == RadioState::DISABLED); | ||
| 296 | |||
| 297 | let r = T::regs(); | ||
| 298 | |||
| 299 | r.txpower.write(|w| w.txpower().variant(tx_power)); | ||
| 300 | } | ||
| 301 | |||
| 302 | /// Set buffer to read/write | ||
| 303 | /// | ||
| 304 | /// This method is unsound. You should guarantee that the buffer will live | ||
| 305 | /// for the life time of the transmission or if the buffer will be modified. | ||
| 306 | /// Also if the buffer is smaller than the packet length, the radio will | ||
| 307 | /// read/write memory out of the buffer bounds. | ||
| 308 | fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { | ||
| 309 | slice_in_ram_or(buffer, Error::BufferNotInRAM)?; | ||
| 310 | |||
| 311 | let r = T::regs(); | ||
| 312 | |||
| 313 | // Here it consider that the length of the packet is | ||
| 314 | // correctly set in the buffer, otherwise it will send | ||
| 315 | // unowned regions of memory | ||
| 316 | let ptr = buffer.as_ptr(); | ||
| 317 | |||
| 318 | // Configure the payload | ||
| 319 | r.packetptr.write(|w| unsafe { w.bits(ptr as u32) }); | ||
| 320 | |||
| 321 | Ok(()) | ||
| 322 | } | ||
| 323 | |||
| 324 | /// Send packet | ||
| 325 | /// If the length byte in the package is greater than the buffer length | ||
| 326 | /// the radio will read memory out of the buffer bounds | ||
| 327 | pub async fn transmit(&mut self, buffer: &[u8]) -> Result<(), Error> { | ||
| 328 | self.set_buffer(buffer)?; | ||
| 329 | |||
| 330 | let r = T::regs(); | ||
| 331 | self.trigger_and_wait_end(move || { | ||
| 332 | // Initialize the transmission | ||
| 333 | // trace!("txen"); | ||
| 334 | r.tasks_txen.write(|w| w.tasks_txen().set_bit()); | ||
| 335 | }) | ||
| 336 | .await; | ||
| 337 | |||
| 338 | Ok(()) | ||
| 339 | } | ||
| 340 | |||
| 341 | /// Receive packet | ||
| 342 | /// If the length byte in the received package is greater than the buffer length | ||
| 343 | /// the radio will write memory out of the buffer bounds | ||
| 344 | pub async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | ||
| 345 | self.set_buffer(buffer)?; | ||
| 346 | |||
| 347 | let r = T::regs(); | ||
| 348 | self.trigger_and_wait_end(move || { | ||
| 349 | // Initialize the transmission | ||
| 350 | // trace!("rxen"); | ||
| 351 | r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()); | ||
| 352 | }) | ||
| 353 | .await; | ||
| 354 | |||
| 355 | Ok(()) | ||
| 356 | } | ||
| 357 | |||
| 358 | async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) { | ||
| 359 | //self.trace_state(); | ||
| 360 | |||
| 361 | let r = T::regs(); | ||
| 362 | let s = T::state(); | ||
| 363 | |||
| 364 | // If the Future is dropped before the end of the transmission | ||
| 365 | // it disable the interrupt and stop the transmission | ||
| 366 | // to keep the state consistent | ||
| 367 | let drop = OnDrop::new(|| { | ||
| 368 | trace!("radio drop: stopping"); | ||
| 369 | |||
| 370 | r.intenclr.write(|w| w.end().clear()); | ||
| 371 | r.events_end.reset(); | ||
| 372 | |||
| 373 | r.tasks_stop.write(|w| w.tasks_stop().set_bit()); | ||
| 374 | |||
| 375 | // The docs don't explicitly mention any event to acknowledge the stop task | ||
| 376 | while r.events_end.read().events_end().bit_is_clear() {} | ||
| 377 | |||
| 378 | trace!("radio drop: stopped"); | ||
| 379 | }); | ||
| 380 | |||
| 381 | // trace!("radio:enable interrupt"); | ||
| 382 | // Clear some remnant side-effects (TODO: check if this is necessary) | ||
| 383 | r.events_end.reset(); | ||
| 384 | |||
| 385 | // Enable interrupt | ||
| 386 | r.intenset.write(|w| w.end().set()); | ||
| 387 | |||
| 388 | compiler_fence(Ordering::SeqCst); | ||
| 389 | |||
| 390 | // Trigger the transmission | ||
| 391 | trigger(); | ||
| 392 | // self.trace_state(); | ||
| 393 | |||
| 394 | // On poll check if interrupt happen | ||
| 395 | poll_fn(|cx| { | ||
| 396 | s.end_waker.register(cx.waker()); | ||
| 397 | if r.events_end.read().events_end().bit_is_set() { | ||
| 398 | // trace!("radio:end"); | ||
| 399 | return core::task::Poll::Ready(()); | ||
| 400 | } | ||
| 401 | Poll::Pending | ||
| 402 | }) | ||
| 403 | .await; | ||
| 404 | |||
| 405 | compiler_fence(Ordering::SeqCst); | ||
| 406 | r.events_disabled.reset(); // ACK | ||
| 407 | |||
| 408 | // Everthing ends fine, so it disable the drop | ||
| 409 | drop.defuse(); | ||
| 410 | } | ||
| 411 | |||
| 412 | /// Disable the radio | ||
| 413 | fn disable(&mut self) { | ||
| 414 | let r = T::regs(); | ||
| 415 | |||
| 416 | compiler_fence(Ordering::SeqCst); | ||
| 417 | // If it is already disabled, do nothing | ||
| 418 | if self.state() != RadioState::DISABLED { | ||
| 419 | trace!("radio:disable"); | ||
| 420 | // Trigger the disable task | ||
| 421 | r.tasks_disable.write(|w| w.tasks_disable().set_bit()); | ||
| 422 | |||
| 423 | // Wait until the radio is disabled | ||
| 424 | while r.events_disabled.read().events_disabled().bit_is_clear() {} | ||
| 425 | |||
| 426 | compiler_fence(Ordering::SeqCst); | ||
| 427 | |||
| 428 | // Acknowledge it | ||
| 429 | r.events_disabled.reset(); | ||
| 430 | } | ||
| 431 | } | ||
| 432 | } | ||
| 433 | |||
| 434 | impl<'d, T: Instance> Drop for Radio<'d, T> { | ||
| 435 | fn drop(&mut self) { | ||
| 436 | self.disable(); | ||
| 437 | } | ||
| 438 | } | ||
diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs new file mode 100644 index 000000000..03f967f87 --- /dev/null +++ b/embassy-nrf/src/radio/mod.rs | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | //! Integrated 2.4 GHz Radio | ||
| 2 | //! | ||
| 3 | //! The 2.4 GHz radio transceiver is compatible with multiple radio standards | ||
| 4 | //! such as 1Mbps, 2Mbps and Long Range Bluetooth Low Energy. | ||
| 5 | |||
| 6 | #![macro_use] | ||
| 7 | |||
| 8 | /// Bluetooth Low Energy Radio driver. | ||
| 9 | pub mod ble; | ||
| 10 | |||
| 11 | use core::marker::PhantomData; | ||
| 12 | |||
| 13 | use crate::{interrupt, pac, Peripheral}; | ||
| 14 | |||
| 15 | /// Interrupt handler | ||
| 16 | pub struct InterruptHandler<T: Instance> { | ||
| 17 | _phantom: PhantomData<T>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||
| 21 | unsafe fn on_interrupt() { | ||
| 22 | let r = T::regs(); | ||
| 23 | let s = T::state(); | ||
| 24 | |||
| 25 | if r.events_end.read().events_end().bit_is_set() { | ||
| 26 | s.end_waker.wake(); | ||
| 27 | r.intenclr.write(|w| w.end().clear()); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | pub(crate) mod sealed { | ||
| 33 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 34 | |||
| 35 | pub struct State { | ||
| 36 | /// end packet transmission or reception | ||
| 37 | pub end_waker: AtomicWaker, | ||
| 38 | } | ||
| 39 | impl State { | ||
| 40 | pub const fn new() -> Self { | ||
| 41 | Self { | ||
| 42 | end_waker: AtomicWaker::new(), | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | pub trait Instance { | ||
| 48 | fn regs() -> &'static crate::pac::radio::RegisterBlock; | ||
| 49 | fn state() -> &'static State; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | macro_rules! impl_radio { | ||
| 54 | ($type:ident, $pac_type:ident, $irq:ident) => { | ||
| 55 | impl crate::radio::sealed::Instance for peripherals::$type { | ||
| 56 | fn regs() -> &'static pac::radio::RegisterBlock { | ||
| 57 | unsafe { &*pac::$pac_type::ptr() } | ||
| 58 | } | ||
| 59 | |||
| 60 | fn state() -> &'static crate::radio::sealed::State { | ||
| 61 | static STATE: crate::radio::sealed::State = crate::radio::sealed::State::new(); | ||
| 62 | &STATE | ||
| 63 | } | ||
| 64 | } | ||
| 65 | impl crate::radio::Instance for peripherals::$type { | ||
| 66 | type Interrupt = crate::interrupt::typelevel::$irq; | ||
| 67 | } | ||
| 68 | }; | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Radio peripheral instance. | ||
| 72 | pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { | ||
| 73 | /// Interrupt for this peripheral. | ||
| 74 | type Interrupt: interrupt::typelevel::Interrupt; | ||
| 75 | } | ||
