diff options
| -rw-r--r-- | embassy-nrf/src/chips/nrf52833.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/ble.rs | 15 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/event.rs | 310 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/ieee802154.rs | 573 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/mod.rs | 34 |
6 files changed, 917 insertions, 22 deletions
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/lib.rs b/embassy-nrf/src/lib.rs index 04a6293a4..132bffa8b 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(any(feature = "nrf52833", feature = "nrf52840"))] |
| 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..ecf8cc5eb 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs | |||
| @@ -15,19 +15,6 @@ use crate::interrupt::typelevel::Interrupt; | |||
| 15 | use crate::radio::*; | 15 | use crate::radio::*; |
| 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>, |
| @@ -393,7 +380,7 @@ impl<'d, T: Instance> Radio<'d, T> { | |||
| 393 | 380 | ||
| 394 | // On poll check if interrupt happen | 381 | // On poll check if interrupt happen |
| 395 | poll_fn(|cx| { | 382 | poll_fn(|cx| { |
| 396 | s.end_waker.register(cx.waker()); | 383 | s.event_waker.register(cx.waker()); |
| 397 | if r.events_end.read().events_end().bit_is_set() { | 384 | if r.events_end.read().events_end().bit_is_set() { |
| 398 | // trace!("radio:end"); | 385 | // trace!("radio:end"); |
| 399 | return core::task::Poll::Ready(()); | 386 | return core::task::Poll::Ready(()); |
diff --git a/embassy-nrf/src/radio/event.rs b/embassy-nrf/src/radio/event.rs new file mode 100644 index 000000000..11056b4d8 --- /dev/null +++ b/embassy-nrf/src/radio/event.rs | |||
| @@ -0,0 +1,310 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use bitflags; | ||
| 3 | |||
| 4 | bitflags::bitflags! { | ||
| 5 | /// Event as bit flags | ||
| 6 | pub struct Event : u32 { | ||
| 7 | /// Radio ready | ||
| 8 | const READY = 1u32 << 0; | ||
| 9 | /// Address operation done | ||
| 10 | const ADDRESS = 1u32 << 1; | ||
| 11 | /// Payload operation done | ||
| 12 | const PAYLOAD = 1u32 << 2; | ||
| 13 | /// Packet operation done | ||
| 14 | const END = 1u32 << 3; | ||
| 15 | /// Radio has been disabled | ||
| 16 | const DISABLED = 1u32 << 4; | ||
| 17 | /// Device address match in last received packet | ||
| 18 | const DEV_MATCH = 1u32 << 5; | ||
| 19 | /// No device address match in last received packet | ||
| 20 | const DEV_MISS = 1u32 << 6; | ||
| 21 | /// RSSI sampling complete | ||
| 22 | const RSSI_END = 1u32 << 7; | ||
| 23 | /// Bit counter reached target | ||
| 24 | const BC_MATCH = 1u32 << 10; | ||
| 25 | /// CRC ok in last received packet | ||
| 26 | const CRC_OK = 1u32 << 12; | ||
| 27 | /// CRC error in last received packet | ||
| 28 | const CRC_ERROR = 1u32 << 13; | ||
| 29 | /// IEEE 802.15.4 length field received | ||
| 30 | const FRAME_START = 1u32 << 14; | ||
| 31 | /// Sampling of energy detect complete | ||
| 32 | const ED_END = 1u32 << 15; | ||
| 33 | /// Sampling of energy detect stopped | ||
| 34 | const ED_STOPPED = 1u32 << 16; | ||
| 35 | /// Wireless medium in idle, ready to sent | ||
| 36 | const CCA_IDLE = 1u32 << 17; | ||
| 37 | /// Wireless medium busy, do not send | ||
| 38 | const CCA_BUSY = 1u32 << 18; | ||
| 39 | /// Clear channel assessment stopped | ||
| 40 | const CCA_STOPPED = 1u32 << 19; | ||
| 41 | /// BLE LR rate boost received | ||
| 42 | const RATE_BOOST = 1u32 << 20; | ||
| 43 | /// Radio has ramped up transmitter | ||
| 44 | const TX_READY = 1u32 << 21; | ||
| 45 | /// Radio has ramped up receiver | ||
| 46 | const RX_READY = 1u32 << 22; | ||
| 47 | /// MAC header match found | ||
| 48 | const MHR_MATCH = 1u32 << 23; | ||
| 49 | /// Preamble received, possible false triggering | ||
| 50 | const SYNC = 1u32 << 26; | ||
| 51 | /// Last bit sent / received | ||
| 52 | const PHY_END = 1u32 << 27; | ||
| 53 | /// Continuous tone extension is present | ||
| 54 | const CTE_PRESENT = 1u32 << 28; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | impl Event { | ||
| 59 | /// Read events from radio | ||
| 60 | #[cfg(not(feature = "nrf52832"))] | ||
| 61 | pub fn from_radio(radio: &pac::radio::RegisterBlock) -> Self { | ||
| 62 | let mut value = Self::empty(); | ||
| 63 | if radio.events_ready.read().events_ready().bit_is_set() { | ||
| 64 | value |= Self::READY; | ||
| 65 | } | ||
| 66 | if radio.events_address.read().events_address().bit_is_set() { | ||
| 67 | value |= Self::ADDRESS; | ||
| 68 | } | ||
| 69 | if radio.events_payload.read().events_payload().bit_is_set() { | ||
| 70 | value |= Self::PAYLOAD; | ||
| 71 | } | ||
| 72 | if radio.events_end.read().events_end().bit_is_set() { | ||
| 73 | value |= Self::END; | ||
| 74 | } | ||
| 75 | if radio.events_disabled.read().events_disabled().bit_is_set() { | ||
| 76 | value |= Self::DISABLED; | ||
| 77 | } | ||
| 78 | if radio.events_devmatch.read().events_devmatch().bit_is_set() { | ||
| 79 | value |= Self::DEV_MATCH; | ||
| 80 | } | ||
| 81 | if radio.events_devmiss.read().events_devmiss().bit_is_set() { | ||
| 82 | value |= Self::DEV_MISS; | ||
| 83 | } | ||
| 84 | if radio.events_rssiend.read().events_rssiend().bit_is_set() { | ||
| 85 | value |= Self::RSSI_END; | ||
| 86 | } | ||
| 87 | if radio.events_bcmatch.read().events_bcmatch().bit_is_set() { | ||
| 88 | value |= Self::BC_MATCH; | ||
| 89 | } | ||
| 90 | if radio.events_crcok.read().events_crcok().bit_is_set() { | ||
| 91 | value |= Self::CRC_OK; | ||
| 92 | } | ||
| 93 | if radio.events_crcerror.read().events_crcerror().bit_is_set() { | ||
| 94 | value |= Self::CRC_ERROR; | ||
| 95 | } | ||
| 96 | #[cfg(any( | ||
| 97 | feature = "nrf52811", | ||
| 98 | feature = "nrf52820", | ||
| 99 | feature = "nrf52833", | ||
| 100 | feature = "_nrf5340-net" | ||
| 101 | ))] | ||
| 102 | if radio.events_framestart.read().events_framestart().bit_is_set() { | ||
| 103 | value |= Self::FRAME_START; | ||
| 104 | } | ||
| 105 | #[cfg(any( | ||
| 106 | feature = "nrf52811", | ||
| 107 | feature = "nrf52820", | ||
| 108 | feature = "nrf52833", | ||
| 109 | feature = "_nrf5340-net" | ||
| 110 | ))] | ||
| 111 | if radio.events_edend.read().events_edend().bit_is_set() { | ||
| 112 | value |= Self::ED_END; | ||
| 113 | } | ||
| 114 | #[cfg(any( | ||
| 115 | feature = "nrf52811", | ||
| 116 | feature = "nrf52820", | ||
| 117 | feature = "nrf52833", | ||
| 118 | feature = "_nrf5340-net" | ||
| 119 | ))] | ||
| 120 | if radio.events_edstopped.read().events_edstopped().bit_is_set() { | ||
| 121 | value |= Self::ED_STOPPED; | ||
| 122 | } | ||
| 123 | #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] | ||
| 124 | if radio.events_ccaidle.read().events_ccaidle().bit_is_set() { | ||
| 125 | value |= Self::CCA_IDLE; | ||
| 126 | } | ||
| 127 | #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] | ||
| 128 | if radio.events_ccabusy.read().events_ccabusy().bit_is_set() { | ||
| 129 | value |= Self::CCA_BUSY; | ||
| 130 | } | ||
| 131 | #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] | ||
| 132 | if radio.events_ccastopped.read().events_ccastopped().bit_is_set() { | ||
| 133 | value |= Self::CCA_STOPPED; | ||
| 134 | } | ||
| 135 | #[cfg(any( | ||
| 136 | feature = "nrf52811", | ||
| 137 | feature = "nrf52820", | ||
| 138 | feature = "nrf52833", | ||
| 139 | feature = "_nrf5340-net" | ||
| 140 | ))] | ||
| 141 | if radio.events_rateboost.read().events_rateboost().bit_is_set() { | ||
| 142 | value |= Self::RATE_BOOST; | ||
| 143 | } | ||
| 144 | #[cfg(any( | ||
| 145 | feature = "nrf52805", | ||
| 146 | feature = "nrf52811", | ||
| 147 | feature = "nrf52820", | ||
| 148 | feature = "nrf52833", | ||
| 149 | feature = "_nrf5340-net" | ||
| 150 | ))] | ||
| 151 | if radio.events_txready.read().events_txready().bit_is_set() { | ||
| 152 | value |= Self::TX_READY; | ||
| 153 | } | ||
| 154 | #[cfg(any( | ||
| 155 | feature = "nrf52805", | ||
| 156 | feature = "nrf52811", | ||
| 157 | feature = "nrf52820", | ||
| 158 | feature = "nrf52833", | ||
| 159 | feature = "_nrf5340-net" | ||
| 160 | ))] | ||
| 161 | if radio.events_rxready.read().events_rxready().bit_is_set() { | ||
| 162 | value |= Self::RX_READY; | ||
| 163 | } | ||
| 164 | #[cfg(any( | ||
| 165 | feature = "nrf52811", | ||
| 166 | feature = "nrf52820", | ||
| 167 | feature = "nrf52833", | ||
| 168 | feature = "_nrf5340-net" | ||
| 169 | ))] | ||
| 170 | if radio.events_mhrmatch.read().events_mhrmatch().bit_is_set() { | ||
| 171 | value |= Self::MHR_MATCH; | ||
| 172 | } | ||
| 173 | #[cfg(any(feature = "nrf52820", feature = "nrf52833", feature = "_nrf5340-net"))] | ||
| 174 | if radio.events_sync.read().events_sync().bit_is_set() { | ||
| 175 | value |= Self::SYNC; | ||
| 176 | } | ||
| 177 | #[cfg(any( | ||
| 178 | feature = "nrf52805", | ||
| 179 | feature = "nrf52811", | ||
| 180 | feature = "nrf52820", | ||
| 181 | feature = "nrf52833", | ||
| 182 | feature = "_nrf5340-net" | ||
| 183 | ))] | ||
| 184 | if radio.events_phyend.read().events_phyend().bit_is_set() { | ||
| 185 | value |= Self::PHY_END; | ||
| 186 | } | ||
| 187 | #[cfg(any( | ||
| 188 | feature = "nrf52811", | ||
| 189 | feature = "nrf52820", | ||
| 190 | feature = "nrf52833", | ||
| 191 | feature = "_nrf5340-net" | ||
| 192 | ))] | ||
| 193 | if radio.events_ctepresent.read().events_ctepresent().bit_is_set() { | ||
| 194 | value |= Self::CTE_PRESENT; | ||
| 195 | } | ||
| 196 | value | ||
| 197 | } | ||
| 198 | // The nRF52832 SVD probably is a bit broken | ||
| 199 | /// Read events from radio | ||
| 200 | #[cfg(feature = "nrf52832")] | ||
| 201 | pub fn from_radio(radio: &pac::radio::RegisterBlock) -> Self { | ||
| 202 | let mut value = Self::empty(); | ||
| 203 | if radio.events_ready.read().bits() == 1 { | ||
| 204 | value |= Self::READY; | ||
| 205 | } | ||
| 206 | if radio.events_address.read().bits() == 1 { | ||
| 207 | value |= Self::ADDRESS; | ||
| 208 | } | ||
| 209 | if radio.events_payload.read().bits() == 1 { | ||
| 210 | value |= Self::PAYLOAD; | ||
| 211 | } | ||
| 212 | if radio.events_end.read().bits() == 1 { | ||
| 213 | value |= Self::END; | ||
| 214 | } | ||
| 215 | if radio.events_disabled.read().bits() == 1 { | ||
| 216 | value |= Self::DISABLED; | ||
| 217 | } | ||
| 218 | if radio.events_devmatch.read().bits() == 1 { | ||
| 219 | value |= Self::DEV_MATCH; | ||
| 220 | } | ||
| 221 | if radio.events_devmiss.read().bits() == 1 { | ||
| 222 | value |= Self::DEV_MISS; | ||
| 223 | } | ||
| 224 | if radio.events_rssiend.read().bits() == 1 { | ||
| 225 | value |= Self::RSSI_END; | ||
| 226 | } | ||
| 227 | if radio.events_bcmatch.read().bits() == 1 { | ||
| 228 | value |= Self::BC_MATCH; | ||
| 229 | } | ||
| 230 | if radio.events_crcok.read().bits() == 1 { | ||
| 231 | value |= Self::CRC_OK; | ||
| 232 | } | ||
| 233 | if radio.events_crcerror.read().bits() == 1 { | ||
| 234 | value |= Self::CRC_ERROR; | ||
| 235 | } | ||
| 236 | value | ||
| 237 | } | ||
| 238 | |||
| 239 | /// Read events from radio, mask with set interrupts | ||
| 240 | pub fn from_radio_masked(radio: &pac::radio::RegisterBlock) -> Self { | ||
| 241 | Self::from_radio(radio) & Self::from_bits_truncate(radio.intenset.read().bits()) | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | #[cfg(feature = "defmt")] | ||
| 246 | impl defmt::Format for Event { | ||
| 247 | fn format(&self, fmt: defmt::Formatter) { | ||
| 248 | defmt::write!( | ||
| 249 | fmt, | ||
| 250 | "{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}", | ||
| 251 | if self.contains(Self::READY) { "RD" } else { "__" }, | ||
| 252 | if self.contains(Self::ADDRESS) { "AD" } else { "__" }, | ||
| 253 | if self.contains(Self::PAYLOAD) { "PL" } else { "__" }, | ||
| 254 | if self.contains(Self::END) { " E" } else { "__" }, | ||
| 255 | if self.contains(Self::DISABLED) { "DI" } else { "__" }, | ||
| 256 | if self.contains(Self::DEV_MATCH) { "D+" } else { "__" }, | ||
| 257 | if self.contains(Self::DEV_MISS) { "D-" } else { "__" }, | ||
| 258 | if self.contains(Self::RSSI_END) { "RE" } else { "__" }, | ||
| 259 | if self.contains(Self::BC_MATCH) { "CM" } else { "__" }, | ||
| 260 | if self.contains(Self::CRC_OK) { "CO" } else { "__" }, | ||
| 261 | if self.contains(Self::CRC_ERROR) { "CE" } else { "__" }, | ||
| 262 | if self.contains(Self::FRAME_START) { "FS" } else { "__" }, | ||
| 263 | if self.contains(Self::ED_END) { "EE" } else { "__" }, | ||
| 264 | if self.contains(Self::ED_STOPPED) { "ES" } else { "__" }, | ||
| 265 | if self.contains(Self::CCA_IDLE) { "CI" } else { "__" }, | ||
| 266 | if self.contains(Self::CCA_BUSY) { "CB" } else { "__" }, | ||
| 267 | if self.contains(Self::CCA_STOPPED) { "CS" } else { "__" }, | ||
| 268 | if self.contains(Self::RATE_BOOST) { "RB" } else { "__" }, | ||
| 269 | if self.contains(Self::TX_READY) { "TX" } else { "__" }, | ||
| 270 | if self.contains(Self::RX_READY) { "RX" } else { "__" }, | ||
| 271 | if self.contains(Self::MHR_MATCH) { "MM" } else { "__" }, | ||
| 272 | if self.contains(Self::SYNC) { "SY" } else { "__" }, | ||
| 273 | if self.contains(Self::PHY_END) { "PE" } else { "__" }, | ||
| 274 | if self.contains(Self::CTE_PRESENT) { "CP" } else { "__" }, | ||
| 275 | ) | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | impl core::fmt::Display for Event { | ||
| 280 | fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 281 | write!( | ||
| 282 | fmt, | ||
| 283 | "{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}", | ||
| 284 | if self.contains(Self::READY) { "RD" } else { "__" }, | ||
| 285 | if self.contains(Self::ADDRESS) { "AD" } else { "__" }, | ||
| 286 | if self.contains(Self::PAYLOAD) { "PL" } else { "__" }, | ||
| 287 | if self.contains(Self::END) { " E" } else { "__" }, | ||
| 288 | if self.contains(Self::DISABLED) { "DI" } else { "__" }, | ||
| 289 | if self.contains(Self::DEV_MATCH) { "D+" } else { "__" }, | ||
| 290 | if self.contains(Self::DEV_MISS) { "D-" } else { "__" }, | ||
| 291 | if self.contains(Self::RSSI_END) { "RE" } else { "__" }, | ||
| 292 | if self.contains(Self::BC_MATCH) { "CM" } else { "__" }, | ||
| 293 | if self.contains(Self::CRC_OK) { "CO" } else { "__" }, | ||
| 294 | if self.contains(Self::CRC_ERROR) { "CE" } else { "__" }, | ||
| 295 | if self.contains(Self::FRAME_START) { "FS" } else { "__" }, | ||
| 296 | if self.contains(Self::ED_END) { "EE" } else { "__" }, | ||
| 297 | if self.contains(Self::ED_STOPPED) { "ES" } else { "__" }, | ||
| 298 | if self.contains(Self::CCA_IDLE) { "CI" } else { "__" }, | ||
| 299 | if self.contains(Self::CCA_BUSY) { "CB" } else { "__" }, | ||
| 300 | if self.contains(Self::CCA_STOPPED) { "CS" } else { "__" }, | ||
| 301 | if self.contains(Self::RATE_BOOST) { "RB" } else { "__" }, | ||
| 302 | if self.contains(Self::TX_READY) { "TX" } else { "__" }, | ||
| 303 | if self.contains(Self::RX_READY) { "RX" } else { "__" }, | ||
| 304 | if self.contains(Self::MHR_MATCH) { "MM" } else { "__" }, | ||
| 305 | if self.contains(Self::SYNC) { "SY" } else { "__" }, | ||
| 306 | if self.contains(Self::PHY_END) { "PE" } else { "__" }, | ||
| 307 | if self.contains(Self::CTE_PRESENT) { "CP" } else { "__" }, | ||
| 308 | ) | ||
| 309 | } | ||
| 310 | } | ||
diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs new file mode 100644 index 000000000..4d7949cb3 --- /dev/null +++ b/embassy-nrf/src/radio/ieee802154.rs | |||
| @@ -0,0 +1,573 @@ | |||
| 1 | //! IEEE 802.15.4 radio | ||
| 2 | |||
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 4 | use core::task::Poll; | ||
| 5 | |||
| 6 | use super::{Error, Event, Instance, InterruptHandler}; | ||
| 7 | use crate::{ | ||
| 8 | interrupt::{self, typelevel::Interrupt}, | ||
| 9 | pac, Peripheral, | ||
| 10 | }; | ||
| 11 | use pac::radio::{state::STATE_A as RadioState, txpower::TXPOWER_A as TxPower}; | ||
| 12 | |||
| 13 | use embassy_hal_internal::drop::OnDrop; | ||
| 14 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 15 | |||
| 16 | /// Default Start of Frame Delimiter = `0xA7` (IEEE compliant) | ||
| 17 | pub const DEFAULT_SFD: u8 = 0xA7; | ||
| 18 | |||
| 19 | // TODO expose the other variants in `pac::CCAMODE_A` | ||
| 20 | /// Clear Channel Assessment method | ||
| 21 | pub enum Cca { | ||
| 22 | /// Carrier sense | ||
| 23 | CarrierSense, | ||
| 24 | /// Energy Detection / Energy Above Threshold | ||
| 25 | EnergyDetection { | ||
| 26 | /// Energy measurements above this value mean that the channel is assumed to be busy. | ||
| 27 | /// Note the measurement range is 0..0xFF - where 0 means that the received power was | ||
| 28 | /// less than 10 dB above the selected receiver sensitivity. This value is not given in dBm, | ||
| 29 | /// but can be converted. See the nrf52840 Product Specification Section 6.20.12.4 | ||
| 30 | /// for details. | ||
| 31 | ed_threshold: u8, | ||
| 32 | }, | ||
| 33 | } | ||
| 34 | |||
| 35 | fn get_state(radio: &pac::radio::RegisterBlock) -> RadioState { | ||
| 36 | match radio.state.read().state().variant() { | ||
| 37 | Some(state) => state, | ||
| 38 | None => unreachable!(), | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | fn trace_state(state: RadioState) { | ||
| 43 | match state { | ||
| 44 | RadioState::DISABLED => trace!("radio:state:DISABLED"), | ||
| 45 | RadioState::RX_RU => trace!("radio:state:RX_RU"), | ||
| 46 | RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"), | ||
| 47 | RadioState::RX => trace!("radio:state:RX"), | ||
| 48 | RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"), | ||
| 49 | RadioState::TX_RU => trace!("radio:state:TX_RU"), | ||
| 50 | RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"), | ||
| 51 | RadioState::TX => trace!("radio:state:TX"), | ||
| 52 | RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"), | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Radio driver. | ||
| 57 | pub struct Radio<'d, T: Instance> { | ||
| 58 | _p: PeripheralRef<'d, T>, | ||
| 59 | needs_enable: bool, | ||
| 60 | } | ||
| 61 | |||
| 62 | impl<'d, T: Instance> Radio<'d, T> { | ||
| 63 | /// Create a new radio driver. | ||
| 64 | pub fn new( | ||
| 65 | radio: impl Peripheral<P = T> + 'd, | ||
| 66 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 67 | ) -> Self { | ||
| 68 | into_ref!(radio); | ||
| 69 | |||
| 70 | let r = T::regs(); | ||
| 71 | |||
| 72 | // Disable and enable to reset peripheral | ||
| 73 | r.power.write(|w| w.power().disabled()); | ||
| 74 | r.power.write(|w| w.power().enabled()); | ||
| 75 | |||
| 76 | // Enable 802.15.4 mode | ||
| 77 | r.mode.write(|w| w.mode().ieee802154_250kbit()); | ||
| 78 | // Configure CRC skip address | ||
| 79 | r.crccnf.write(|w| w.len().two().skipaddr().ieee802154()); | ||
| 80 | unsafe { | ||
| 81 | // Configure CRC polynomial and init | ||
| 82 | r.crcpoly.write(|w| w.crcpoly().bits(0x0001_1021)); | ||
| 83 | r.crcinit.write(|w| w.crcinit().bits(0)); | ||
| 84 | // Configure packet layout | ||
| 85 | // 8-bit on air length | ||
| 86 | // S0 length, zero bytes | ||
| 87 | // S1 length, zero bytes | ||
| 88 | // S1 included in RAM if S1 length > 0, No. | ||
| 89 | // Code Indicator length, 0 | ||
| 90 | // Preamble length 32-bit zero | ||
| 91 | // Exclude CRC | ||
| 92 | // No TERM field | ||
| 93 | r.pcnf0.write(|w| { | ||
| 94 | w.lflen() | ||
| 95 | .bits(8) | ||
| 96 | .s0len() | ||
| 97 | .clear_bit() | ||
| 98 | .s1len() | ||
| 99 | .bits(0) | ||
| 100 | .s1incl() | ||
| 101 | .clear_bit() | ||
| 102 | .cilen() | ||
| 103 | .bits(0) | ||
| 104 | .plen() | ||
| 105 | ._32bit_zero() | ||
| 106 | .crcinc() | ||
| 107 | .include() | ||
| 108 | }); | ||
| 109 | r.pcnf1.write(|w| { | ||
| 110 | w.maxlen() | ||
| 111 | .bits(Packet::MAX_PSDU_LEN) | ||
| 112 | .statlen() | ||
| 113 | .bits(0) | ||
| 114 | .balen() | ||
| 115 | .bits(0) | ||
| 116 | .endian() | ||
| 117 | .clear_bit() | ||
| 118 | .whiteen() | ||
| 119 | .clear_bit() | ||
| 120 | }); | ||
| 121 | } | ||
| 122 | |||
| 123 | // Enable NVIC interrupt | ||
| 124 | T::Interrupt::unpend(); | ||
| 125 | unsafe { T::Interrupt::enable() }; | ||
| 126 | |||
| 127 | let mut radio = Self { | ||
| 128 | _p: radio, | ||
| 129 | needs_enable: false, | ||
| 130 | }; | ||
| 131 | |||
| 132 | radio.set_sfd(DEFAULT_SFD); | ||
| 133 | radio.set_transmission_power(0); | ||
| 134 | radio.set_channel(11); | ||
| 135 | radio.set_cca(Cca::CarrierSense); | ||
| 136 | |||
| 137 | radio | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Changes the radio channel | ||
| 141 | pub fn set_channel(&mut self, channel: u8) { | ||
| 142 | let r = T::regs(); | ||
| 143 | if channel < 11 || channel > 26 { | ||
| 144 | panic!("Bad 802.15.4 channel"); | ||
| 145 | } | ||
| 146 | let frequency_offset = (channel - 10) * 5; | ||
| 147 | self.needs_enable = true; | ||
| 148 | r.frequency | ||
| 149 | .write(|w| unsafe { w.frequency().bits(frequency_offset).map().default() }); | ||
| 150 | } | ||
| 151 | |||
| 152 | /// Changes the Clear Channel Assessment method | ||
| 153 | pub fn set_cca(&mut self, cca: Cca) { | ||
| 154 | let r = T::regs(); | ||
| 155 | self.needs_enable = true; | ||
| 156 | match cca { | ||
| 157 | Cca::CarrierSense => r.ccactrl.write(|w| w.ccamode().carrier_mode()), | ||
| 158 | Cca::EnergyDetection { ed_threshold } => { | ||
| 159 | // "[ED] is enabled by first configuring the field CCAMODE=EdMode in CCACTRL | ||
| 160 | // and writing the CCAEDTHRES field to a chosen value." | ||
| 161 | r.ccactrl | ||
| 162 | .write(|w| unsafe { w.ccamode().ed_mode().ccaedthres().bits(ed_threshold) }); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Changes the Start of Frame Delimiter | ||
| 168 | pub fn set_sfd(&mut self, sfd: u8) { | ||
| 169 | let r = T::regs(); | ||
| 170 | r.sfd.write(|w| unsafe { w.sfd().bits(sfd) }); | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Clear interrupts | ||
| 174 | pub fn clear_all_interrupts(&mut self) { | ||
| 175 | let r = T::regs(); | ||
| 176 | r.intenclr.write(|w| unsafe { w.bits(0xffff_ffff) }); | ||
| 177 | } | ||
| 178 | |||
| 179 | /// Changes the radio transmission power | ||
| 180 | pub fn set_transmission_power(&mut self, power: i8) { | ||
| 181 | let r = T::regs(); | ||
| 182 | self.needs_enable = true; | ||
| 183 | |||
| 184 | let tx_power: TxPower = match power { | ||
| 185 | 8 => TxPower::POS8D_BM, | ||
| 186 | 7 => TxPower::POS7D_BM, | ||
| 187 | 6 => TxPower::POS6D_BM, | ||
| 188 | 5 => TxPower::POS5D_BM, | ||
| 189 | 4 => TxPower::POS4D_BM, | ||
| 190 | 3 => TxPower::POS3D_BM, | ||
| 191 | 2 => TxPower::POS2D_BM, | ||
| 192 | 0 => TxPower::_0D_BM, | ||
| 193 | -4 => TxPower::NEG4D_BM, | ||
| 194 | -8 => TxPower::NEG8D_BM, | ||
| 195 | -12 => TxPower::NEG12D_BM, | ||
| 196 | -16 => TxPower::NEG16D_BM, | ||
| 197 | -20 => TxPower::NEG20D_BM, | ||
| 198 | -30 => TxPower::NEG30D_BM, | ||
| 199 | -40 => TxPower::NEG40D_BM, | ||
| 200 | _ => panic!("Invalid transmission power value"), | ||
| 201 | }; | ||
| 202 | |||
| 203 | r.txpower.write(|w| w.txpower().variant(tx_power)); | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Waits until the radio state matches the given `state` | ||
| 207 | fn wait_for_radio_state(&self, state: RadioState) { | ||
| 208 | while self.state() != state {} | ||
| 209 | } | ||
| 210 | |||
| 211 | fn state(&self) -> RadioState { | ||
| 212 | let r = T::regs(); | ||
| 213 | match r.state.read().state().variant() { | ||
| 214 | Some(state) => state, | ||
| 215 | None => unreachable!(), | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | fn trace_state(&self) { | ||
| 220 | trace_state(self.state()); | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Moves the radio from any state to the DISABLED state | ||
| 224 | fn disable(&mut self) { | ||
| 225 | let r = T::regs(); | ||
| 226 | // See figure 110 in nRF52840-PS | ||
| 227 | loop { | ||
| 228 | match self.state() { | ||
| 229 | RadioState::DISABLED => return, | ||
| 230 | |||
| 231 | RadioState::RX_RU | RadioState::RX_IDLE | RadioState::TX_RU | RadioState::TX_IDLE => { | ||
| 232 | r.tasks_disable.write(|w| w.tasks_disable().set_bit()); | ||
| 233 | |||
| 234 | self.wait_for_radio_state(RadioState::DISABLED); | ||
| 235 | return; | ||
| 236 | } | ||
| 237 | |||
| 238 | // ramping down | ||
| 239 | RadioState::RX_DISABLE | RadioState::TX_DISABLE => { | ||
| 240 | self.wait_for_radio_state(RadioState::DISABLED); | ||
| 241 | return; | ||
| 242 | } | ||
| 243 | |||
| 244 | // cancel ongoing transfer or ongoing CCA | ||
| 245 | RadioState::RX => { | ||
| 246 | r.tasks_ccastop.write(|w| w.tasks_ccastop().set_bit()); | ||
| 247 | r.tasks_stop.write(|w| w.tasks_stop().set_bit()); | ||
| 248 | self.wait_for_radio_state(RadioState::RX_IDLE); | ||
| 249 | } | ||
| 250 | RadioState::TX => { | ||
| 251 | r.tasks_stop.write(|w| w.tasks_stop().set_bit()); | ||
| 252 | self.wait_for_radio_state(RadioState::TX_IDLE); | ||
| 253 | } | ||
| 254 | } | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | fn set_buffer(&mut self, buffer: &[u8]) { | ||
| 259 | let r = T::regs(); | ||
| 260 | r.packetptr.write(|w| unsafe { w.bits(buffer.as_ptr() as u32) }); | ||
| 261 | } | ||
| 262 | |||
| 263 | /// Moves the radio to the RXIDLE state | ||
| 264 | fn receive_prepare(&mut self) { | ||
| 265 | let state = self.state(); | ||
| 266 | |||
| 267 | let disable = match state { | ||
| 268 | RadioState::DISABLED => false, | ||
| 269 | RadioState::RX_DISABLE => true, | ||
| 270 | RadioState::TX_DISABLE => true, | ||
| 271 | RadioState::RX_IDLE => self.needs_enable, | ||
| 272 | // NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RX_IDLE | ||
| 273 | RadioState::TX_IDLE => true, | ||
| 274 | _ => unreachable!(), | ||
| 275 | }; | ||
| 276 | if disable { | ||
| 277 | trace!("Receive Setup"); | ||
| 278 | self.trace_state(); | ||
| 279 | self.disable(); | ||
| 280 | } | ||
| 281 | self.needs_enable = false; | ||
| 282 | } | ||
| 283 | |||
| 284 | fn receive_start(&mut self, packet: &mut Packet) { | ||
| 285 | // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's | ||
| 286 | // allocated in RAM | ||
| 287 | let r = T::regs(); | ||
| 288 | |||
| 289 | // clear related events | ||
| 290 | r.events_framestart.reset(); | ||
| 291 | r.events_ccabusy.reset(); | ||
| 292 | r.events_phyend.reset(); | ||
| 293 | |||
| 294 | self.receive_prepare(); | ||
| 295 | |||
| 296 | // Configure shortcuts | ||
| 297 | // | ||
| 298 | // The radio goes through following states when receiving a 802.15.4 packet | ||
| 299 | // | ||
| 300 | // enable RX → ramp up RX → RX idle → Receive → end (PHYEND) | ||
| 301 | r.shorts.write(|w| w.rxready_start().enabled()); | ||
| 302 | |||
| 303 | // set up RX buffer | ||
| 304 | self.set_buffer(packet.buffer.as_mut()); | ||
| 305 | |||
| 306 | // start transfer | ||
| 307 | dma_start_fence(); | ||
| 308 | |||
| 309 | match self.state() { | ||
| 310 | // Re-start receiver | ||
| 311 | RadioState::RX_IDLE => r.tasks_start.write(|w| w.tasks_start().set_bit()), | ||
| 312 | // Enable receiver | ||
| 313 | _ => r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()), | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | fn receive_cancel() { | ||
| 318 | let r = T::regs(); | ||
| 319 | r.shorts.reset(); | ||
| 320 | if r.events_framestart.read().events_framestart().bit_is_set() { | ||
| 321 | // TODO: Is there a way to finish receiving this frame | ||
| 322 | trace!("EVENTS {}", Event::from_radio(r)); | ||
| 323 | } | ||
| 324 | r.tasks_stop.write(|w| w.tasks_stop().set_bit()); | ||
| 325 | loop { | ||
| 326 | match get_state(r) { | ||
| 327 | RadioState::DISABLED | RadioState::RX_IDLE => break, | ||
| 328 | _ => (), | ||
| 329 | } | ||
| 330 | } | ||
| 331 | // DMA transfer may have been in progress so synchronize with its memory operations | ||
| 332 | dma_end_fence(); | ||
| 333 | } | ||
| 334 | |||
| 335 | /// Receives one radio packet and copies its contents into the given `packet` buffer | ||
| 336 | /// | ||
| 337 | /// This methods returns the `Ok` variant if the CRC included the packet was successfully | ||
| 338 | /// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet` | ||
| 339 | /// will be updated with the received packet's data | ||
| 340 | pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), u16> { | ||
| 341 | let s = T::state(); | ||
| 342 | let r = T::regs(); | ||
| 343 | |||
| 344 | // Start the read | ||
| 345 | self.receive_start(packet); | ||
| 346 | |||
| 347 | let dropper = OnDrop::new(|| Self::receive_cancel()); | ||
| 348 | |||
| 349 | self.clear_all_interrupts(); | ||
| 350 | // wait until we have received something | ||
| 351 | core::future::poll_fn(|cx| { | ||
| 352 | s.event_waker.register(cx.waker()); | ||
| 353 | |||
| 354 | if r.events_phyend.read().events_phyend().bit_is_set() { | ||
| 355 | r.events_phyend.reset(); | ||
| 356 | trace!("RX done poll"); | ||
| 357 | return Poll::Ready(()); | ||
| 358 | } else { | ||
| 359 | r.intenset.write(|w| w.phyend().set()); | ||
| 360 | }; | ||
| 361 | |||
| 362 | Poll::Pending | ||
| 363 | }) | ||
| 364 | .await; | ||
| 365 | |||
| 366 | dma_end_fence(); | ||
| 367 | dropper.defuse(); | ||
| 368 | |||
| 369 | let crc = r.rxcrc.read().rxcrc().bits() as u16; | ||
| 370 | if r.crcstatus.read().crcstatus().bit_is_set() { | ||
| 371 | Ok(()) | ||
| 372 | } else { | ||
| 373 | Err(crc) | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | /// Tries to send the given `packet` | ||
| 378 | /// | ||
| 379 | /// This method performs Clear Channel Assessment (CCA) first and sends the `packet` only if the | ||
| 380 | /// channel is observed to be *clear* (no transmission is currently ongoing), otherwise no | ||
| 381 | /// packet is transmitted and the `Err` variant is returned | ||
| 382 | /// | ||
| 383 | /// NOTE this method will *not* modify the `packet` argument. The mutable reference is used to | ||
| 384 | /// ensure the `packet` buffer is allocated in RAM, which is required by the RADIO peripheral | ||
| 385 | // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's | ||
| 386 | // allocated in RAM | ||
| 387 | pub async fn try_send(&mut self, packet: &mut Packet) -> Result<(), Error> { | ||
| 388 | let s = T::state(); | ||
| 389 | let r = T::regs(); | ||
| 390 | |||
| 391 | // clear related events | ||
| 392 | r.events_framestart.reset(); | ||
| 393 | r.events_ccabusy.reset(); | ||
| 394 | r.events_phyend.reset(); | ||
| 395 | |||
| 396 | // enable radio to perform cca | ||
| 397 | self.receive_prepare(); | ||
| 398 | |||
| 399 | /// transmit result | ||
| 400 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 401 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 402 | pub enum TransmitResult { | ||
| 403 | /// Success | ||
| 404 | Success, | ||
| 405 | /// Clear channel assessment reported channel in use | ||
| 406 | ChannelInUse, | ||
| 407 | } | ||
| 408 | |||
| 409 | // Configure shortcuts | ||
| 410 | // | ||
| 411 | // The radio goes through following states when sending a 802.15.4 packet | ||
| 412 | // | ||
| 413 | // enable RX → ramp up RX → clear channel assessment (CCA) → CCA result | ||
| 414 | // CCA idle → enable TX → start TX → TX → end (PHYEND) → disabled | ||
| 415 | // | ||
| 416 | // CCA might end up in the event CCABUSY in which there will be no transmission | ||
| 417 | r.shorts.write(|w| { | ||
| 418 | w.rxready_ccastart() | ||
| 419 | .enabled() | ||
| 420 | .ccaidle_txen() | ||
| 421 | .enabled() | ||
| 422 | .txready_start() | ||
| 423 | .enabled() | ||
| 424 | .ccabusy_disable() | ||
| 425 | .enabled() | ||
| 426 | .phyend_disable() | ||
| 427 | .enabled() | ||
| 428 | }); | ||
| 429 | |||
| 430 | // Set transmission buffer | ||
| 431 | self.set_buffer(packet.buffer.as_mut()); | ||
| 432 | |||
| 433 | // the DMA transfer will start at some point after the following write operation so | ||
| 434 | // we place the compiler fence here | ||
| 435 | dma_start_fence(); | ||
| 436 | // start CCA. In case the channel is clear, the data at packetptr will be sent automatically | ||
| 437 | |||
| 438 | match self.state() { | ||
| 439 | // Re-start receiver | ||
| 440 | RadioState::RX_IDLE => r.tasks_ccastart.write(|w| w.tasks_ccastart().set_bit()), | ||
| 441 | // Enable receiver | ||
| 442 | _ => r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()), | ||
| 443 | } | ||
| 444 | |||
| 445 | self.clear_all_interrupts(); | ||
| 446 | let result = core::future::poll_fn(|cx| { | ||
| 447 | s.event_waker.register(cx.waker()); | ||
| 448 | |||
| 449 | if r.events_phyend.read().events_phyend().bit_is_set() { | ||
| 450 | r.events_phyend.reset(); | ||
| 451 | r.events_ccabusy.reset(); | ||
| 452 | trace!("TX done poll"); | ||
| 453 | return Poll::Ready(TransmitResult::Success); | ||
| 454 | } else if r.events_ccabusy.read().events_ccabusy().bit_is_set() { | ||
| 455 | r.events_ccabusy.reset(); | ||
| 456 | trace!("TX no CCA"); | ||
| 457 | return Poll::Ready(TransmitResult::ChannelInUse); | ||
| 458 | } | ||
| 459 | |||
| 460 | r.intenset.write(|w| w.phyend().set().ccabusy().set()); | ||
| 461 | |||
| 462 | Poll::Pending | ||
| 463 | }) | ||
| 464 | .await; | ||
| 465 | |||
| 466 | match result { | ||
| 467 | TransmitResult::Success => Ok(()), | ||
| 468 | TransmitResult::ChannelInUse => Err(Error::ChannelInUse), | ||
| 469 | } | ||
| 470 | } | ||
| 471 | } | ||
| 472 | |||
| 473 | /// An IEEE 802.15.4 packet | ||
| 474 | /// | ||
| 475 | /// This `Packet` is a PHY layer packet. It's made up of the physical header (PHR) and the PSDU | ||
| 476 | /// (PHY service data unit). The PSDU of this `Packet` will always include the MAC level CRC, AKA | ||
| 477 | /// the FCS (Frame Control Sequence) -- the CRC is fully computed in hardware and automatically | ||
| 478 | /// appended on transmission and verified on reception. | ||
| 479 | /// | ||
| 480 | /// The API lets users modify the usable part (not the CRC) of the PSDU via the `deref` and | ||
| 481 | /// `copy_from_slice` methods. These methods will automatically update the PHR. | ||
| 482 | /// | ||
| 483 | /// See figure 119 in the Product Specification of the nRF52840 for more details | ||
| 484 | pub struct Packet { | ||
| 485 | buffer: [u8; Self::SIZE], | ||
| 486 | } | ||
| 487 | |||
| 488 | // See figure 124 in nRF52840-PS | ||
| 489 | impl Packet { | ||
| 490 | // for indexing purposes | ||
| 491 | const PHY_HDR: usize = 0; | ||
| 492 | const DATA: core::ops::RangeFrom<usize> = 1..; | ||
| 493 | |||
| 494 | /// Maximum amount of usable payload (CRC excluded) a single packet can contain, in bytes | ||
| 495 | pub const CAPACITY: u8 = 125; | ||
| 496 | const CRC: u8 = 2; // size of the CRC, which is *never* copied to / from RAM | ||
| 497 | const MAX_PSDU_LEN: u8 = Self::CAPACITY + Self::CRC; | ||
| 498 | const SIZE: usize = 1 /* PHR */ + Self::MAX_PSDU_LEN as usize; | ||
| 499 | |||
| 500 | /// Returns an empty packet (length = 0) | ||
| 501 | pub fn new() -> Self { | ||
| 502 | let mut packet = Self { | ||
| 503 | buffer: [0; Self::SIZE], | ||
| 504 | }; | ||
| 505 | packet.set_len(0); | ||
| 506 | packet | ||
| 507 | } | ||
| 508 | |||
| 509 | /// Fills the packet payload with given `src` data | ||
| 510 | /// | ||
| 511 | /// # Panics | ||
| 512 | /// | ||
| 513 | /// This function panics if `src` is larger than `Self::CAPACITY` | ||
| 514 | pub fn copy_from_slice(&mut self, src: &[u8]) { | ||
| 515 | assert!(src.len() <= Self::CAPACITY as usize); | ||
| 516 | let len = src.len() as u8; | ||
| 517 | self.buffer[Self::DATA][..len as usize].copy_from_slice(&src[..len.into()]); | ||
| 518 | self.set_len(len); | ||
| 519 | } | ||
| 520 | |||
| 521 | /// Returns the size of this packet's payload | ||
| 522 | pub fn len(&self) -> u8 { | ||
| 523 | self.buffer[Self::PHY_HDR] - Self::CRC | ||
| 524 | } | ||
| 525 | |||
| 526 | /// Changes the size of the packet's payload | ||
| 527 | /// | ||
| 528 | /// # Panics | ||
| 529 | /// | ||
| 530 | /// This function panics if `len` is larger than `Self::CAPACITY` | ||
| 531 | pub fn set_len(&mut self, len: u8) { | ||
| 532 | assert!(len <= Self::CAPACITY); | ||
| 533 | self.buffer[Self::PHY_HDR] = len + Self::CRC; | ||
| 534 | } | ||
| 535 | |||
| 536 | /// Returns the LQI (Link Quality Indicator) of the received packet | ||
| 537 | /// | ||
| 538 | /// Note that the LQI is stored in the `Packet`'s internal buffer by the hardware so the value | ||
| 539 | /// returned by this method is only valid after a `Radio.recv` operation. Operations that | ||
| 540 | /// modify the `Packet`, like `copy_from_slice` or `set_len`+`deref_mut`, will overwrite the | ||
| 541 | /// stored LQI value. | ||
| 542 | /// | ||
| 543 | /// Also note that the hardware will *not* compute a LQI for packets smaller than 3 bytes so | ||
| 544 | /// this method will return an invalid value for those packets. | ||
| 545 | pub fn lqi(&self) -> u8 { | ||
| 546 | self.buffer[1 /* PHY_HDR */ + self.len() as usize /* data */] | ||
| 547 | } | ||
| 548 | } | ||
| 549 | |||
| 550 | impl core::ops::Deref for Packet { | ||
| 551 | type Target = [u8]; | ||
| 552 | |||
| 553 | fn deref(&self) -> &[u8] { | ||
| 554 | &self.buffer[Self::DATA][..self.len() as usize] | ||
| 555 | } | ||
| 556 | } | ||
| 557 | |||
| 558 | impl core::ops::DerefMut for Packet { | ||
| 559 | fn deref_mut(&mut self) -> &mut [u8] { | ||
| 560 | let len = self.len(); | ||
| 561 | &mut self.buffer[Self::DATA][..len as usize] | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | /// NOTE must be followed by a volatile write operation | ||
| 566 | fn dma_start_fence() { | ||
| 567 | compiler_fence(Ordering::Release); | ||
| 568 | } | ||
| 569 | |||
| 570 | /// NOTE must be preceded by a volatile read operation | ||
| 571 | fn dma_end_fence() { | ||
| 572 | compiler_fence(Ordering::Acquire); | ||
| 573 | } | ||
diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 03f967f87..430078e8a 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs | |||
| @@ -7,11 +7,32 @@ | |||
| 7 | 7 | ||
| 8 | /// Bluetooth Low Energy Radio driver. | 8 | /// Bluetooth Low Energy Radio driver. |
| 9 | pub mod ble; | 9 | pub mod ble; |
| 10 | mod event; | ||
| 11 | #[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "_nrf5340-net"))] | ||
| 12 | /// IEEE 802.15.4 | ||
| 13 | pub mod ieee802154; | ||
| 14 | |||
| 15 | pub use event::Event; | ||
| 10 | 16 | ||
| 11 | use core::marker::PhantomData; | 17 | use core::marker::PhantomData; |
| 12 | 18 | ||
| 13 | use crate::{interrupt, pac, Peripheral}; | 19 | use crate::{interrupt, pac, Peripheral}; |
| 14 | 20 | ||
| 21 | /// RADIO error. | ||
| 22 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 23 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 24 | #[non_exhaustive] | ||
| 25 | pub enum Error { | ||
| 26 | /// Buffer was too long. | ||
| 27 | BufferTooLong, | ||
| 28 | /// Buffer was too short. | ||
| 29 | BufferTooShort, | ||
| 30 | /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. | ||
| 31 | BufferNotInRAM, | ||
| 32 | /// Clear channel assessment reported channel in use | ||
| 33 | ChannelInUse, | ||
| 34 | } | ||
| 35 | |||
| 15 | /// Interrupt handler | 36 | /// Interrupt handler |
| 16 | pub struct InterruptHandler<T: Instance> { | 37 | pub struct InterruptHandler<T: Instance> { |
| 17 | _phantom: PhantomData<T>, | 38 | _phantom: PhantomData<T>, |
| @@ -21,11 +42,10 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 21 | unsafe fn on_interrupt() { | 42 | unsafe fn on_interrupt() { |
| 22 | let r = T::regs(); | 43 | let r = T::regs(); |
| 23 | let s = T::state(); | 44 | let s = T::state(); |
| 24 | 45 | let events = Event::from_radio_masked(r); | |
| 25 | if r.events_end.read().events_end().bit_is_set() { | 46 | // clear active interrupts |
| 26 | s.end_waker.wake(); | 47 | r.intenclr.write(|w| w.bits(events.bits())); |
| 27 | r.intenclr.write(|w| w.end().clear()); | 48 | s.event_waker.wake(); |
| 28 | } | ||
| 29 | } | 49 | } |
| 30 | } | 50 | } |
| 31 | 51 | ||
| @@ -34,12 +54,12 @@ pub(crate) mod sealed { | |||
| 34 | 54 | ||
| 35 | pub struct State { | 55 | pub struct State { |
| 36 | /// end packet transmission or reception | 56 | /// end packet transmission or reception |
| 37 | pub end_waker: AtomicWaker, | 57 | pub event_waker: AtomicWaker, |
| 38 | } | 58 | } |
| 39 | impl State { | 59 | impl State { |
| 40 | pub const fn new() -> Self { | 60 | pub const fn new() -> Self { |
| 41 | Self { | 61 | Self { |
| 42 | end_waker: AtomicWaker::new(), | 62 | event_waker: AtomicWaker::new(), |
| 43 | } | 63 | } |
| 44 | } | 64 | } |
| 45 | } | 65 | } |
