aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-nrf/src')
-rw-r--r--embassy-nrf/src/radio/ble.rs394
-rw-r--r--embassy-nrf/src/radio/ieee802154.rs7
-rw-r--r--embassy-nrf/src/radio/mod.rs8
3 files changed, 5 insertions, 404 deletions
diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
deleted file mode 100644
index d42bbe5f6..000000000
--- a/embassy-nrf/src/radio/ble.rs
+++ /dev/null
@@ -1,394 +0,0 @@
1//! Radio driver implementation focused on Bluetooth Low-Energy transmission.
2
3use core::future::poll_fn;
4use core::sync::atomic::{compiler_fence, Ordering};
5use core::task::Poll;
6
7use embassy_hal_internal::drop::OnDrop;
8pub use pac::radio::vals::Mode;
9#[cfg(not(feature = "_nrf51"))]
10use pac::radio::vals::Plen as PreambleLength;
11
12use crate::interrupt::typelevel::Interrupt;
13use crate::pac::radio::vals;
14use crate::radio::*;
15pub use crate::radio::{Error, TxPower};
16use crate::util::slice_in_ram_or;
17use crate::Peri;
18
19/// Radio driver.
20pub struct Radio<'d, T: Instance> {
21 _p: Peri<'d, T>,
22}
23
24impl<'d, T: Instance> Radio<'d, T> {
25 /// Create a new radio driver.
26 pub fn new(
27 radio: Peri<'d, T>,
28 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
29 ) -> Self {
30 let r = T::regs();
31
32 r.pcnf1().write(|w| {
33 // It is 0 bytes long in a standard BLE packet
34 w.set_statlen(0);
35 // MaxLen configures the maximum packet payload plus add-on size in
36 // number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure
37 // that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means
38 // that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a
39 // packet larger than MAXLEN, the payload will be truncated at MAXLEN
40 //
41 // To simplify the implementation, It is setted as the maximum value
42 // and the length of the packet is controlled only by the LENGTH field in the packet
43 w.set_maxlen(255);
44 // Configure the length of the address field in the packet
45 // The prefix after the address fields is always appended, so is always 1 byte less than the size of the address
46 // The base address is truncated from the least significant byte if the BALEN is less than 4
47 //
48 // BLE address is always 4 bytes long
49 w.set_balen(3); // 3 bytes base address (+ 1 prefix);
50 // Configure the endianess
51 // For BLE is always little endian (LSB first)
52 w.set_endian(vals::Endian::LITTLE);
53 // Data whitening is used to avoid long sequences of zeros or
54 // ones, e.g., 0b0000000 or 0b1111111, in the data bit stream.
55 // The whitener and de-whitener are defined the same way,
56 // using a 7-bit linear feedback shift register with the
57 // polynomial x7 + x4 + 1.
58 //
59 // In BLE Whitening shall be applied on the PDU and CRC of all
60 // Link Layer packets and is performed after the CRC generation
61 // in the transmitter. No other parts of the packets are whitened.
62 // De-whitening is performed before the CRC checking in the receiver
63 // Before whitening or de-whitening, the shift register should be
64 // initialized based on the channel index.
65 w.set_whiteen(true);
66 });
67
68 // Configure CRC
69 r.crccnf().write(|w| {
70 // In BLE the CRC shall be calculated on the PDU of all Link Layer
71 // packets (even if the packet is encrypted).
72 // It skips the address field
73 w.set_skipaddr(vals::Skipaddr::SKIP);
74 // In BLE 24-bit CRC = 3 bytes
75 w.set_len(vals::Len::THREE);
76 });
77
78 // Ch map between 2400 MHZ .. 2500 MHz
79 // All modes use this range
80 #[cfg(not(feature = "_nrf51"))]
81 r.frequency().write(|w| w.set_map(vals::Map::DEFAULT));
82
83 // Configure shortcuts to simplify and speed up sending and receiving packets.
84 r.shorts().write(|w| {
85 // start transmission/recv immediately after ramp-up
86 // disable radio when transmission/recv is done
87 w.set_ready_start(true);
88 w.set_end_disable(true);
89 });
90
91 // Enable NVIC interrupt
92 T::Interrupt::unpend();
93 unsafe { T::Interrupt::enable() };
94
95 Self { _p: radio }
96 }
97
98 fn state(&self) -> RadioState {
99 super::state(T::regs())
100 }
101
102 /// Set the radio mode
103 ///
104 /// The radio must be disabled before calling this function
105 pub fn set_mode(&mut self, mode: Mode) {
106 assert!(self.state() == RadioState::DISABLED);
107
108 let r = T::regs();
109 r.mode().write(|w| w.set_mode(mode));
110
111 #[cfg(not(feature = "_nrf51"))]
112 r.pcnf0().write(|w| {
113 w.set_plen(match mode {
114 Mode::BLE_1MBIT => PreambleLength::_8BIT,
115 Mode::BLE_2MBIT => PreambleLength::_16BIT,
116 #[cfg(any(
117 feature = "nrf52811",
118 feature = "nrf52820",
119 feature = "nrf52833",
120 feature = "nrf52840",
121 feature = "_nrf5340-net"
122 ))]
123 Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE,
124 _ => unimplemented!(),
125 })
126 });
127 }
128
129 /// Set the header size changing the S1's len field
130 ///
131 /// The radio must be disabled before calling this function
132 pub fn set_header_expansion(&mut self, use_s1_field: bool) {
133 assert!(self.state() == RadioState::DISABLED);
134
135 let r = T::regs();
136
137 // s1 len in bits
138 let s1len: u8 = match use_s1_field {
139 false => 0,
140 true => 8,
141 };
142
143 r.pcnf0().write(|w| {
144 // Configure S0 to 1 byte length, this will represent the Data/Adv header flags
145 w.set_s0len(true);
146 // Configure the length (in bits) field to 1 byte length, this will represent the length of the payload
147 // and also be used to know how many bytes to read/write from/to the buffer
148 w.set_lflen(0);
149 // Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE.
150 w.set_s1len(s1len);
151 });
152 }
153
154 /// Set initial data whitening value
155 /// Data whitening is used to avoid long sequences of zeros or ones, e.g., 0b0000000 or 0b1111111, in the data bit stream
156 /// On BLE the initial value is the channel index | 0x40
157 ///
158 /// The radio must be disabled before calling this function
159 pub fn set_whitening_init(&mut self, whitening_init: u8) {
160 assert!(self.state() == RadioState::DISABLED);
161
162 let r = T::regs();
163
164 r.datawhiteiv().write(|w| w.set_datawhiteiv(whitening_init));
165 }
166
167 /// Set the central frequency to be used
168 /// It should be in the range 2400..2500
169 ///
170 /// [The radio must be disabled before calling this function](https://devzone.nordicsemi.com/f/nordic-q-a/15829/radio-frequency-change)
171 pub fn set_frequency(&mut self, frequency: u32) {
172 assert!(self.state() == RadioState::DISABLED);
173 assert!((2400..=2500).contains(&frequency));
174
175 let r = T::regs();
176
177 r.frequency().write(|w| w.set_frequency((frequency - 2400) as u8));
178 }
179
180 /// Set the acess address
181 /// This address is always constants for advertising
182 /// And a random value generate on each connection
183 /// It is used to filter the packages
184 ///
185 /// The radio must be disabled before calling this function
186 pub fn set_access_address(&mut self, access_address: u32) {
187 assert!(self.state() == RadioState::DISABLED);
188
189 let r = T::regs();
190
191 // Configure logical address
192 // The byte ordering on air is always least significant byte first for the address
193 // So for the address 0xAA_BB_CC_DD, the address on air will be DD CC BB AA
194 // The package order is BASE, PREFIX so BASE=0xBB_CC_DD and PREFIX=0xAA
195 r.prefix0().write(|w| w.set_ap0((access_address >> 24) as u8));
196
197 // The base address is truncated from the least significant byte (because the BALEN is less than 4)
198 // So it shifts the address to the right
199 r.base0().write_value(access_address << 8);
200
201 // Don't match tx address
202 r.txaddress().write(|w| w.set_txaddress(0));
203
204 // Match on logical address
205 // This config only filter the packets by the address,
206 // so only packages send to the previous address
207 // will finish the reception (TODO: check the explanation)
208 r.rxaddresses().write(|w| {
209 w.set_addr0(true);
210 w.set_addr1(true);
211 w.set_addr2(true);
212 w.set_addr3(true);
213 w.set_addr4(true);
214 });
215 }
216
217 /// Set the CRC polynomial
218 /// It only uses the 24 least significant bits
219 ///
220 /// The radio must be disabled before calling this function
221 pub fn set_crc_poly(&mut self, crc_poly: u32) {
222 assert!(self.state() == RadioState::DISABLED);
223
224 let r = T::regs();
225
226 r.crcpoly().write(|w| {
227 // Configure the CRC polynomial
228 // Each term in the CRC polynomial is mapped to a bit in this
229 // register which index corresponds to the term's exponent.
230 // The least significant term/bit is hard-wired internally to
231 // 1, and bit number 0 of the register content is ignored by
232 // the hardware. The following example is for an 8 bit CRC
233 // polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 .
234 w.set_crcpoly(crc_poly & 0xFFFFFF)
235 });
236 }
237
238 /// Set the CRC init value
239 /// It only uses the 24 least significant bits
240 /// The CRC initial value varies depending of the PDU type
241 ///
242 /// The radio must be disabled before calling this function
243 pub fn set_crc_init(&mut self, crc_init: u32) {
244 assert!(self.state() == RadioState::DISABLED);
245
246 let r = T::regs();
247
248 r.crcinit().write(|w| w.set_crcinit(crc_init & 0xFFFFFF));
249 }
250
251 /// Set the radio tx power
252 ///
253 /// The radio must be disabled before calling this function
254 pub fn set_tx_power(&mut self, tx_power: TxPower) {
255 assert!(self.state() == RadioState::DISABLED);
256
257 let r = T::regs();
258
259 r.txpower().write(|w| w.set_txpower(tx_power));
260 }
261
262 /// Set buffer to read/write
263 ///
264 /// This method is unsound. You should guarantee that the buffer will live
265 /// for the life time of the transmission or if the buffer will be modified.
266 /// Also if the buffer is smaller than the packet length, the radio will
267 /// read/write memory out of the buffer bounds.
268 fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
269 slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
270
271 let r = T::regs();
272
273 // Here it consider that the length of the packet is
274 // correctly set in the buffer, otherwise it will send
275 // unowned regions of memory
276 let ptr = buffer.as_ptr();
277
278 // Configure the payload
279 r.packetptr().write_value(ptr as u32);
280
281 Ok(())
282 }
283
284 /// Send packet
285 /// If the length byte in the package is greater than the buffer length
286 /// the radio will read memory out of the buffer bounds
287 pub async fn transmit(&mut self, buffer: &[u8]) -> Result<(), Error> {
288 self.set_buffer(buffer)?;
289
290 let r = T::regs();
291 self.trigger_and_wait_end(move || {
292 // Initialize the transmission
293 // trace!("txen");
294
295 r.tasks_txen().write_value(1);
296 })
297 .await;
298
299 Ok(())
300 }
301
302 /// Receive packet
303 /// If the length byte in the received package is greater than the buffer length
304 /// the radio will write memory out of the buffer bounds
305 pub async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
306 self.set_buffer(buffer)?;
307
308 let r = T::regs();
309 self.trigger_and_wait_end(move || {
310 // Initialize the transmission
311 // trace!("rxen");
312 r.tasks_rxen().write_value(1);
313 })
314 .await;
315
316 Ok(())
317 }
318
319 async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) {
320 let r = T::regs();
321 let s = T::state();
322
323 // If the Future is dropped before the end of the transmission
324 // it disable the interrupt and stop the transmission
325 // to keep the state consistent
326 let drop = OnDrop::new(|| {
327 trace!("radio drop: stopping");
328
329 r.intenclr().write(|w| w.set_end(true));
330
331 r.tasks_stop().write_value(1);
332
333 r.events_end().write_value(0);
334
335 trace!("radio drop: stopped");
336 });
337
338 // trace!("radio:enable interrupt");
339 // Clear some remnant side-effects (TODO: check if this is necessary)
340 r.events_end().write_value(0);
341
342 // Enable interrupt
343 r.intenset().write(|w| w.set_end(true));
344
345 compiler_fence(Ordering::SeqCst);
346
347 // Trigger the transmission
348 trigger();
349
350 // On poll check if interrupt happen
351 poll_fn(|cx| {
352 s.event_waker.register(cx.waker());
353 if r.events_end().read() == 1 {
354 // trace!("radio:end");
355 return core::task::Poll::Ready(());
356 }
357 Poll::Pending
358 })
359 .await;
360
361 compiler_fence(Ordering::SeqCst);
362 r.events_end().write_value(0); // ACK
363
364 // Everthing ends fine, so it disable the drop
365 drop.defuse();
366 }
367
368 /// Disable the radio
369 fn disable(&mut self) {
370 let r = T::regs();
371
372 compiler_fence(Ordering::SeqCst);
373 // If it is already disabled, do nothing
374 if self.state() != RadioState::DISABLED {
375 trace!("radio:disable");
376 // Trigger the disable task
377 r.tasks_disable().write_value(1);
378
379 // Wait until the radio is disabled
380 while r.events_disabled().read() == 0 {}
381
382 compiler_fence(Ordering::SeqCst);
383
384 // Acknowledge it
385 r.events_disabled().write_value(0);
386 }
387 }
388}
389
390impl<'d, T: Instance> Drop for Radio<'d, T> {
391 fn drop(&mut self) {
392 self.disable();
393 }
394}
diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs
index 2f0bcbe04..7f4f8f462 100644
--- a/embassy-nrf/src/radio/ieee802154.rs
+++ b/embassy-nrf/src/radio/ieee802154.rs
@@ -5,10 +5,11 @@ use core::task::Poll;
5 5
6use embassy_hal_internal::drop::OnDrop; 6use embassy_hal_internal::drop::OnDrop;
7 7
8use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower}; 8use super::{Error, Instance, InterruptHandler, TxPower};
9use crate::interrupt::typelevel::Interrupt; 9use crate::interrupt::typelevel::Interrupt;
10use crate::interrupt::{self}; 10use crate::interrupt::{self};
11use crate::pac::radio::vals; 11use crate::pac::radio::vals;
12pub use crate::pac::radio::vals::State as RadioState;
12use crate::Peri; 13use crate::Peri;
13 14
14/// Default (IEEE compliant) Start of Frame Delimiter 15/// Default (IEEE compliant) Start of Frame Delimiter
@@ -200,7 +201,7 @@ impl<'d, T: Instance> Radio<'d, T> {
200 201
201 /// Get the current radio state 202 /// Get the current radio state
202 fn state(&self) -> RadioState { 203 fn state(&self) -> RadioState {
203 state(T::regs()) 204 T::regs().state().read().state()
204 } 205 }
205 206
206 /// Moves the radio from any state to the DISABLED state 207 /// Moves the radio from any state to the DISABLED state
@@ -293,7 +294,7 @@ impl<'d, T: Instance> Radio<'d, T> {
293 r.shorts().write(|_| {}); 294 r.shorts().write(|_| {});
294 r.tasks_stop().write_value(1); 295 r.tasks_stop().write_value(1);
295 loop { 296 loop {
296 match state(r) { 297 match r.state().read().state() {
297 RadioState::DISABLED | RadioState::RX_IDLE => break, 298 RadioState::DISABLED | RadioState::RX_IDLE => break,
298 _ => (), 299 _ => (),
299 } 300 }
diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs
index 982436266..608ef9024 100644
--- a/embassy-nrf/src/radio/mod.rs
+++ b/embassy-nrf/src/radio/mod.rs
@@ -6,7 +6,6 @@
6#![macro_use] 6#![macro_use]
7 7
8/// Bluetooth Low Energy Radio driver. 8/// Bluetooth Low Energy Radio driver.
9pub mod ble;
10#[cfg(any( 9#[cfg(any(
11 feature = "nrf52811", 10 feature = "nrf52811",
12 feature = "nrf52820", 11 feature = "nrf52820",
@@ -21,7 +20,6 @@ use core::marker::PhantomData;
21 20
22use embassy_hal_internal::PeripheralType; 21use embassy_hal_internal::PeripheralType;
23use embassy_sync::waitqueue::AtomicWaker; 22use embassy_sync::waitqueue::AtomicWaker;
24use pac::radio::vals::State as RadioState;
25pub use pac::radio::vals::Txpower as TxPower; 23pub use pac::radio::vals::Txpower as TxPower;
26 24
27use crate::{interrupt, pac}; 25use crate::{interrupt, pac};
@@ -82,6 +80,7 @@ macro_rules! impl_radio {
82 pac::$pac_type 80 pac::$pac_type
83 } 81 }
84 82
83 #[allow(unused)]
85 fn state() -> &'static crate::radio::State { 84 fn state() -> &'static crate::radio::State {
86 static STATE: crate::radio::State = crate::radio::State::new(); 85 static STATE: crate::radio::State = crate::radio::State::new();
87 &STATE 86 &STATE
@@ -99,8 +98,3 @@ pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
99 /// Interrupt for this peripheral. 98 /// Interrupt for this peripheral.
100 type Interrupt: interrupt::typelevel::Interrupt; 99 type Interrupt: interrupt::typelevel::Interrupt;
101} 100}
102
103/// Get the state of the radio
104pub(crate) fn state(radio: pac::radio::Radio) -> RadioState {
105 radio.state().read().state()
106}