aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2024-03-06 16:40:26 +0000
committerGitHub <[email protected]>2024-03-06 16:40:26 +0000
commit0cedce4068d56050f9fc9c69bffab905a91bbf08 (patch)
treebabe142f4105f99e964cba9b9329fa026e012c10
parent3638df789e4f498b9058bbeb27bdddab4a80bd49 (diff)
parentbb73d6b3fe81f26cb527d45bd700d2aef9b07090 (diff)
Merge pull request #2649 from blueluna/feat/radio-ieee802154
[embassy-nrf] IEEE 802.15.4 radio
-rw-r--r--embassy-nrf/src/chips/nrf51.rs5
-rw-r--r--embassy-nrf/src/chips/nrf52805.rs5
-rw-r--r--embassy-nrf/src/chips/nrf52810.rs5
-rw-r--r--embassy-nrf/src/chips/nrf52811.rs5
-rw-r--r--embassy-nrf/src/chips/nrf52820.rs5
-rw-r--r--embassy-nrf/src/chips/nrf52832.rs5
-rw-r--r--embassy-nrf/src/chips/nrf52833.rs5
-rw-r--r--embassy-nrf/src/chips/nrf5340_net.rs5
-rw-r--r--embassy-nrf/src/lib.rs2
-rw-r--r--embassy-nrf/src/radio/ble.rs63
-rw-r--r--embassy-nrf/src/radio/ieee802154.rs546
-rw-r--r--embassy-nrf/src/radio/mod.rs49
-rw-r--r--embassy-nrf/src/util.rs1
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
104impl_timer!(TIMER0, TIMER0, TIMER0); 107impl_timer!(TIMER0, TIMER0, TIMER0);
@@ -140,6 +143,8 @@ impl_pin!(P0_29, 0, 29);
140impl_pin!(P0_30, 0, 30); 143impl_pin!(P0_30, 0, 30);
141impl_pin!(P0_31, 0, 31); 144impl_pin!(P0_31, 0, 31);
142 145
146impl_radio!(RADIO, RADIO, RADIO);
147
143embassy_hal_internal::interrupt_mod!( 148embassy_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
134impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 137impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -209,6 +212,8 @@ impl_ppi_channel!(PPI_CH31, 31 => static);
209impl_saadc_input!(P0_04, ANALOG_INPUT2); 212impl_saadc_input!(P0_04, ANALOG_INPUT2);
210impl_saadc_input!(P0_05, ANALOG_INPUT3); 213impl_saadc_input!(P0_05, ANALOG_INPUT3);
211 214
215impl_radio!(RADIO, RADIO, RADIO);
216
212embassy_hal_internal::interrupt_mod!( 217embassy_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
140impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 143impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -235,6 +238,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
235impl_saadc_input!(P0_30, ANALOG_INPUT6); 238impl_saadc_input!(P0_30, ANALOG_INPUT6);
236impl_saadc_input!(P0_31, ANALOG_INPUT7); 239impl_saadc_input!(P0_31, ANALOG_INPUT7);
237 240
241impl_radio!(RADIO, RADIO, RADIO);
242
238embassy_hal_internal::interrupt_mod!( 243embassy_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
140impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 143impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -237,6 +240,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
237impl_saadc_input!(P0_30, ANALOG_INPUT6); 240impl_saadc_input!(P0_30, ANALOG_INPUT6);
238impl_saadc_input!(P0_31, ANALOG_INPUT7); 241impl_saadc_input!(P0_31, ANALOG_INPUT7);
239 242
243impl_radio!(RADIO, RADIO, RADIO);
244
240embassy_hal_internal::interrupt_mod!( 245embassy_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
135impl_usb!(USBD, USBD, USBD); 138impl_usb!(USBD, USBD, USBD);
@@ -224,6 +227,8 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
224impl_ppi_channel!(PPI_CH30, 30 => static); 227impl_ppi_channel!(PPI_CH30, 30 => static);
225impl_ppi_channel!(PPI_CH31, 31 => static); 228impl_ppi_channel!(PPI_CH31, 31 => static);
226 229
230impl_radio!(RADIO, RADIO, RADIO);
231
227embassy_hal_internal::interrupt_mod!( 232embassy_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
155impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 158impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -264,6 +267,8 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
264 267
265impl_i2s!(I2S, I2S, I2S); 268impl_i2s!(I2S, I2S, I2S);
266 269
270impl_radio!(RADIO, RADIO, RADIO);
271
267embassy_hal_internal::interrupt_mod!( 272embassy_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
175impl_usb!(USBD, USBD, USBD); 178impl_usb!(USBD, USBD, USBD);
@@ -306,6 +309,8 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
306 309
307impl_i2s!(I2S, I2S, I2S); 310impl_i2s!(I2S, I2S, I2S);
308 311
312impl_radio!(RADIO, RADIO, RADIO);
313
309embassy_hal_internal::interrupt_mod!( 314embassy_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
253impl_uarte!(SERIAL0, UARTE0, SERIAL0); 256impl_uarte!(SERIAL0, UARTE0, SERIAL0);
@@ -345,6 +348,8 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
345impl_ppi_channel!(PPI_CH30, 30 => configurable); 348impl_ppi_channel!(PPI_CH30, 30 => configurable);
346impl_ppi_channel!(PPI_CH31, 31 => configurable); 349impl_ppi_channel!(PPI_CH31, 31 => configurable);
347 350
351impl_radio!(RADIO, RADIO, RADIO);
352
348embassy_hal_internal::interrupt_mod!( 353embassy_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;
47pub mod gpiote; 47pub 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")))]
51pub mod radio; 51pub 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;
7use embassy_hal_internal::drop::OnDrop; 7use embassy_hal_internal::drop::OnDrop;
8use embassy_hal_internal::{into_ref, PeripheralRef}; 8use embassy_hal_internal::{into_ref, PeripheralRef};
9pub use pac::radio::mode::MODE_A as Mode; 9pub use pac::radio::mode::MODE_A as Mode;
10#[cfg(not(feature = "nrf51"))]
10use pac::radio::pcnf0::PLEN_A as PreambleLength; 11use pac::radio::pcnf0::PLEN_A as PreambleLength;
11use pac::radio::state::STATE_A as RadioState;
12pub use pac::radio::txpower::TXPOWER_A as TxPower;
13 12
14use crate::interrupt::typelevel::Interrupt; 13use crate::interrupt::typelevel::Interrupt;
15use crate::radio::*; 14use crate::radio::*;
15pub use crate::radio::{Error, TxPower};
16use crate::util::slice_in_ram_or; 16use 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]
22pub 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.
32pub struct Radio<'d, T: Instance> { 19pub 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
3use core::sync::atomic::{compiler_fence, Ordering};
4use core::task::Poll;
5
6use embassy_hal_internal::drop::OnDrop;
7use embassy_hal_internal::{into_ref, PeripheralRef};
8
9use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower};
10use crate::interrupt::typelevel::Interrupt;
11use crate::interrupt::{self};
12use crate::Peripheral;
13
14/// Default (IEEE compliant) Start of Frame Delimiter
15pub const DEFAULT_SFD: u8 = 0xA7;
16
17// TODO expose the other variants in `pac::CCAMODE_A`
18/// Clear Channel Assessment method
19pub 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.
34pub struct Radio<'d, T: Instance> {
35 _p: PeripheralRef<'d, T>,
36 needs_enable: bool,
37}
38
39impl<'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
457pub struct Packet {
458 buffer: [u8; Self::SIZE],
459}
460
461// See figure 124 in nRF52840-PS
462impl 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
523impl 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
531impl 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
539fn dma_start_fence() {
540 compiler_fence(Ordering::Release);
541}
542
543/// NOTE must be preceded by a volatile read operation
544fn 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.
9pub mod ble; 9pub 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
18pub mod ieee802154;
10 19
11use core::marker::PhantomData; 20use core::marker::PhantomData;
12 21
22use pac::radio::state::STATE_A as RadioState;
23pub use pac::radio::txpower::TXPOWER_A as TxPower;
24
13use crate::{interrupt, pac, Peripheral}; 25use 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]
31pub 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
16pub struct InterruptHandler<T: Instance> { 45pub 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
105pub(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"))]
38pub(crate) fn slice_in_ram_or<T, E>(slice: *const [T], err: E) -> Result<(), E> { 37pub(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) {