initial ieee802.15.4 API#143
Conversation
|
Thanks for this work.
Both the nRF52810 and nRF52832 don't support 802.15.4 mode. 51 probably doesn't support it either. |
|
Cool stuff! I need to test this on my boards =) The 52840, 52811 52833 and 5340 support 802.15.4. Are you aware of the https://crates.io/crates/ieee802154 crate? I think it is higher level (MAC) layer, but could be useful to test this and not reinvent the wheel. Thanks for putting in the work! |
That's the MAC frame. Yes, that's what you should put in the payload of the Also, yes that would be useful to test this against the MRF24J40, which (iirc) does do HW-level MAC address filtering. Though that may end up being more of a test for the |
The DW1000 uses UWB (ultra-wideband) in the PHY layer, so I don't think it can communicate with the nRF radio (I might be wrong wrong; despite my work with the DW1000/DWM1001, I know very little about radios).
The DW1000 also does hardware-based address matching and this is used in the driver. To my knowledge, this has never caused any problems with |
|
Yes, the DW1000 is a UWB PHY (so around 500 MHz bandwidth at 3 to 10 GHz depending on channel) and hence incompatible with a 2.4 GHz PHY (around 80 MHz bandwidth at 2.4 GHz) based system. I wasn't aware the DW1000 had hardware address detection, so that's good to know! Getting back on topic, Rust 802.15.4 on the nRF52 series is excellent. Great work :) |
|
Nice! Looks better than my old implementation. |
|
for those not getting notifications on new commits being pushed: I have added APIs to change the TX power and the start of frame delimiter; added a method the get a packet's Link Quality Indicator; and added CRC verification to the Radio.recv method (the method now returns a Result) |
not all values in the -40..=8 range are valid configurations
this method lets you insert a delay between failed CCAs to be IEEE spec compliant
hubertmis
left a comment
There was a problem hiding this comment.
This driver looks nice. Based on experience with similar code in C, I've posted multiple suggestions that could be helpful to improve it.
| } | ||
|
|
||
| /// Default Clear Channel Assessment method = Carrier sense | ||
| pub const DEFAULT_CCA: Cca = Cca::CarrierSense; |
There was a problem hiding this comment.
The CarrierSense mode needs some calibration to work correctly. I would recommend EnergyDetection mode as a default one.
| /// IEEE 802.15.4 channels | ||
| /// | ||
| /// NOTE these are NOT the same as WiFi 2.4 GHz channels | ||
| // TODO it is possible to use non-standard frequencies below 2_400 MHz; should those be exposed too? |
There was a problem hiding this comment.
This list is correct. To be compliant with IEEE 802.15.4 other frequencies shall not be exposed. The hardware in selected nRF devices is capable of using other frequencies, but it should not be used in 802.15.4 protocol.
|
|
||
| radio.radio.pcnf1.write(|w| { | ||
| w.maxlen() | ||
| .bits(Packet::MAX_LEN + 2 /* CRC */) // payload length |
There was a problem hiding this comment.
This line is a little confusing. When we configure hardware, we usually work on PHY layer. Max PSDU length is 127 and it includes CRC. I would change this constant to MAX_PSDU_LEN = 127 and use it here without + 2. It would be unambiguous.
| pub fn set_channel(&mut self, channel: Channel) { | ||
| self.disable(); | ||
|
|
||
| // NOTE(unsafe) radio is currently disabled |
There was a problem hiding this comment.
It is OK to change frequency while radio is enabled. The FREQUENCY register is double buffered and it is read on RXEN or TXEN tasks.
There is no need to call self.disable(); before.
|
|
||
| /// Changes the Clear Channel Assessment method | ||
| pub fn set_cca(&mut self, cca: Cca) { | ||
| self.disable(); |
There was a problem hiding this comment.
Like with frequency it is OK to change CCA mode while radio is enabled.
| match state { | ||
| STATE_A::DISABLED => return, | ||
|
|
||
| STATE_A::RXRU | STATE_A::RXIDLE | STATE_A::TXRU | STATE_A::TXIDLE => { |
There was a problem hiding this comment.
RXIDLE state may indicate ongoing CCA procedure that may result in TX with radio shorts. To be fully safe, following set of tasks should be triggered:
CCASTOP
STOP
DISABLE
| } | ||
|
|
||
| /// Moves the radio from any state to the DISABLED state | ||
| fn disable(&mut self) { |
There was a problem hiding this comment.
There is a possibility that with RADIO SHORTS enabled this function would work in an unexpected way. I would add at least assertion here to verify that selected shorts are disabled.
|
|
||
| /// An IEEE 802.15.4 packet | ||
| /// | ||
| /// This `Packet` is closest to the PPDU (PHY Protocol Data Unit) defined in the IEEE spec. The API |
There was a problem hiding this comment.
closest to the PPDU is ambiguous. I would state here clearly that this packet is PHR concatenated with PSDU
| /// | ||
| /// NOTE `src` data will be truncated to `MAX_PACKET_SIZE` bytes | ||
| pub fn copy_from_slice(&mut self, src: &[u8]) { | ||
| let len = cmp::min(src.len(), Self::MAX_LEN as usize) as u8; |
There was a problem hiding this comment.
I don't think it is a good idea to silently limit the given slice size. When user of this function wants to transmit 200 bytes packet and calls this function, would get only 125 bytes transmitted and would not be notified that there was a problem. I would change function signature to return Result or add assertion.
| /// | ||
| /// NOTE `len` will be truncated to `MAX_LEN` bytes | ||
| pub fn set_len(&mut self, len: u8) { | ||
| let len = cmp::min(len, Self::MAX_LEN); |
There was a problem hiding this comment.
Again silently truncated. Using such API could be a source of hard to find bugs in the higher layers.
the previous version result in `recv` resuming immediately after issuing a `*send`
|
@hubertmis thanks for the great feedback! (I have seen it just now). I'll incorporate it in the PR in the coming days. |
- Packet::set_len: panic instead of silently clapping the value - Packet::copy_from_slice: panic instead of silently truncating - rename some internal constants related to the size of the parts of Packet - doc tweak: Packet is PHR + PSDU - keep Channel enum as it is (removed TODO) - add send_no_cca method - shortcut ccaidle to txen in try_send - simplify transitions to the TXIDLE / RXIDLE state
hubertmis
left a comment
There was a problem hiding this comment.
The driver looks solid. There is place to add more features and optimizations.
I'm not a fan of blocking recv method. But making it asynchronous would need serious refactoring of the driver. For simple applications blocking approach is sufficient.
|
@japaric , to verify CRC of generated frames, you can easily use https://github.com/NordicSemiconductor/nRF-Sniffer-for-802.15.4 . Firmware used in this project reports only frames with valid CRC. |
Co-authored-by: Hubert Miś <hubert.mis@nordicsemi.no>
ieee802154.rs: add CCA-Energy Detection
|
I was thinking about timing out while receiving. Current implementation blocks execution of the calling thread until a frame is received or a timeout fires. It's not good for the system performance. In drivers in C usually asynchronous approach is implemented: function like Recently I've spotted https://github.com/rust-embedded/nb and it looks like a solution to this problem. The user of the driver could call What do you think about it? |
| pub const DEFAULT_CCA: Cca = Cca::CarrierSense; | ||
|
|
||
| /// Default radio channel = Channel 20 (`2_450` MHz) | ||
| pub const DEFAULT_CHANNEL: Channel = Channel::_18; // _17 todo undo; set this to interfere with my wifi |
There was a problem hiding this comment.
It looks like this needs to be reset to channel 17.
Are the defaults specified somewhere or were they picked arbitrarily?
There was a problem hiding this comment.
the FREQUENCY register reset value is 2, which doesn't match to any of the Channel values because they are all multiples of 5.
The channel values (the frequencies) are the standard IEEE frequencies.
There was a problem hiding this comment.
changed the default channel to 11 (2405 MHz) which is closer to the reset value of the FREQUENCY register (2402 MHz)
Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
to enforce that the buffer is RAM allocated
|
Okay, I think this is ready to go. The remaining notes can be addressed later, and an async/non-blocking API can also be added then. bors r+ |
|
Build succeeded: |
|
I only just noticed that the module was added directly to the nrf52840-hal instead of nrf-hal-common. I've opened #299 to move it (and enable it for the nRF52833 in the process). |
what the title says
this PR adds an
ieee802154::RadioAPI that wraps theRADIOperipheral and puts it in IEEE 802.15.4 mode.I haven't checked the silicon erratas of (or tested) other chips (the code contains one workaround for (nRF52840 specific?) silicon errata) so for now I have put the API directly under
nrf52840-hal. But I guess the API should also work on other nRF52 devices -- I think the nRF51 chips do not have an 802.15.4 mode? And I don't know about other chip families.The implementation is not super IEEE compliant. It's missing a backoff delay mechanism when doing CCA (Clear Channel Assessment) before transmitting packets and it's also missing IFS (inter-frame spacing) between consecutive packets (the hardware has some built-in support for this but the PR doesn't expose it). But it is sufficient to send packets around and receive them on other nRF52s running the same implementation, though it would be good to check this against some third party implementation like the DWM1001 or the MRF24J40.
There are some TODOs here and there in terms of additional things the API could expose like changing the transmit power. I'm not sure if all those need to be added before a review though. They could be added as follow up PRs too.
TODO items