Some more unifying, documentation
This commit is contained in:
parent
0c4c996339
commit
c00f014f18
3 changed files with 61 additions and 90 deletions
embassy-nrf/src/radio
|
@ -8,8 +8,6 @@ use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
pub use pac::radio::mode::MODE_A as Mode;
|
pub use pac::radio::mode::MODE_A as Mode;
|
||||||
use pac::radio::pcnf0::PLEN_A as PreambleLength;
|
use pac::radio::pcnf0::PLEN_A as PreambleLength;
|
||||||
use pac::radio::state::STATE_A as RadioState;
|
|
||||||
pub use pac::radio::txpower::TXPOWER_A as TxPower;
|
|
||||||
|
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
pub use crate::radio::Error;
|
pub use crate::radio::Error;
|
||||||
|
@ -111,17 +109,7 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn trace_state(&self) {
|
fn trace_state(&self) {
|
||||||
match self.state() {
|
super::trace_state(T::regs())
|
||||||
RadioState::DISABLED => trace!("radio:state:DISABLED"),
|
|
||||||
RadioState::RX_RU => trace!("radio:state:RX_RU"),
|
|
||||||
RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"),
|
|
||||||
RadioState::RX => trace!("radio:state:RX"),
|
|
||||||
RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"),
|
|
||||||
RadioState::TX_RU => trace!("radio:state:TX_RU"),
|
|
||||||
RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"),
|
|
||||||
RadioState::TX => trace!("radio:state:TX"),
|
|
||||||
RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the radio mode
|
/// Set the radio mode
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
//! IEEE 802.15.4 radio
|
//! IEEE 802.15.4 radio driver
|
||||||
|
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_internal::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use pac::radio::state::STATE_A as RadioState;
|
|
||||||
use pac::radio::txpower::TXPOWER_A as TxPower;
|
|
||||||
|
|
||||||
use super::{Error, Instance, InterruptHandler};
|
use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower};
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
use crate::interrupt::{self};
|
use crate::interrupt::{self};
|
||||||
use crate::{pac, Peripheral};
|
use crate::Peripheral;
|
||||||
|
|
||||||
/// Default Start of Frame Delimiter = `0xA7` (IEEE compliant)
|
/// Default (IEEE compliant) Start of Frame Delimiter
|
||||||
pub const DEFAULT_SFD: u8 = 0xA7;
|
pub const DEFAULT_SFD: u8 = 0xA7;
|
||||||
|
|
||||||
// TODO expose the other variants in `pac::CCAMODE_A`
|
// TODO expose the other variants in `pac::CCAMODE_A`
|
||||||
|
@ -32,35 +30,14 @@ pub enum Cca {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_state(radio: &pac::radio::RegisterBlock) -> RadioState {
|
/// IEEE 802.15.4 radio driver.
|
||||||
match radio.state.read().state().variant() {
|
|
||||||
Some(state) => state,
|
|
||||||
None => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trace_state(state: RadioState) {
|
|
||||||
match state {
|
|
||||||
RadioState::DISABLED => trace!("radio:state:DISABLED"),
|
|
||||||
RadioState::RX_RU => trace!("radio:state:RX_RU"),
|
|
||||||
RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"),
|
|
||||||
RadioState::RX => trace!("radio:state:RX"),
|
|
||||||
RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"),
|
|
||||||
RadioState::TX_RU => trace!("radio:state:TX_RU"),
|
|
||||||
RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"),
|
|
||||||
RadioState::TX => trace!("radio:state:TX"),
|
|
||||||
RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Radio driver.
|
|
||||||
pub struct Radio<'d, T: Instance> {
|
pub struct Radio<'d, T: Instance> {
|
||||||
_p: PeripheralRef<'d, T>,
|
_p: PeripheralRef<'d, T>,
|
||||||
needs_enable: bool,
|
needs_enable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Radio<'d, T> {
|
impl<'d, T: Instance> Radio<'d, T> {
|
||||||
/// Create a new radio driver.
|
/// Create a new IEEE 802.15.4 radio driver.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
radio: impl Peripheral<P = T> + 'd,
|
radio: impl Peripheral<P = T> + 'd,
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
|
@ -81,40 +58,43 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||||
// Configure CRC polynomial and init
|
// Configure CRC polynomial and init
|
||||||
r.crcpoly.write(|w| w.crcpoly().bits(0x0001_1021));
|
r.crcpoly.write(|w| w.crcpoly().bits(0x0001_1021));
|
||||||
r.crcinit.write(|w| w.crcinit().bits(0));
|
r.crcinit.write(|w| w.crcinit().bits(0));
|
||||||
// Configure packet layout
|
|
||||||
// 8-bit on air length
|
|
||||||
// S0 length, zero bytes
|
|
||||||
// S1 length, zero bytes
|
|
||||||
// S1 included in RAM if S1 length > 0, No.
|
|
||||||
// Code Indicator length, 0
|
|
||||||
// Preamble length 32-bit zero
|
|
||||||
// Exclude CRC
|
|
||||||
// No TERM field
|
|
||||||
r.pcnf0.write(|w| {
|
r.pcnf0.write(|w| {
|
||||||
|
// 8-bit on air length
|
||||||
w.lflen()
|
w.lflen()
|
||||||
.bits(8)
|
.bits(8)
|
||||||
|
// Zero bytes S0 field length
|
||||||
.s0len()
|
.s0len()
|
||||||
.clear_bit()
|
.clear_bit()
|
||||||
|
// Zero bytes S1 field length
|
||||||
.s1len()
|
.s1len()
|
||||||
.bits(0)
|
.bits(0)
|
||||||
|
// Do not include S1 field in RAM if S1 length > 0
|
||||||
.s1incl()
|
.s1incl()
|
||||||
.clear_bit()
|
.clear_bit()
|
||||||
|
// Zero code Indicator length
|
||||||
.cilen()
|
.cilen()
|
||||||
.bits(0)
|
.bits(0)
|
||||||
|
// 32-bit zero preamble
|
||||||
.plen()
|
.plen()
|
||||||
._32bit_zero()
|
._32bit_zero()
|
||||||
|
// Include CRC in length
|
||||||
.crcinc()
|
.crcinc()
|
||||||
.include()
|
.include()
|
||||||
});
|
});
|
||||||
r.pcnf1.write(|w| {
|
r.pcnf1.write(|w| {
|
||||||
|
// Maximum packet length
|
||||||
w.maxlen()
|
w.maxlen()
|
||||||
.bits(Packet::MAX_PSDU_LEN)
|
.bits(Packet::MAX_PSDU_LEN)
|
||||||
|
// Zero static length
|
||||||
.statlen()
|
.statlen()
|
||||||
.bits(0)
|
.bits(0)
|
||||||
|
// Zero base address length
|
||||||
.balen()
|
.balen()
|
||||||
.bits(0)
|
.bits(0)
|
||||||
|
// Little-endian
|
||||||
.endian()
|
.endian()
|
||||||
.clear_bit()
|
.clear_bit()
|
||||||
|
// Disable packet whitening
|
||||||
.whiteen()
|
.whiteen()
|
||||||
.clear_bit()
|
.clear_bit()
|
||||||
});
|
});
|
||||||
|
@ -208,16 +188,9 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||||
while self.state() != state {}
|
while self.state() != state {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current radio state
|
||||||
fn state(&self) -> RadioState {
|
fn state(&self) -> RadioState {
|
||||||
let r = T::regs();
|
state(T::regs())
|
||||||
match r.state.read().state().variant() {
|
|
||||||
Some(state) => state,
|
|
||||||
None => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trace_state(&self) {
|
|
||||||
trace_state(self.state());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves the radio from any state to the DISABLED state
|
/// Moves the radio from any state to the DISABLED state
|
||||||
|
@ -227,20 +200,17 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||||
loop {
|
loop {
|
||||||
match self.state() {
|
match self.state() {
|
||||||
RadioState::DISABLED => return,
|
RadioState::DISABLED => return,
|
||||||
|
// idle or ramping up
|
||||||
RadioState::RX_RU | RadioState::RX_IDLE | RadioState::TX_RU | RadioState::TX_IDLE => {
|
RadioState::RX_RU | RadioState::RX_IDLE | RadioState::TX_RU | RadioState::TX_IDLE => {
|
||||||
r.tasks_disable.write(|w| w.tasks_disable().set_bit());
|
r.tasks_disable.write(|w| w.tasks_disable().set_bit());
|
||||||
|
|
||||||
self.wait_for_radio_state(RadioState::DISABLED);
|
self.wait_for_radio_state(RadioState::DISABLED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ramping down
|
// ramping down
|
||||||
RadioState::RX_DISABLE | RadioState::TX_DISABLE => {
|
RadioState::RX_DISABLE | RadioState::TX_DISABLE => {
|
||||||
self.wait_for_radio_state(RadioState::DISABLED);
|
self.wait_for_radio_state(RadioState::DISABLED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancel ongoing transfer or ongoing CCA
|
// cancel ongoing transfer or ongoing CCA
|
||||||
RadioState::RX => {
|
RadioState::RX => {
|
||||||
r.tasks_ccastop.write(|w| w.tasks_ccastop().set_bit());
|
r.tasks_ccastop.write(|w| w.tasks_ccastop().set_bit());
|
||||||
|
@ -262,35 +232,27 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||||
|
|
||||||
/// Moves the radio to the RXIDLE state
|
/// Moves the radio to the RXIDLE state
|
||||||
fn receive_prepare(&mut self) {
|
fn receive_prepare(&mut self) {
|
||||||
let state = self.state();
|
// clear related events
|
||||||
|
T::regs().events_ccabusy.reset();
|
||||||
let disable = match state {
|
T::regs().events_phyend.reset();
|
||||||
|
// NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RX_IDLE
|
||||||
|
let disable = match self.state() {
|
||||||
RadioState::DISABLED => false,
|
RadioState::DISABLED => false,
|
||||||
RadioState::RX_DISABLE => true,
|
|
||||||
RadioState::TX_DISABLE => true,
|
|
||||||
RadioState::RX_IDLE => self.needs_enable,
|
RadioState::RX_IDLE => self.needs_enable,
|
||||||
// NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RX_IDLE
|
_ => true,
|
||||||
RadioState::TX_IDLE => true,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
};
|
||||||
if disable {
|
if disable {
|
||||||
trace!("Receive Setup");
|
|
||||||
self.trace_state();
|
|
||||||
self.disable();
|
self.disable();
|
||||||
}
|
}
|
||||||
self.needs_enable = false;
|
self.needs_enable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prepare radio for receiving a packet
|
||||||
fn receive_start(&mut self, packet: &mut Packet) {
|
fn receive_start(&mut self, packet: &mut Packet) {
|
||||||
// NOTE we do NOT check the address of `packet` because the mutable reference ensures it's
|
// NOTE we do NOT check the address of `packet` because the mutable reference ensures it's
|
||||||
// allocated in RAM
|
// allocated in RAM
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
// clear related events
|
|
||||||
r.events_framestart.reset();
|
|
||||||
r.events_ccabusy.reset();
|
|
||||||
r.events_phyend.reset();
|
|
||||||
|
|
||||||
self.receive_prepare();
|
self.receive_prepare();
|
||||||
|
|
||||||
// Configure shortcuts
|
// Configure shortcuts
|
||||||
|
@ -314,15 +276,13 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cancel receiving packet
|
||||||
fn receive_cancel() {
|
fn receive_cancel() {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
r.shorts.reset();
|
r.shorts.reset();
|
||||||
if r.events_framestart.read().events_framestart().bit_is_set() {
|
|
||||||
// TODO: Is there a way to finish receiving this frame
|
|
||||||
}
|
|
||||||
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
|
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
|
||||||
loop {
|
loop {
|
||||||
match get_state(r) {
|
match state(r) {
|
||||||
RadioState::DISABLED | RadioState::RX_IDLE => break,
|
RadioState::DISABLED | RadioState::RX_IDLE => break,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -336,7 +296,7 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||||
/// This methods returns the `Ok` variant if the CRC included the packet was successfully
|
/// This methods returns the `Ok` variant if the CRC included the packet was successfully
|
||||||
/// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet`
|
/// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet`
|
||||||
/// will be updated with the received packet's data
|
/// will be updated with the received packet's data
|
||||||
pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), u16> {
|
pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), Error> {
|
||||||
let s = T::state();
|
let s = T::state();
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
|
@ -369,7 +329,7 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||||
if r.crcstatus.read().crcstatus().bit_is_set() {
|
if r.crcstatus.read().crcstatus().bit_is_set() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(crc)
|
Err(Error::CrcFailed(crc))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,11 +347,6 @@ impl<'d, T: Instance> Radio<'d, T> {
|
||||||
let s = T::state();
|
let s = T::state();
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
// clear related events
|
|
||||||
r.events_framestart.reset();
|
|
||||||
r.events_ccabusy.reset();
|
|
||||||
r.events_phyend.reset();
|
|
||||||
|
|
||||||
// enable radio to perform cca
|
// enable radio to perform cca
|
||||||
self.receive_prepare();
|
self.receive_prepare();
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,9 @@ use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::{interrupt, pac, Peripheral};
|
use crate::{interrupt, pac, Peripheral};
|
||||||
|
|
||||||
|
use pac::radio::state::STATE_A as RadioState;
|
||||||
|
use pac::radio::txpower::TXPOWER_A as TxPower;
|
||||||
|
|
||||||
/// RADIO error.
|
/// RADIO error.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
@ -28,6 +31,8 @@ pub enum Error {
|
||||||
BufferNotInRAM,
|
BufferNotInRAM,
|
||||||
/// Clear channel assessment reported channel in use
|
/// Clear channel assessment reported channel in use
|
||||||
ChannelInUse,
|
ChannelInUse,
|
||||||
|
/// CRC check failed
|
||||||
|
CrcFailed(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interrupt handler
|
/// Interrupt handler
|
||||||
|
@ -89,3 +94,26 @@ pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
||||||
/// Interrupt for this peripheral.
|
/// Interrupt for this peripheral.
|
||||||
type Interrupt: interrupt::typelevel::Interrupt;
|
type Interrupt: interrupt::typelevel::Interrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the state of the radio
|
||||||
|
pub(crate) fn state(radio: &pac::radio::RegisterBlock) -> RadioState {
|
||||||
|
match radio.state.read().state().variant() {
|
||||||
|
Some(state) => state,
|
||||||
|
None => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn trace_state(radio: &pac::radio::RegisterBlock) {
|
||||||
|
match state(radio) {
|
||||||
|
RadioState::DISABLED => trace!("radio:state:DISABLED"),
|
||||||
|
RadioState::RX_RU => trace!("radio:state:RX_RU"),
|
||||||
|
RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"),
|
||||||
|
RadioState::RX => trace!("radio:state:RX"),
|
||||||
|
RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"),
|
||||||
|
RadioState::TX_RU => trace!("radio:state:TX_RU"),
|
||||||
|
RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"),
|
||||||
|
RadioState::TX => trace!("radio:state:TX"),
|
||||||
|
RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue