diff options
| -rw-r--r-- | embassy-nrf/src/chips/nrf51.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52805.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52810.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52811.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52820.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52832.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52833.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf5340_net.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/ble.rs | 63 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/ieee802154.rs | 546 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/mod.rs | 49 | ||||
| -rw-r--r-- | embassy-nrf/src/util.rs | 1 |
13 files changed, 650 insertions, 51 deletions
diff --git a/embassy-nrf/src/chips/nrf51.rs b/embassy-nrf/src/chips/nrf51.rs index 016352fb8..cc1cbc8a0 100644 --- a/embassy-nrf/src/chips/nrf51.rs +++ b/embassy-nrf/src/chips/nrf51.rs | |||
| @@ -99,6 +99,9 @@ embassy_hal_internal::peripherals! { | |||
| 99 | 99 | ||
| 100 | // TEMP | 100 | // TEMP |
| 101 | TEMP, | 101 | TEMP, |
| 102 | |||
| 103 | // Radio | ||
| 104 | RADIO, | ||
| 102 | } | 105 | } |
| 103 | 106 | ||
| 104 | impl_timer!(TIMER0, TIMER0, TIMER0); | 107 | impl_timer!(TIMER0, TIMER0, TIMER0); |
| @@ -140,6 +143,8 @@ impl_pin!(P0_29, 0, 29); | |||
| 140 | impl_pin!(P0_30, 0, 30); | 143 | impl_pin!(P0_30, 0, 30); |
| 141 | impl_pin!(P0_31, 0, 31); | 144 | impl_pin!(P0_31, 0, 31); |
| 142 | 145 | ||
| 146 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 147 | |||
| 143 | embassy_hal_internal::interrupt_mod!( | 148 | embassy_hal_internal::interrupt_mod!( |
| 144 | POWER_CLOCK, | 149 | POWER_CLOCK, |
| 145 | RADIO, | 150 | RADIO, |
diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 624d6613d..14c3f9b1a 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs | |||
| @@ -129,6 +129,9 @@ embassy_hal_internal::peripherals! { | |||
| 129 | 129 | ||
| 130 | // QDEC | 130 | // QDEC |
| 131 | QDEC, | 131 | QDEC, |
| 132 | |||
| 133 | // Radio | ||
| 134 | RADIO, | ||
| 132 | } | 135 | } |
| 133 | 136 | ||
| 134 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 137 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
| @@ -209,6 +212,8 @@ impl_ppi_channel!(PPI_CH31, 31 => static); | |||
| 209 | impl_saadc_input!(P0_04, ANALOG_INPUT2); | 212 | impl_saadc_input!(P0_04, ANALOG_INPUT2); |
| 210 | impl_saadc_input!(P0_05, ANALOG_INPUT3); | 213 | impl_saadc_input!(P0_05, ANALOG_INPUT3); |
| 211 | 214 | ||
| 215 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 216 | |||
| 212 | embassy_hal_internal::interrupt_mod!( | 217 | embassy_hal_internal::interrupt_mod!( |
| 213 | POWER_CLOCK, | 218 | POWER_CLOCK, |
| 214 | RADIO, | 219 | RADIO, |
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 002feab3b..c607586db 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs | |||
| @@ -135,6 +135,9 @@ embassy_hal_internal::peripherals! { | |||
| 135 | 135 | ||
| 136 | // PDM | 136 | // PDM |
| 137 | PDM, | 137 | PDM, |
| 138 | |||
| 139 | // Radio | ||
| 140 | RADIO, | ||
| 138 | } | 141 | } |
| 139 | 142 | ||
| 140 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 143 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
| @@ -235,6 +238,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); | |||
| 235 | impl_saadc_input!(P0_30, ANALOG_INPUT6); | 238 | impl_saadc_input!(P0_30, ANALOG_INPUT6); |
| 236 | impl_saadc_input!(P0_31, ANALOG_INPUT7); | 239 | impl_saadc_input!(P0_31, ANALOG_INPUT7); |
| 237 | 240 | ||
| 241 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 242 | |||
| 238 | embassy_hal_internal::interrupt_mod!( | 243 | embassy_hal_internal::interrupt_mod!( |
| 239 | POWER_CLOCK, | 244 | POWER_CLOCK, |
| 240 | RADIO, | 245 | RADIO, |
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 5952907f8..5f70365b4 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs | |||
| @@ -135,6 +135,9 @@ embassy_hal_internal::peripherals! { | |||
| 135 | 135 | ||
| 136 | // PDM | 136 | // PDM |
| 137 | PDM, | 137 | PDM, |
| 138 | |||
| 139 | // Radio | ||
| 140 | RADIO, | ||
| 138 | } | 141 | } |
| 139 | 142 | ||
| 140 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 143 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
| @@ -237,6 +240,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); | |||
| 237 | impl_saadc_input!(P0_30, ANALOG_INPUT6); | 240 | impl_saadc_input!(P0_30, ANALOG_INPUT6); |
| 238 | impl_saadc_input!(P0_31, ANALOG_INPUT7); | 241 | impl_saadc_input!(P0_31, ANALOG_INPUT7); |
| 239 | 242 | ||
| 243 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 244 | |||
| 240 | embassy_hal_internal::interrupt_mod!( | 245 | embassy_hal_internal::interrupt_mod!( |
| 241 | POWER_CLOCK, | 246 | POWER_CLOCK, |
| 242 | RADIO, | 247 | RADIO, |
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index c2f792cb9..82d097407 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs | |||
| @@ -130,6 +130,9 @@ embassy_hal_internal::peripherals! { | |||
| 130 | 130 | ||
| 131 | // QDEC | 131 | // QDEC |
| 132 | QDEC, | 132 | QDEC, |
| 133 | |||
| 134 | // Radio | ||
| 135 | RADIO, | ||
| 133 | } | 136 | } |
| 134 | 137 | ||
| 135 | impl_usb!(USBD, USBD, USBD); | 138 | impl_usb!(USBD, USBD, USBD); |
| @@ -224,6 +227,8 @@ impl_ppi_channel!(PPI_CH29, 29 => static); | |||
| 224 | impl_ppi_channel!(PPI_CH30, 30 => static); | 227 | impl_ppi_channel!(PPI_CH30, 30 => static); |
| 225 | impl_ppi_channel!(PPI_CH31, 31 => static); | 228 | impl_ppi_channel!(PPI_CH31, 31 => static); |
| 226 | 229 | ||
| 230 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 231 | |||
| 227 | embassy_hal_internal::interrupt_mod!( | 232 | embassy_hal_internal::interrupt_mod!( |
| 228 | POWER_CLOCK, | 233 | POWER_CLOCK, |
| 229 | RADIO, | 234 | RADIO, |
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 65d52364d..67b32fe5f 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs | |||
| @@ -150,6 +150,9 @@ embassy_hal_internal::peripherals! { | |||
| 150 | 150 | ||
| 151 | // PDM | 151 | // PDM |
| 152 | PDM, | 152 | PDM, |
| 153 | |||
| 154 | // Radio | ||
| 155 | RADIO, | ||
| 153 | } | 156 | } |
| 154 | 157 | ||
| 155 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 158 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
| @@ -264,6 +267,8 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); | |||
| 264 | 267 | ||
| 265 | impl_i2s!(I2S, I2S, I2S); | 268 | impl_i2s!(I2S, I2S, I2S); |
| 266 | 269 | ||
| 270 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 271 | |||
| 267 | embassy_hal_internal::interrupt_mod!( | 272 | embassy_hal_internal::interrupt_mod!( |
| 268 | POWER_CLOCK, | 273 | POWER_CLOCK, |
| 269 | RADIO, | 274 | RADIO, |
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 7c9b66d69..20f14e2d6 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs | |||
| @@ -170,6 +170,9 @@ embassy_hal_internal::peripherals! { | |||
| 170 | 170 | ||
| 171 | // I2S | 171 | // I2S |
| 172 | I2S, | 172 | I2S, |
| 173 | |||
| 174 | // Radio | ||
| 175 | RADIO, | ||
| 173 | } | 176 | } |
| 174 | 177 | ||
| 175 | impl_usb!(USBD, USBD, USBD); | 178 | impl_usb!(USBD, USBD, USBD); |
| @@ -306,6 +309,8 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); | |||
| 306 | 309 | ||
| 307 | impl_i2s!(I2S, I2S, I2S); | 310 | impl_i2s!(I2S, I2S, I2S); |
| 308 | 311 | ||
| 312 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 313 | |||
| 309 | embassy_hal_internal::interrupt_mod!( | 314 | embassy_hal_internal::interrupt_mod!( |
| 310 | POWER_CLOCK, | 315 | POWER_CLOCK, |
| 311 | RADIO, | 316 | RADIO, |
diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index a7cf82872..65e8f9653 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! { | |||
| 248 | P1_13, | 248 | P1_13, |
| 249 | P1_14, | 249 | P1_14, |
| 250 | P1_15, | 250 | P1_15, |
| 251 | |||
| 252 | // Radio | ||
| 253 | RADIO, | ||
| 251 | } | 254 | } |
| 252 | 255 | ||
| 253 | impl_uarte!(SERIAL0, UARTE0, SERIAL0); | 256 | impl_uarte!(SERIAL0, UARTE0, SERIAL0); |
| @@ -345,6 +348,8 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); | |||
| 345 | impl_ppi_channel!(PPI_CH30, 30 => configurable); | 348 | impl_ppi_channel!(PPI_CH30, 30 => configurable); |
| 346 | impl_ppi_channel!(PPI_CH31, 31 => configurable); | 349 | impl_ppi_channel!(PPI_CH31, 31 => configurable); |
| 347 | 350 | ||
| 351 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 352 | |||
| 348 | embassy_hal_internal::interrupt_mod!( | 353 | embassy_hal_internal::interrupt_mod!( |
| 349 | CLOCK_POWER, | 354 | CLOCK_POWER, |
| 350 | RADIO, | 355 | RADIO, |
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 04a6293a4..718f229a3 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -47,7 +47,7 @@ pub mod gpio; | |||
| 47 | pub mod gpiote; | 47 | pub mod gpiote; |
| 48 | 48 | ||
| 49 | // TODO: tested on other chips | 49 | // TODO: tested on other chips |
| 50 | #[cfg(any(feature = "nrf52840"))] | 50 | #[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340-app")))] |
| 51 | pub mod radio; | 51 | pub mod radio; |
| 52 | 52 | ||
| 53 | #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] | 53 | #[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 24dba582f..93003fb19 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs | |||
| @@ -7,27 +7,14 @@ use core::task::Poll; | |||
| 7 | use embassy_hal_internal::drop::OnDrop; | 7 | use embassy_hal_internal::drop::OnDrop; |
| 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
| 9 | pub use pac::radio::mode::MODE_A as Mode; | 9 | pub use pac::radio::mode::MODE_A as Mode; |
| 10 | #[cfg(not(feature = "nrf51"))] | ||
| 10 | use pac::radio::pcnf0::PLEN_A as PreambleLength; | 11 | 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 | 12 | ||
| 14 | use crate::interrupt::typelevel::Interrupt; | 13 | use crate::interrupt::typelevel::Interrupt; |
| 15 | use crate::radio::*; | 14 | use crate::radio::*; |
| 15 | pub use crate::radio::{Error, TxPower}; | ||
| 16 | use crate::util::slice_in_ram_or; | 16 | use crate::util::slice_in_ram_or; |
| 17 | 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. | 18 | /// Radio driver. |
| 32 | pub struct Radio<'d, T: Instance> { | 19 | pub struct Radio<'d, T: Instance> { |
| 33 | _p: PeripheralRef<'d, T>, | 20 | _p: PeripheralRef<'d, T>, |
| @@ -98,6 +85,7 @@ impl<'d, T: Instance> Radio<'d, T> { | |||
| 98 | 85 | ||
| 99 | // Ch map between 2400 MHZ .. 2500 MHz | 86 | // Ch map between 2400 MHZ .. 2500 MHz |
| 100 | // All modes use this range | 87 | // All modes use this range |
| 88 | #[cfg(not(feature = "nrf51"))] | ||
| 101 | r.frequency.write(|w| w.map().default()); | 89 | r.frequency.write(|w| w.map().default()); |
| 102 | 90 | ||
| 103 | // Configure shortcuts to simplify and speed up sending and receiving packets. | 91 | // Configure shortcuts to simplify and speed up sending and receiving packets. |
| @@ -115,25 +103,7 @@ impl<'d, T: Instance> Radio<'d, T> { | |||
| 115 | } | 103 | } |
| 116 | 104 | ||
| 117 | fn state(&self) -> RadioState { | 105 | fn state(&self) -> RadioState { |
| 118 | match T::regs().state.read().state().variant() { | 106 | super::state(T::regs()) |
| 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 | } | 107 | } |
| 138 | 108 | ||
| 139 | /// Set the radio mode | 109 | /// Set the radio mode |
| @@ -145,10 +115,18 @@ impl<'d, T: Instance> Radio<'d, T> { | |||
| 145 | let r = T::regs(); | 115 | let r = T::regs(); |
| 146 | r.mode.write(|w| w.mode().variant(mode)); | 116 | r.mode.write(|w| w.mode().variant(mode)); |
| 147 | 117 | ||
| 118 | #[cfg(not(feature = "nrf51"))] | ||
| 148 | r.pcnf0.write(|w| { | 119 | r.pcnf0.write(|w| { |
| 149 | w.plen().variant(match mode { | 120 | w.plen().variant(match mode { |
| 150 | Mode::BLE_1MBIT => PreambleLength::_8BIT, | 121 | Mode::BLE_1MBIT => PreambleLength::_8BIT, |
| 151 | Mode::BLE_2MBIT => PreambleLength::_16BIT, | 122 | Mode::BLE_2MBIT => PreambleLength::_16BIT, |
| 123 | #[cfg(any( | ||
| 124 | feature = "nrf52811", | ||
| 125 | feature = "nrf52820", | ||
| 126 | feature = "nrf52833", | ||
| 127 | feature = "nrf52840", | ||
| 128 | feature = "_nrf5340-net" | ||
| 129 | ))] | ||
| 152 | Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE, | 130 | Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE, |
| 153 | _ => unimplemented!(), | 131 | _ => unimplemented!(), |
| 154 | }) | 132 | }) |
| @@ -331,7 +309,8 @@ impl<'d, T: Instance> Radio<'d, T> { | |||
| 331 | self.trigger_and_wait_end(move || { | 309 | self.trigger_and_wait_end(move || { |
| 332 | // Initialize the transmission | 310 | // Initialize the transmission |
| 333 | // trace!("txen"); | 311 | // trace!("txen"); |
| 334 | r.tasks_txen.write(|w| w.tasks_txen().set_bit()); | 312 | |
| 313 | r.tasks_txen.write(|w| unsafe { w.bits(1) }); | ||
| 335 | }) | 314 | }) |
| 336 | .await; | 315 | .await; |
| 337 | 316 | ||
| @@ -348,7 +327,7 @@ impl<'d, T: Instance> Radio<'d, T> { | |||
| 348 | self.trigger_and_wait_end(move || { | 327 | self.trigger_and_wait_end(move || { |
| 349 | // Initialize the transmission | 328 | // Initialize the transmission |
| 350 | // trace!("rxen"); | 329 | // trace!("rxen"); |
| 351 | r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()); | 330 | r.tasks_rxen.write(|w| unsafe { w.bits(1) }); |
| 352 | }) | 331 | }) |
| 353 | .await; | 332 | .await; |
| 354 | 333 | ||
| @@ -370,10 +349,10 @@ impl<'d, T: Instance> Radio<'d, T> { | |||
| 370 | r.intenclr.write(|w| w.end().clear()); | 349 | r.intenclr.write(|w| w.end().clear()); |
| 371 | r.events_end.reset(); | 350 | r.events_end.reset(); |
| 372 | 351 | ||
| 373 | r.tasks_stop.write(|w| w.tasks_stop().set_bit()); | 352 | r.tasks_stop.write(|w| unsafe { w.bits(1) }); |
| 374 | 353 | ||
| 375 | // The docs don't explicitly mention any event to acknowledge the stop task | 354 | // The docs don't explicitly mention any event to acknowledge the stop task |
| 376 | while r.events_end.read().events_end().bit_is_clear() {} | 355 | while r.events_end.read().bits() == 0 {} |
| 377 | 356 | ||
| 378 | trace!("radio drop: stopped"); | 357 | trace!("radio drop: stopped"); |
| 379 | }); | 358 | }); |
| @@ -393,8 +372,8 @@ impl<'d, T: Instance> Radio<'d, T> { | |||
| 393 | 372 | ||
| 394 | // On poll check if interrupt happen | 373 | // On poll check if interrupt happen |
| 395 | poll_fn(|cx| { | 374 | poll_fn(|cx| { |
| 396 | s.end_waker.register(cx.waker()); | 375 | s.event_waker.register(cx.waker()); |
| 397 | if r.events_end.read().events_end().bit_is_set() { | 376 | if r.events_end.read().bits() == 1 { |
| 398 | // trace!("radio:end"); | 377 | // trace!("radio:end"); |
| 399 | return core::task::Poll::Ready(()); | 378 | return core::task::Poll::Ready(()); |
| 400 | } | 379 | } |
| @@ -418,10 +397,10 @@ impl<'d, T: Instance> Radio<'d, T> { | |||
| 418 | if self.state() != RadioState::DISABLED { | 397 | if self.state() != RadioState::DISABLED { |
| 419 | trace!("radio:disable"); | 398 | trace!("radio:disable"); |
| 420 | // Trigger the disable task | 399 | // Trigger the disable task |
| 421 | r.tasks_disable.write(|w| w.tasks_disable().set_bit()); | 400 | r.tasks_disable.write(|w| unsafe { w.bits(1) }); |
| 422 | 401 | ||
| 423 | // Wait until the radio is disabled | 402 | // Wait until the radio is disabled |
| 424 | while r.events_disabled.read().events_disabled().bit_is_clear() {} | 403 | while r.events_disabled.read().bits() == 0 {} |
| 425 | 404 | ||
| 426 | compiler_fence(Ordering::SeqCst); | 405 | compiler_fence(Ordering::SeqCst); |
| 427 | 406 | ||
diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs new file mode 100644 index 000000000..298f8a574 --- /dev/null +++ b/embassy-nrf/src/radio/ieee802154.rs | |||
| @@ -0,0 +1,546 @@ | |||
| 1 | //! IEEE 802.15.4 radio driver | ||
| 2 | |||
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 4 | use core::task::Poll; | ||
| 5 | |||
| 6 | use embassy_hal_internal::drop::OnDrop; | ||
| 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 8 | |||
| 9 | use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower}; | ||
| 10 | use crate::interrupt::typelevel::Interrupt; | ||
| 11 | use crate::interrupt::{self}; | ||
| 12 | use crate::Peripheral; | ||
| 13 | |||
| 14 | /// Default (IEEE compliant) Start of Frame Delimiter | ||
| 15 | pub const DEFAULT_SFD: u8 = 0xA7; | ||
| 16 | |||
| 17 | // TODO expose the other variants in `pac::CCAMODE_A` | ||
| 18 | /// Clear Channel Assessment method | ||
| 19 | pub enum Cca { | ||
| 20 | /// Carrier sense | ||
| 21 | CarrierSense, | ||
| 22 | /// Energy Detection / Energy Above Threshold | ||
| 23 | EnergyDetection { | ||
| 24 | /// Energy measurements above this value mean that the channel is assumed to be busy. | ||
| 25 | /// Note the measurement range is 0..0xFF - where 0 means that the received power was | ||
| 26 | /// less than 10 dB above the selected receiver sensitivity. This value is not given in dBm, | ||
| 27 | /// but can be converted. See the nrf52840 Product Specification Section 6.20.12.4 | ||
| 28 | /// for details. | ||
| 29 | ed_threshold: u8, | ||
| 30 | }, | ||
| 31 | } | ||
| 32 | |||
| 33 | /// IEEE 802.15.4 radio driver. | ||
| 34 | pub struct Radio<'d, T: Instance> { | ||
| 35 | _p: PeripheralRef<'d, T>, | ||
| 36 | needs_enable: bool, | ||
| 37 | } | ||
| 38 | |||
| 39 | impl<'d, T: Instance> Radio<'d, T> { | ||
| 40 | /// Create a new IEEE 802.15.4 radio driver. | ||
| 41 | pub fn new( | ||
| 42 | radio: impl Peripheral<P = T> + 'd, | ||
| 43 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 44 | ) -> Self { | ||
| 45 | into_ref!(radio); | ||
| 46 | |||
| 47 | let r = T::regs(); | ||
| 48 | |||
| 49 | // Disable and enable to reset peripheral | ||
| 50 | r.power.write(|w| w.power().disabled()); | ||
| 51 | r.power.write(|w| w.power().enabled()); | ||
| 52 | |||
| 53 | // Enable 802.15.4 mode | ||
| 54 | r.mode.write(|w| w.mode().ieee802154_250kbit()); | ||
| 55 | // Configure CRC skip address | ||
| 56 | r.crccnf.write(|w| w.len().two().skipaddr().ieee802154()); | ||
| 57 | unsafe { | ||
| 58 | // Configure CRC polynomial and init | ||
| 59 | r.crcpoly.write(|w| w.crcpoly().bits(0x0001_1021)); | ||
| 60 | r.crcinit.write(|w| w.crcinit().bits(0)); | ||
| 61 | r.pcnf0.write(|w| { | ||
| 62 | // 8-bit on air length | ||
| 63 | w.lflen() | ||
| 64 | .bits(8) | ||
| 65 | // Zero bytes S0 field length | ||
| 66 | .s0len() | ||
| 67 | .clear_bit() | ||
| 68 | // Zero bytes S1 field length | ||
| 69 | .s1len() | ||
| 70 | .bits(0) | ||
| 71 | // Do not include S1 field in RAM if S1 length > 0 | ||
| 72 | .s1incl() | ||
| 73 | .clear_bit() | ||
| 74 | // Zero code Indicator length | ||
| 75 | .cilen() | ||
| 76 | .bits(0) | ||
| 77 | // 32-bit zero preamble | ||
| 78 | .plen() | ||
| 79 | ._32bit_zero() | ||
| 80 | // Include CRC in length | ||
| 81 | .crcinc() | ||
| 82 | .include() | ||
| 83 | }); | ||
| 84 | r.pcnf1.write(|w| { | ||
| 85 | // Maximum packet length | ||
| 86 | w.maxlen() | ||
| 87 | .bits(Packet::MAX_PSDU_LEN) | ||
| 88 | // Zero static length | ||
| 89 | .statlen() | ||
| 90 | .bits(0) | ||
| 91 | // Zero base address length | ||
| 92 | .balen() | ||
| 93 | .bits(0) | ||
| 94 | // Little-endian | ||
| 95 | .endian() | ||
| 96 | .clear_bit() | ||
| 97 | // Disable packet whitening | ||
| 98 | .whiteen() | ||
| 99 | .clear_bit() | ||
| 100 | }); | ||
| 101 | } | ||
| 102 | |||
| 103 | // Enable NVIC interrupt | ||
| 104 | T::Interrupt::unpend(); | ||
| 105 | unsafe { T::Interrupt::enable() }; | ||
| 106 | |||
| 107 | let mut radio = Self { | ||
| 108 | _p: radio, | ||
| 109 | needs_enable: false, | ||
| 110 | }; | ||
| 111 | |||
| 112 | radio.set_sfd(DEFAULT_SFD); | ||
| 113 | radio.set_transmission_power(0); | ||
| 114 | radio.set_channel(11); | ||
| 115 | radio.set_cca(Cca::CarrierSense); | ||
| 116 | |||
| 117 | radio | ||
| 118 | } | ||
| 119 | |||
| 120 | /// Changes the radio channel | ||
| 121 | pub fn set_channel(&mut self, channel: u8) { | ||
| 122 | let r = T::regs(); | ||
| 123 | if channel < 11 || channel > 26 { | ||
| 124 | panic!("Bad 802.15.4 channel"); | ||
| 125 | } | ||
| 126 | let frequency_offset = (channel - 10) * 5; | ||
| 127 | self.needs_enable = true; | ||
| 128 | r.frequency | ||
| 129 | .write(|w| unsafe { w.frequency().bits(frequency_offset).map().default() }); | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Changes the Clear Channel Assessment method | ||
| 133 | pub fn set_cca(&mut self, cca: Cca) { | ||
| 134 | let r = T::regs(); | ||
| 135 | self.needs_enable = true; | ||
| 136 | match cca { | ||
| 137 | Cca::CarrierSense => r.ccactrl.write(|w| w.ccamode().carrier_mode()), | ||
| 138 | Cca::EnergyDetection { ed_threshold } => { | ||
| 139 | // "[ED] is enabled by first configuring the field CCAMODE=EdMode in CCACTRL | ||
| 140 | // and writing the CCAEDTHRES field to a chosen value." | ||
| 141 | r.ccactrl | ||
| 142 | .write(|w| unsafe { w.ccamode().ed_mode().ccaedthres().bits(ed_threshold) }); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Changes the Start of Frame Delimiter (SFD) | ||
| 148 | pub fn set_sfd(&mut self, sfd: u8) { | ||
| 149 | let r = T::regs(); | ||
| 150 | r.sfd.write(|w| unsafe { w.sfd().bits(sfd) }); | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Clear interrupts | ||
| 154 | pub fn clear_all_interrupts(&mut self) { | ||
| 155 | let r = T::regs(); | ||
| 156 | r.intenclr.write(|w| unsafe { w.bits(0xffff_ffff) }); | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Changes the radio transmission power | ||
| 160 | pub fn set_transmission_power(&mut self, power: i8) { | ||
| 161 | let r = T::regs(); | ||
| 162 | self.needs_enable = true; | ||
| 163 | |||
| 164 | let tx_power: TxPower = match power { | ||
| 165 | #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] | ||
| 166 | 8 => TxPower::POS8D_BM, | ||
| 167 | #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] | ||
| 168 | 7 => TxPower::POS7D_BM, | ||
| 169 | #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] | ||
| 170 | 6 => TxPower::POS6D_BM, | ||
| 171 | #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] | ||
| 172 | 5 => TxPower::POS5D_BM, | ||
| 173 | #[cfg(not(feature = "_nrf5340-net"))] | ||
| 174 | 4 => TxPower::POS4D_BM, | ||
| 175 | #[cfg(not(feature = "_nrf5340-net"))] | ||
| 176 | 3 => TxPower::POS3D_BM, | ||
| 177 | #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] | ||
| 178 | 2 => TxPower::POS2D_BM, | ||
| 179 | 0 => TxPower::_0D_BM, | ||
| 180 | #[cfg(feature = "_nrf5340-net")] | ||
| 181 | -1 => TxPower::NEG1D_BM, | ||
| 182 | #[cfg(feature = "_nrf5340-net")] | ||
| 183 | -2 => TxPower::NEG2D_BM, | ||
| 184 | #[cfg(feature = "_nrf5340-net")] | ||
| 185 | -3 => TxPower::NEG3D_BM, | ||
| 186 | -4 => TxPower::NEG4D_BM, | ||
| 187 | #[cfg(feature = "_nrf5340-net")] | ||
| 188 | -5 => TxPower::NEG5D_BM, | ||
| 189 | #[cfg(feature = "_nrf5340-net")] | ||
| 190 | -6 => TxPower::NEG6D_BM, | ||
| 191 | #[cfg(feature = "_nrf5340-net")] | ||
| 192 | -7 => TxPower::NEG7D_BM, | ||
| 193 | -8 => TxPower::NEG8D_BM, | ||
| 194 | -12 => TxPower::NEG12D_BM, | ||
| 195 | -16 => TxPower::NEG16D_BM, | ||
| 196 | -20 => TxPower::NEG20D_BM, | ||
| 197 | -30 => TxPower::NEG30D_BM, | ||
| 198 | -40 => TxPower::NEG40D_BM, | ||
| 199 | _ => panic!("Invalid transmission power value"), | ||
| 200 | }; | ||
| 201 | |||
| 202 | r.txpower.write(|w| w.txpower().variant(tx_power)); | ||
| 203 | } | ||
| 204 | |||
| 205 | /// Waits until the radio state matches the given `state` | ||
| 206 | fn wait_for_radio_state(&self, state: RadioState) { | ||
| 207 | while self.state() != state {} | ||
| 208 | } | ||
| 209 | |||
| 210 | /// Get the current radio state | ||
| 211 | fn state(&self) -> RadioState { | ||
| 212 | state(T::regs()) | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Moves the radio from any state to the DISABLED state | ||
| 216 | fn disable(&mut self) { | ||
| 217 | let r = T::regs(); | ||
| 218 | // See figure 110 in nRF52840-PS | ||
| 219 | loop { | ||
| 220 | match self.state() { | ||
| 221 | RadioState::DISABLED => return, | ||
| 222 | // idle or ramping up | ||
| 223 | RadioState::RX_RU | RadioState::RX_IDLE | RadioState::TX_RU | RadioState::TX_IDLE => { | ||
| 224 | r.tasks_disable.write(|w| w.tasks_disable().set_bit()); | ||
| 225 | self.wait_for_radio_state(RadioState::DISABLED); | ||
| 226 | return; | ||
| 227 | } | ||
| 228 | // ramping down | ||
| 229 | RadioState::RX_DISABLE | RadioState::TX_DISABLE => { | ||
| 230 | self.wait_for_radio_state(RadioState::DISABLED); | ||
| 231 | return; | ||
| 232 | } | ||
| 233 | // cancel ongoing transfer or ongoing CCA | ||
| 234 | RadioState::RX => { | ||
| 235 | r.tasks_ccastop.write(|w| w.tasks_ccastop().set_bit()); | ||
| 236 | r.tasks_stop.write(|w| w.tasks_stop().set_bit()); | ||
| 237 | self.wait_for_radio_state(RadioState::RX_IDLE); | ||
| 238 | } | ||
| 239 | RadioState::TX => { | ||
| 240 | r.tasks_stop.write(|w| w.tasks_stop().set_bit()); | ||
| 241 | self.wait_for_radio_state(RadioState::TX_IDLE); | ||
| 242 | } | ||
| 243 | } | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | fn set_buffer(&mut self, buffer: &[u8]) { | ||
| 248 | let r = T::regs(); | ||
| 249 | r.packetptr.write(|w| unsafe { w.bits(buffer.as_ptr() as u32) }); | ||
| 250 | } | ||
| 251 | |||
| 252 | /// Moves the radio to the RXIDLE state | ||
| 253 | fn receive_prepare(&mut self) { | ||
| 254 | // clear related events | ||
| 255 | T::regs().events_ccabusy.reset(); | ||
| 256 | T::regs().events_phyend.reset(); | ||
| 257 | // NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RX_IDLE | ||
| 258 | let disable = match self.state() { | ||
| 259 | RadioState::DISABLED => false, | ||
| 260 | RadioState::RX_IDLE => self.needs_enable, | ||
| 261 | _ => true, | ||
| 262 | }; | ||
| 263 | if disable { | ||
| 264 | self.disable(); | ||
| 265 | } | ||
| 266 | self.needs_enable = false; | ||
| 267 | } | ||
| 268 | |||
| 269 | /// Prepare radio for receiving a packet | ||
| 270 | fn receive_start(&mut self, packet: &mut Packet) { | ||
| 271 | // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's | ||
| 272 | // allocated in RAM | ||
| 273 | let r = T::regs(); | ||
| 274 | |||
| 275 | self.receive_prepare(); | ||
| 276 | |||
| 277 | // Configure shortcuts | ||
| 278 | // | ||
| 279 | // The radio goes through following states when receiving a 802.15.4 packet | ||
| 280 | // | ||
| 281 | // enable RX → ramp up RX → RX idle → Receive → end (PHYEND) | ||
| 282 | r.shorts.write(|w| w.rxready_start().enabled()); | ||
| 283 | |||
| 284 | // set up RX buffer | ||
| 285 | self.set_buffer(packet.buffer.as_mut()); | ||
| 286 | |||
| 287 | // start transfer | ||
| 288 | dma_start_fence(); | ||
| 289 | |||
| 290 | match self.state() { | ||
| 291 | // Re-start receiver | ||
| 292 | RadioState::RX_IDLE => r.tasks_start.write(|w| w.tasks_start().set_bit()), | ||
| 293 | // Enable receiver | ||
| 294 | _ => r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()), | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | /// Cancel receiving packet | ||
| 299 | fn receive_cancel() { | ||
| 300 | let r = T::regs(); | ||
| 301 | r.shorts.reset(); | ||
| 302 | r.tasks_stop.write(|w| w.tasks_stop().set_bit()); | ||
| 303 | loop { | ||
| 304 | match state(r) { | ||
| 305 | RadioState::DISABLED | RadioState::RX_IDLE => break, | ||
| 306 | _ => (), | ||
| 307 | } | ||
| 308 | } | ||
| 309 | // DMA transfer may have been in progress so synchronize with its memory operations | ||
| 310 | dma_end_fence(); | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Receives one radio packet and copies its contents into the given `packet` buffer | ||
| 314 | /// | ||
| 315 | /// This methods returns the `Ok` variant if the CRC included the packet was successfully | ||
| 316 | /// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet` | ||
| 317 | /// will be updated with the received packet's data | ||
| 318 | pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), Error> { | ||
| 319 | let s = T::state(); | ||
| 320 | let r = T::regs(); | ||
| 321 | |||
| 322 | // Start the read | ||
| 323 | self.receive_start(packet); | ||
| 324 | |||
| 325 | let dropper = OnDrop::new(|| Self::receive_cancel()); | ||
| 326 | |||
| 327 | self.clear_all_interrupts(); | ||
| 328 | // wait until we have received something | ||
| 329 | core::future::poll_fn(|cx| { | ||
| 330 | s.event_waker.register(cx.waker()); | ||
| 331 | |||
| 332 | if r.events_phyend.read().events_phyend().bit_is_set() { | ||
| 333 | r.events_phyend.reset(); | ||
| 334 | trace!("RX done poll"); | ||
| 335 | return Poll::Ready(()); | ||
| 336 | } else { | ||
| 337 | r.intenset.write(|w| w.phyend().set()); | ||
| 338 | }; | ||
| 339 | |||
| 340 | Poll::Pending | ||
| 341 | }) | ||
| 342 | .await; | ||
| 343 | |||
| 344 | dma_end_fence(); | ||
| 345 | dropper.defuse(); | ||
| 346 | |||
| 347 | let crc = r.rxcrc.read().rxcrc().bits() as u16; | ||
| 348 | if r.crcstatus.read().crcstatus().bit_is_set() { | ||
| 349 | Ok(()) | ||
| 350 | } else { | ||
| 351 | Err(Error::CrcFailed(crc)) | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | /// Tries to send the given `packet` | ||
| 356 | /// | ||
| 357 | /// This method performs Clear Channel Assessment (CCA) first and sends the `packet` only if the | ||
| 358 | /// channel is observed to be *clear* (no transmission is currently ongoing), otherwise no | ||
| 359 | /// packet is transmitted and the `Err` variant is returned | ||
| 360 | /// | ||
| 361 | /// NOTE this method will *not* modify the `packet` argument. The mutable reference is used to | ||
| 362 | /// ensure the `packet` buffer is allocated in RAM, which is required by the RADIO peripheral | ||
| 363 | // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's | ||
| 364 | // allocated in RAM | ||
| 365 | pub async fn try_send(&mut self, packet: &mut Packet) -> Result<(), Error> { | ||
| 366 | let s = T::state(); | ||
| 367 | let r = T::regs(); | ||
| 368 | |||
| 369 | // enable radio to perform cca | ||
| 370 | self.receive_prepare(); | ||
| 371 | |||
| 372 | /// transmit result | ||
| 373 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 374 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 375 | pub enum TransmitResult { | ||
| 376 | /// Success | ||
| 377 | Success, | ||
| 378 | /// Clear channel assessment reported channel in use | ||
| 379 | ChannelInUse, | ||
| 380 | } | ||
| 381 | |||
| 382 | // Configure shortcuts | ||
| 383 | // | ||
| 384 | // The radio goes through following states when sending a 802.15.4 packet | ||
| 385 | // | ||
| 386 | // enable RX → ramp up RX → clear channel assessment (CCA) → CCA result | ||
| 387 | // CCA idle → enable TX → start TX → TX → end (PHYEND) → disabled | ||
| 388 | // | ||
| 389 | // CCA might end up in the event CCABUSY in which there will be no transmission | ||
| 390 | r.shorts.write(|w| { | ||
| 391 | w.rxready_ccastart() | ||
| 392 | .enabled() | ||
| 393 | .ccaidle_txen() | ||
| 394 | .enabled() | ||
| 395 | .txready_start() | ||
| 396 | .enabled() | ||
| 397 | .ccabusy_disable() | ||
| 398 | .enabled() | ||
| 399 | .phyend_disable() | ||
| 400 | .enabled() | ||
| 401 | }); | ||
| 402 | |||
| 403 | // Set transmission buffer | ||
| 404 | self.set_buffer(packet.buffer.as_mut()); | ||
| 405 | |||
| 406 | // the DMA transfer will start at some point after the following write operation so | ||
| 407 | // we place the compiler fence here | ||
| 408 | dma_start_fence(); | ||
| 409 | // start CCA. In case the channel is clear, the data at packetptr will be sent automatically | ||
| 410 | |||
| 411 | match self.state() { | ||
| 412 | // Re-start receiver | ||
| 413 | RadioState::RX_IDLE => r.tasks_ccastart.write(|w| w.tasks_ccastart().set_bit()), | ||
| 414 | // Enable receiver | ||
| 415 | _ => r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()), | ||
| 416 | } | ||
| 417 | |||
| 418 | self.clear_all_interrupts(); | ||
| 419 | let result = core::future::poll_fn(|cx| { | ||
| 420 | s.event_waker.register(cx.waker()); | ||
| 421 | |||
| 422 | if r.events_phyend.read().events_phyend().bit_is_set() { | ||
| 423 | r.events_phyend.reset(); | ||
| 424 | r.events_ccabusy.reset(); | ||
| 425 | trace!("TX done poll"); | ||
| 426 | return Poll::Ready(TransmitResult::Success); | ||
| 427 | } else if r.events_ccabusy.read().events_ccabusy().bit_is_set() { | ||
| 428 | r.events_ccabusy.reset(); | ||
| 429 | trace!("TX no CCA"); | ||
| 430 | return Poll::Ready(TransmitResult::ChannelInUse); | ||
| 431 | } | ||
| 432 | |||
| 433 | r.intenset.write(|w| w.phyend().set().ccabusy().set()); | ||
| 434 | |||
| 435 | Poll::Pending | ||
| 436 | }) | ||
| 437 | .await; | ||
| 438 | |||
| 439 | match result { | ||
| 440 | TransmitResult::Success => Ok(()), | ||
| 441 | TransmitResult::ChannelInUse => Err(Error::ChannelInUse), | ||
| 442 | } | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | /// An IEEE 802.15.4 packet | ||
| 447 | /// | ||
| 448 | /// This `Packet` is a PHY layer packet. It's made up of the physical header (PHR) and the PSDU | ||
| 449 | /// (PHY service data unit). The PSDU of this `Packet` will always include the MAC level CRC, AKA | ||
| 450 | /// the FCS (Frame Control Sequence) -- the CRC is fully computed in hardware and automatically | ||
| 451 | /// appended on transmission and verified on reception. | ||
| 452 | /// | ||
| 453 | /// The API lets users modify the usable part (not the CRC) of the PSDU via the `deref` and | ||
| 454 | /// `copy_from_slice` methods. These methods will automatically update the PHR. | ||
| 455 | /// | ||
| 456 | /// See figure 119 in the Product Specification of the nRF52840 for more details | ||
| 457 | pub struct Packet { | ||
| 458 | buffer: [u8; Self::SIZE], | ||
| 459 | } | ||
| 460 | |||
| 461 | // See figure 124 in nRF52840-PS | ||
| 462 | impl Packet { | ||
| 463 | // for indexing purposes | ||
| 464 | const PHY_HDR: usize = 0; | ||
| 465 | const DATA: core::ops::RangeFrom<usize> = 1..; | ||
| 466 | |||
| 467 | /// Maximum amount of usable payload (CRC excluded) a single packet can contain, in bytes | ||
| 468 | pub const CAPACITY: u8 = 125; | ||
| 469 | const CRC: u8 = 2; // size of the CRC, which is *never* copied to / from RAM | ||
| 470 | const MAX_PSDU_LEN: u8 = Self::CAPACITY + Self::CRC; | ||
| 471 | const SIZE: usize = 1 /* PHR */ + Self::MAX_PSDU_LEN as usize; | ||
| 472 | |||
| 473 | /// Returns an empty packet (length = 0) | ||
| 474 | pub fn new() -> Self { | ||
| 475 | let mut packet = Self { | ||
| 476 | buffer: [0; Self::SIZE], | ||
| 477 | }; | ||
| 478 | packet.set_len(0); | ||
| 479 | packet | ||
| 480 | } | ||
| 481 | |||
| 482 | /// Fills the packet payload with given `src` data | ||
| 483 | /// | ||
| 484 | /// # Panics | ||
| 485 | /// | ||
| 486 | /// This function panics if `src` is larger than `Self::CAPACITY` | ||
| 487 | pub fn copy_from_slice(&mut self, src: &[u8]) { | ||
| 488 | assert!(src.len() <= Self::CAPACITY as usize); | ||
| 489 | let len = src.len() as u8; | ||
| 490 | self.buffer[Self::DATA][..len as usize].copy_from_slice(&src[..len.into()]); | ||
| 491 | self.set_len(len); | ||
| 492 | } | ||
| 493 | |||
| 494 | /// Returns the size of this packet's payload | ||
| 495 | pub fn len(&self) -> u8 { | ||
| 496 | self.buffer[Self::PHY_HDR] - Self::CRC | ||
| 497 | } | ||
| 498 | |||
| 499 | /// Changes the size of the packet's payload | ||
| 500 | /// | ||
| 501 | /// # Panics | ||
| 502 | /// | ||
| 503 | /// This function panics if `len` is larger than `Self::CAPACITY` | ||
| 504 | pub fn set_len(&mut self, len: u8) { | ||
| 505 | assert!(len <= Self::CAPACITY); | ||
| 506 | self.buffer[Self::PHY_HDR] = len + Self::CRC; | ||
| 507 | } | ||
| 508 | |||
| 509 | /// Returns the LQI (Link Quality Indicator) of the received packet | ||
| 510 | /// | ||
| 511 | /// Note that the LQI is stored in the `Packet`'s internal buffer by the hardware so the value | ||
| 512 | /// returned by this method is only valid after a `Radio.recv` operation. Operations that | ||
| 513 | /// modify the `Packet`, like `copy_from_slice` or `set_len`+`deref_mut`, will overwrite the | ||
| 514 | /// stored LQI value. | ||
| 515 | /// | ||
| 516 | /// Also note that the hardware will *not* compute a LQI for packets smaller than 3 bytes so | ||
| 517 | /// this method will return an invalid value for those packets. | ||
| 518 | pub fn lqi(&self) -> u8 { | ||
| 519 | self.buffer[1 /* PHY_HDR */ + self.len() as usize /* data */] | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | impl core::ops::Deref for Packet { | ||
| 524 | type Target = [u8]; | ||
| 525 | |||
| 526 | fn deref(&self) -> &[u8] { | ||
| 527 | &self.buffer[Self::DATA][..self.len() as usize] | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | impl core::ops::DerefMut for Packet { | ||
| 532 | fn deref_mut(&mut self) -> &mut [u8] { | ||
| 533 | let len = self.len(); | ||
| 534 | &mut self.buffer[Self::DATA][..len as usize] | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | /// NOTE must be followed by a volatile write operation | ||
| 539 | fn dma_start_fence() { | ||
| 540 | compiler_fence(Ordering::Release); | ||
| 541 | } | ||
| 542 | |||
| 543 | /// NOTE must be preceded by a volatile read operation | ||
| 544 | fn dma_end_fence() { | ||
| 545 | compiler_fence(Ordering::Acquire); | ||
| 546 | } | ||
diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 03f967f87..4c0cc3280 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs | |||
| @@ -7,11 +7,40 @@ | |||
| 7 | 7 | ||
| 8 | /// Bluetooth Low Energy Radio driver. | 8 | /// Bluetooth Low Energy Radio driver. |
| 9 | pub mod ble; | 9 | pub mod ble; |
| 10 | #[cfg(any( | ||
| 11 | feature = "nrf52811", | ||
| 12 | feature = "nrf52820", | ||
| 13 | feature = "nrf52833", | ||
| 14 | feature = "nrf52840", | ||
| 15 | feature = "_nrf5340-net" | ||
| 16 | ))] | ||
| 17 | /// IEEE 802.15.4 | ||
| 18 | pub mod ieee802154; | ||
| 10 | 19 | ||
| 11 | use core::marker::PhantomData; | 20 | use core::marker::PhantomData; |
| 12 | 21 | ||
| 22 | use pac::radio::state::STATE_A as RadioState; | ||
| 23 | pub use pac::radio::txpower::TXPOWER_A as TxPower; | ||
| 24 | |||
| 13 | use crate::{interrupt, pac, Peripheral}; | 25 | use crate::{interrupt, pac, Peripheral}; |
| 14 | 26 | ||
| 27 | /// RADIO error. | ||
| 28 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 29 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 30 | #[non_exhaustive] | ||
| 31 | pub enum Error { | ||
| 32 | /// Buffer was too long. | ||
| 33 | BufferTooLong, | ||
| 34 | /// Buffer was too short. | ||
| 35 | BufferTooShort, | ||
| 36 | /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. | ||
| 37 | BufferNotInRAM, | ||
| 38 | /// Clear channel assessment reported channel in use | ||
| 39 | ChannelInUse, | ||
| 40 | /// CRC check failed | ||
| 41 | CrcFailed(u16), | ||
| 42 | } | ||
| 43 | |||
| 15 | /// Interrupt handler | 44 | /// Interrupt handler |
| 16 | pub struct InterruptHandler<T: Instance> { | 45 | pub struct InterruptHandler<T: Instance> { |
| 17 | _phantom: PhantomData<T>, | 46 | _phantom: PhantomData<T>, |
| @@ -21,11 +50,9 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 21 | unsafe fn on_interrupt() { | 50 | unsafe fn on_interrupt() { |
| 22 | let r = T::regs(); | 51 | let r = T::regs(); |
| 23 | let s = T::state(); | 52 | let s = T::state(); |
| 24 | 53 | // clear all interrupts | |
| 25 | if r.events_end.read().events_end().bit_is_set() { | 54 | r.intenclr.write(|w| w.bits(0xffff_ffff)); |
| 26 | s.end_waker.wake(); | 55 | s.event_waker.wake(); |
| 27 | r.intenclr.write(|w| w.end().clear()); | ||
| 28 | } | ||
| 29 | } | 56 | } |
| 30 | } | 57 | } |
| 31 | 58 | ||
| @@ -34,12 +61,12 @@ pub(crate) mod sealed { | |||
| 34 | 61 | ||
| 35 | pub struct State { | 62 | pub struct State { |
| 36 | /// end packet transmission or reception | 63 | /// end packet transmission or reception |
| 37 | pub end_waker: AtomicWaker, | 64 | pub event_waker: AtomicWaker, |
| 38 | } | 65 | } |
| 39 | impl State { | 66 | impl State { |
| 40 | pub const fn new() -> Self { | 67 | pub const fn new() -> Self { |
| 41 | Self { | 68 | Self { |
| 42 | end_waker: AtomicWaker::new(), | 69 | event_waker: AtomicWaker::new(), |
| 43 | } | 70 | } |
| 44 | } | 71 | } |
| 45 | } | 72 | } |
| @@ -73,3 +100,11 @@ pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { | |||
| 73 | /// Interrupt for this peripheral. | 100 | /// Interrupt for this peripheral. |
| 74 | type Interrupt: interrupt::typelevel::Interrupt; | 101 | type Interrupt: interrupt::typelevel::Interrupt; |
| 75 | } | 102 | } |
| 103 | |||
| 104 | /// Get the state of the radio | ||
| 105 | pub(crate) fn state(radio: &pac::radio::RegisterBlock) -> RadioState { | ||
| 106 | match radio.state.read().state().variant() { | ||
| 107 | Some(state) => state, | ||
| 108 | None => unreachable!(), | ||
| 109 | } | ||
| 110 | } | ||
diff --git a/embassy-nrf/src/util.rs b/embassy-nrf/src/util.rs index 6cdb97f08..13aba7dec 100644 --- a/embassy-nrf/src/util.rs +++ b/embassy-nrf/src/util.rs | |||
| @@ -34,7 +34,6 @@ pub(crate) fn slice_in_ram<T>(slice: *const [T]) -> bool { | |||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | /// Return an error if slice is not in RAM. Skips check if slice is zero-length. | 36 | /// Return an error if slice is not in RAM. Skips check if slice is zero-length. |
| 37 | #[cfg(not(feature = "nrf51"))] | ||
| 38 | pub(crate) fn slice_in_ram_or<T, E>(slice: *const [T], err: E) -> Result<(), E> { | 37 | pub(crate) fn slice_in_ram_or<T, E>(slice: *const [T], err: E) -> Result<(), E> { |
| 39 | let (_, len) = slice_ptr_parts(slice); | 38 | let (_, len) = slice_ptr_parts(slice); |
| 40 | if len == 0 || slice_in_ram(slice) { | 39 | if len == 0 || slice_in_ram(slice) { |
