aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src/radio
diff options
context:
space:
mode:
authorErik Bånvik <[email protected]>2024-02-28 22:36:31 +0100
committerErik Bånvik <[email protected]>2024-02-28 22:36:31 +0100
commit4294bc5e4bb191adcdd2daffd77180bb602da720 (patch)
tree1b33da24e4b40683936d50ac7d33b48a4a777ba6 /embassy-nrf/src/radio
parent263d1b024ca7fc3baac0014c26ffc0af57b27c4e (diff)
Added IEEE 802.15.4 radio
Diffstat (limited to 'embassy-nrf/src/radio')
-rw-r--r--embassy-nrf/src/radio/ble.rs15
-rw-r--r--embassy-nrf/src/radio/event.rs310
-rw-r--r--embassy-nrf/src/radio/ieee802154.rs573
-rw-r--r--embassy-nrf/src/radio/mod.rs34
4 files changed, 911 insertions, 21 deletions
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;
15use crate::radio::*; 15use crate::radio::*;
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>,
@@ -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 @@
1use crate::pac;
2use bitflags;
3
4bitflags::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
58impl 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")]
246impl 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
279impl 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
3use core::sync::atomic::{compiler_fence, Ordering};
4use core::task::Poll;
5
6use super::{Error, Event, Instance, InterruptHandler};
7use crate::{
8 interrupt::{self, typelevel::Interrupt},
9 pac, Peripheral,
10};
11use pac::radio::{state::STATE_A as RadioState, txpower::TXPOWER_A as TxPower};
12
13use embassy_hal_internal::drop::OnDrop;
14use embassy_hal_internal::{into_ref, PeripheralRef};
15
16/// Default Start of Frame Delimiter = `0xA7` (IEEE compliant)
17pub const DEFAULT_SFD: u8 = 0xA7;
18
19// TODO expose the other variants in `pac::CCAMODE_A`
20/// Clear Channel Assessment method
21pub 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
35fn get_state(radio: &pac::radio::RegisterBlock) -> RadioState {
36 match radio.state.read().state().variant() {
37 Some(state) => state,
38 None => unreachable!(),
39 }
40}
41
42fn 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.
57pub struct Radio<'d, T: Instance> {
58 _p: PeripheralRef<'d, T>,
59 needs_enable: bool,
60}
61
62impl<'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
484pub struct Packet {
485 buffer: [u8; Self::SIZE],
486}
487
488// See figure 124 in nRF52840-PS
489impl 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
550impl 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
558impl 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
566fn dma_start_fence() {
567 compiler_fence(Ordering::Release);
568}
569
570/// NOTE must be preceded by a volatile read operation
571fn 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.
9pub mod ble; 9pub mod ble;
10mod event;
11#[cfg(any(feature = "nrf52840", feature = "nrf52833", feature = "_nrf5340-net"))]
12/// IEEE 802.15.4
13pub mod ieee802154;
14
15pub use event::Event;
10 16
11use core::marker::PhantomData; 17use core::marker::PhantomData;
12 18
13use crate::{interrupt, pac, Peripheral}; 19use 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]
25pub 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
16pub struct InterruptHandler<T: Instance> { 37pub 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 }