Skip to content

Commit b57ba81

Browse files
authored
Merge pull request #1 from Lotterleben/radio_add_cca_ed
ieee802154.rs: add CCA-Energy Detection
2 parents b4fcf23 + cbc008c commit b57ba81

File tree

1 file changed

+65
-5
lines changed

1 file changed

+65
-5
lines changed

nrf52840-hal/src/ieee802154.rs

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub struct Radio<'c> {
3030
pub const DEFAULT_CCA: Cca = Cca::CarrierSense;
3131

3232
/// Default radio channel = Channel 20 (`2_450` MHz)
33-
pub const DEFAULT_CHANNEL: Channel = Channel::_20;
33+
pub const DEFAULT_CHANNEL: Channel = Channel::_18; // _17 todo undo; set this to interfere with my wifi
3434

3535
/// Default TX power = 0 dBm
3636
pub const DEFAULT_TXPOWER: TxPower = TxPower::_0dBm;
@@ -43,6 +43,15 @@ pub const DEFAULT_SFD: u8 = 0xA7;
4343
pub enum Cca {
4444
/// Carrier sense
4545
CarrierSense,
46+
/// Energy Detection / Energy Above Threshold
47+
EnergyDetection {
48+
/// Energy measurements above this value mean that the channel is assumed to be busy.
49+
/// Note the the measurement range is 0..0xFF - where 0 means that the received power was
50+
/// less than 10 dB above the selected receiver sensitivity. This value is not given in dBm,
51+
/// but can be converted. See the nrf52840 Product Specification Section 6.20.12.4
52+
/// for details.
53+
ed_threshold: u8,
54+
},
4655
}
4756

4857
/// IEEE 802.15.4 channels
@@ -203,8 +212,8 @@ impl<'c> Radio<'c> {
203212
}
204213

205214
// set default settings
206-
radio.set_cca(DEFAULT_CCA);
207215
radio.set_channel(DEFAULT_CHANNEL);
216+
radio.set_cca(DEFAULT_CCA);
208217
radio.set_sfd(DEFAULT_SFD);
209218
radio.set_txpower(DEFAULT_TXPOWER);
210219

@@ -226,6 +235,13 @@ impl<'c> Radio<'c> {
226235
self.needs_enable = true;
227236
match cca {
228237
Cca::CarrierSense => self.radio.ccactrl.write(|w| w.ccamode().carrier_mode()),
238+
Cca::EnergyDetection { ed_threshold } => {
239+
// "[ED] is enabled by first configuring the field CCAMODE=EdMode in CCACTRL
240+
// and writing the CCAEDTHRES field to a chosen value."
241+
self.radio
242+
.ccactrl
243+
.write(|w| unsafe { w.ccamode().ed_mode().ccaedthres().bits(ed_threshold) });
244+
}
229245
}
230246
}
231247

@@ -243,6 +259,48 @@ impl<'c> Radio<'c> {
243259
.write(|w| w.txpower().variant(power._into()));
244260
}
245261

262+
/// Sample the received signal power (i.e. the presence of possibly interfering signals)
263+
/// within the bandwidth of the currently used channel for sample_cycles iterations.
264+
/// Note that one iteration has a sample time of 128μs, and that each iteration produces the
265+
/// average RSSI value measured during this sample time.
266+
///
267+
/// Returns the *maximum* measurement recorded during sampling as reported by the hardware (not in dBm!).
268+
/// The result can be used to find a suitable ED threshold for Energy Detection-based CCA mechanisms.
269+
///
270+
/// For details, see Section 6.20.12.3 Energy detection (ED) of the PS.
271+
/// RSSI samples are averaged over a measurement time of 8 symbol periods (128 μs).
272+
pub fn energy_detection_scan(&mut self, sample_cycles: u32) -> u8 {
273+
unsafe {
274+
// Increase the time spent listening
275+
self.radio.edcnt.write(|w| w.edcnt().bits(sample_cycles));
276+
}
277+
278+
// ensure that the shortcut between READY event and START task is disabled before putting
279+
// the radio into recv mode
280+
self.radio.shorts.reset();
281+
self.put_in_rx_mode();
282+
283+
// clear related events
284+
self.radio.events_edend.reset();
285+
286+
// start energy detection sampling
287+
self.radio
288+
.tasks_edstart
289+
.write(|w| w.tasks_edstart().set_bit());
290+
291+
loop {
292+
if self.radio.events_edend.read().events_edend().bit_is_set() {
293+
// sampling period is over; collect value
294+
self.radio.events_edend.reset();
295+
296+
// note that since we have increased EDCNT, the EDSAMPLE register contains the
297+
// maximumrecorded value, not the average
298+
let read_lvl = self.radio.edsample.read().edlvl().bits();
299+
return read_lvl;
300+
}
301+
}
302+
}
303+
246304
/// Recevies one radio packet and copies its contents into the given `packet` buffer
247305
///
248306
/// This methods returns the `Ok` variant if the CRC included the packet was successfully
@@ -356,6 +414,7 @@ impl<'c> Radio<'c> {
356414
/// packet is transmitted and the `Err` variant is returned
357415
// NOTE we do NOT check the address of `packet`; see comment in `Packet::new` for details
358416
pub fn try_send(&mut self, packet: &Packet) -> Result<(), ()> {
417+
// enable radio to perform cca
359418
self.put_in_rx_mode();
360419

361420
// clear related events
@@ -369,15 +428,15 @@ impl<'c> Radio<'c> {
369428
.write(|w| w.packetptr().bits(packet.buffer.as_ptr() as u32));
370429
}
371430

372-
// immediately start transmission if the channel is idle
431+
// configure radio to immediately start transmission if the channel is idle
373432
self.radio
374433
.shorts
375434
.modify(|_, w| w.ccaidle_txen().set_bit().txready_start().set_bit());
376435

377436
// the DMA transfer will start at some point after the following write operation so
378437
// we place the compiler fence here
379438
dma_start_fence();
380-
// start CCA
439+
// start CCA. In case the channel is clear, the data at packetptr will be sent automatically
381440
self.radio
382441
.tasks_ccastart
383442
.write(|w| w.tasks_ccastart().set_bit());
@@ -413,6 +472,7 @@ impl<'c> Radio<'c> {
413472
/// CCA attempts to be spec compliant
414473
// NOTE we do NOT check the address of `packet`; see comment in `Packet::new` for details
415474
pub fn send(&mut self, packet: &Packet) {
475+
// enable radio to perform cca
416476
self.put_in_rx_mode();
417477

418478
// clear related events
@@ -435,7 +495,7 @@ impl<'c> Radio<'c> {
435495
}
436496

437497
'cca: loop {
438-
// start CCA
498+
// start CCA (+ sending if channel is clear)
439499
self.radio
440500
.tasks_ccastart
441501
.write(|w| w.tasks_ccastart().set_bit());

0 commit comments

Comments
 (0)