Add the esp-ieee802154 package to the repository (#1554)

* Add the `esp-ieee802154` package to the repo

* Add examples for `esp-ieee802154` package
This commit is contained in:
Jesse Braham 2024-05-15 15:22:27 +00:00 committed by GitHub
parent 3c05759556
commit 8a1df42309
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 1998 additions and 8 deletions

View File

@ -7,6 +7,7 @@ exclude = [
"esp-hal", "esp-hal",
"esp-hal-procmacros", "esp-hal-procmacros",
"esp-hal-smartled", "esp-hal-smartled",
"esp-ieee802154",
"esp-lp-hal", "esp-lp-hal",
"esp-metadata", "esp-metadata",
"esp-riscv-rt", "esp-riscv-rt",

31
esp-ieee802154/Cargo.toml Normal file
View File

@ -0,0 +1,31 @@
[package]
name = "esp-ieee802154"
version = "0.1.0"
edition = "2021"
rust-version = "1.76.0"
description = "Low-level IEEE 802.15.4 driver for the ESP32-C6 and ESP32-H2"
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.docs.rs]
default-target = "riscv32imac-unknown-none-elf"
features = ["esp32c6"]
[lib]
bench = false
test = false
[dependencies]
byte = "0.2.7"
critical-section = "1.1.2"
esp-hal = { version = "0.17.0", path = "../esp-hal" }
esp-wifi-sys = { git = "https://github.com/esp-rs/esp-wifi", rev = "2ceb4b3" }
heapless = "0.8.0"
ieee802154 = "0.6.1"
log = "0.4.21"
vcell = "0.1.3"
[features]
default = []
esp32c6 = ["esp-hal/esp32c6", "esp-wifi-sys/esp32c6"]
esp32h2 = ["esp-hal/esp32h2", "esp-wifi-sys/esp32h2"]

35
esp-ieee802154/README.md Normal file
View File

@ -0,0 +1,35 @@
# esp-ieee802154
[![Crates.io](https://img.shields.io/crates/v/esp-ieee802154?color=C96329&logo=Rust&style=flat-square)](https://crates.io/crates/esp-ieee802154)
[![docs.rs](https://img.shields.io/docsrs/esp-ieee802154?color=C96329&logo=rust&style=flat-square)](https://docs.rs/esp-ieee802154)
![MSRV](https://img.shields.io/badge/MSRV-1.76-blue?labelColor=1C2C2E&style=flat-square)
![Crates.io](https://img.shields.io/crates/l/esp-ieee802154?style=flat-square)
[![Matrix](https://img.shields.io/matrix/esp-rs:matrix.org?label=join%20matrix&labelColor=1C2C2E&color=BEC5C9&logo=matrix&style=flat-square)](https://matrix.to/#/#esp-rs:matrix.org)
Low-level [IEEE 802.15.4] driver for the ESP32-C6 and ESP32-H2.
Implements the PHY/MAC layers of the IEEE802.15.4 protocol stack, and supports sending and receiving of raw frames.
[IEEE 802.15.4]: https://en.wikipedia.org/wiki/IEEE_802.15.4
## [Documentation](https://docs.rs/crate/esp-ieee802154)
## Minimum Supported Rust Version (MSRV)
This crate is guaranteed to compile on stable Rust 1.76 and up. It _might_
compile with older versions but that may change in any new patch release.
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
any additional terms or conditions.

6
esp-ieee802154/build.rs Normal file
View File

@ -0,0 +1,6 @@
use std::{env, path::PathBuf};
fn main() {
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
println!("cargo:rustc-link-search={}", out.display());
}

View File

@ -0,0 +1,130 @@
use core::{ffi::VaListImpl, fmt::Write};
use log::info;
use self::str_buf::StrBuf;
mod str_buf;
#[no_mangle]
pub unsafe extern "C" fn phy_printf(format: *const u8, args: ...) {
syslog(format, args);
}
#[no_mangle]
pub unsafe extern "C" fn rtc_printf(format: *const u8, args: ...) {
syslog(format, args);
}
#[no_mangle]
pub unsafe extern "C" fn coexist_printf(format: *const u8, args: ...) {
syslog(format, args);
}
pub unsafe extern "C" fn syslog(format: *const u8, args: VaListImpl) {
let mut buf = [0u8; 512];
vsnprintf(&mut buf as *mut u8, 511, format, args);
let res_str = StrBuf::from(&buf as *const u8);
info!("{}", res_str.as_str_ref());
}
pub(crate) unsafe fn vsnprintf(
dst: *mut u8,
_n: u32,
format: *const u8,
mut args: VaListImpl,
) -> i32 {
let fmt_str_ptr = format;
let mut res_str = StrBuf::new();
let strbuf = StrBuf::from(fmt_str_ptr);
let s = strbuf.as_str_ref();
let mut format_char = ' ';
let mut is_long = false;
let mut found = false;
for c in s.chars() {
if !found {
if c == '%' {
found = true;
}
if !found {
res_str.append_char(c);
}
} else if c.is_numeric() || c == '-' || c == 'l' {
if c == 'l' {
is_long = true;
}
// ignore
} else {
// a format char
format_char = c;
}
if found && format_char != ' ' {
// have to format an arg
match format_char {
'd' => {
if is_long {
let v = args.arg::<i64>();
write!(res_str, "{}", v).ok();
} else {
let v = args.arg::<i32>();
write!(res_str, "{}", v).ok();
}
}
'u' => {
let v = args.arg::<u32>();
write!(res_str, "{}", v).ok();
}
'p' => {
let v = args.arg::<u32>();
write!(res_str, "0x{:x}", v).ok();
}
'X' => {
let v = args.arg::<u32>();
write!(res_str, "{:02x}", (v & 0xff000000) >> 24).ok();
}
'x' => {
let v = args.arg::<u32>();
write!(res_str, "{:02x}", v).ok();
}
's' => {
let v = args.arg::<u32>() as *const u8;
let vbuf = StrBuf::from(v);
write!(res_str, "{}", vbuf.as_str_ref()).ok();
}
'c' => {
let v = args.arg::<u8>();
if v != 0 {
write!(res_str, "{}", v as char).ok();
}
}
_ => {
write!(res_str, "<UNKNOWN{}>", format_char).ok();
}
}
format_char = ' ';
found = false;
is_long = false;
}
}
let mut idx = 0;
res_str.as_str_ref().chars().for_each(|c| {
*(dst.offset(idx)) = c as u8;
idx += 1;
});
*(dst.offset(idx)) = 0;
idx as i32
}

View File

@ -0,0 +1,58 @@
use core::fmt::Write;
pub struct StrBuf {
buffer: [u8; 512],
len: usize,
}
impl StrBuf {
pub fn new() -> StrBuf {
StrBuf {
buffer: [0u8; 512],
len: 0,
}
}
pub unsafe fn from(c_str: *const u8) -> StrBuf {
let mut res = StrBuf {
buffer: [0u8; 512],
len: 0,
};
let mut idx: usize = 0;
while *(c_str.add(idx)) != 0 {
res.buffer[idx] = *(c_str.add(idx));
idx += 1;
}
res.len = idx;
res
}
pub fn append(&mut self, s: &str) {
let mut idx: usize = self.len;
s.chars().for_each(|c| {
self.buffer[idx] = c as u8;
idx += 1;
});
self.len = idx;
}
pub fn append_char(&mut self, c: char) {
let mut idx: usize = self.len;
self.buffer[idx] = c as u8;
idx += 1;
self.len = idx;
}
pub unsafe fn as_str_ref(&self) -> &str {
core::str::from_utf8_unchecked(&self.buffer[..self.len])
}
}
impl Write for StrBuf {
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
self.append(s);
Ok(())
}
}

View File

@ -0,0 +1,45 @@
use heapless::Vec;
use ieee802154::mac::{FrameContent, Header};
pub(crate) const FRAME_SIZE: usize = 129;
pub(crate) const FRAME_VERSION_1: u8 = 0x10; // IEEE 802.15.4 - 2006 & 2011
pub(crate) const FRAME_VERSION_2: u8 = 0x20; // IEEE 802.15.4 - 2015
const FRAME_AR_OFFSET: usize = 1;
const FRAME_AR_BIT: u8 = 0x20;
const FRAME_VERSION_OFFSET: usize = 2;
const FRAME_VERSION_MASK: u8 = 0x30;
/// IEEE 802.15.4 MAC frame
#[derive(Debug, Clone)]
pub struct Frame {
/// Header
pub header: Header,
/// Content
pub content: FrameContent,
/// Payload
pub payload: Vec<u8, FRAME_SIZE>,
/// This is a 2-byte CRC checksum
pub footer: [u8; 2],
}
/// IEEE 802.15.4 MAC frame which has been received
#[derive(Debug, Clone)]
pub struct ReceivedFrame {
/// Frame
pub frame: Frame,
/// Receiver channel
pub channel: u8,
/// Received Signal Strength Indicator (RSSI)
pub rssi: i8,
/// Link Quality Indication (LQI)
pub lqi: u8,
}
pub(crate) fn frame_is_ack_required(frame: &[u8]) -> bool {
(frame[FRAME_AR_OFFSET] & FRAME_AR_BIT) != 0
}
pub(crate) fn frame_get_version(frame: &[u8]) -> u8 {
frame[FRAME_VERSION_OFFSET] & FRAME_VERSION_MASK
}

437
esp-ieee802154/src/hal.rs Normal file
View File

@ -0,0 +1,437 @@
use core::ops::{BitAnd, BitOr};
use esp_hal::peripherals::IEEE802154;
use crate::pib::CcaMode;
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum Event {
TxDone = 1 << 0,
RxDone = 1 << 1,
AckTxDone = 1 << 2,
AckRxDone = 1 << 3,
RxAbort = 1 << 4,
TxAbort = 1 << 5,
EdDone = 1 << 6,
Timer0Overflow = 1 << 8,
Timer1Overflow = 1 << 9,
ClockCountMatch = 1 << 10,
TxSfdDone = 1 << 11,
RxSfdDone = 1 << 12,
}
impl Event {
pub(crate) fn mask() -> u16 {
0x0000_1FFF
}
}
impl BitAnd<Event> for u16 {
type Output = u16;
fn bitand(self, rhs: Event) -> Self::Output {
self & rhs as u16
}
}
impl BitOr for Event {
type Output = u16;
fn bitor(self, rhs: Self) -> Self::Output {
self as u16 | rhs as u16
}
}
impl BitOr<Event> for u16 {
type Output = u16;
fn bitor(self, rhs: Event) -> Self::Output {
self | rhs as u16
}
}
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum TxAbortReason {
RxAckStop = 1,
RxAckSfdTimeout = 2,
RxAckCrcError = 3,
RxAckInvalidLen = 4,
RxAckFilterFail = 5,
RxAckNoRss = 6,
RxAckCoexBreak = 7,
RxAckTypeNotAck = 8,
RxAckRestart = 9,
RxAckTimeout = 16,
TxStop = 17,
TxCoexBreak = 18,
TxSecurityError = 19,
CcaFailed = 24,
CcaBusy = 25,
}
impl TxAbortReason {
pub fn bit(&self) -> u32 {
1 << (*self as u32 - 1)
}
}
impl BitOr for TxAbortReason {
type Output = u32;
fn bitor(self, rhs: Self) -> Self::Output {
self.bit() | rhs.bit()
}
}
impl BitOr<TxAbortReason> for u32 {
type Output = u32;
fn bitor(self, rhs: TxAbortReason) -> Self::Output {
self | rhs.bit()
}
}
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum RxAbortReason {
RxStop = 1,
SfdTimeout = 2,
CrcError = 3,
InvalidLen = 4,
FilterFail = 5,
NoRss = 6,
CoexBreak = 7,
UnexpectedAck = 8,
RxRestart = 9,
TxAckTimeout = 16,
TxAckStop = 17,
TxAckCoexBreak = 18,
EnhackSecurityError = 19,
EdAbort = 24,
EdStop = 25,
EdCoexReject = 26,
}
impl RxAbortReason {
pub fn bit(&self) -> u32 {
1 << (*self as u32 - 1)
}
}
impl BitOr for RxAbortReason {
type Output = u32;
fn bitor(self, rhs: Self) -> Self::Output {
self.bit() | rhs.bit()
}
}
impl BitOr<RxAbortReason> for u32 {
type Output = u32;
fn bitor(self, rhs: RxAbortReason) -> Self::Output {
self | rhs.bit()
}
}
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum EdSampleMode {
Max = 0,
Avg = 1,
}
#[allow(unused)]
#[derive(Debug, Clone, Copy)]
pub(crate) enum Command {
TxStart = 0x41,
RxStart = 0x42,
CcaTxStart = 0x43,
EdStart = 0x44,
Stop = 0x45,
DtmTxStart = 0x46,
DtmRxStart = 0x47,
DtmStop = 0x48,
Timer0Start = 0x4C,
Timer0Stop = 0x4D,
Timer1Start = 0x4E,
Timer1Stop = 0x4F,
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum MultipanIndex {
Multipan0 = 0,
Multipan1 = 1,
Multipan2 = 2,
Multipan3 = 3,
}
impl From<usize> for MultipanIndex {
fn from(value: usize) -> Self {
match value {
0 => MultipanIndex::Multipan0,
1 => MultipanIndex::Multipan1,
2 => MultipanIndex::Multipan2,
3 => MultipanIndex::Multipan3,
_ => panic!(),
}
}
}
#[inline(always)]
pub(crate) fn mac_date() -> u32 {
unsafe { &*IEEE802154::PTR }.mac_date().read().bits()
}
#[inline(always)]
pub(crate) fn set_rx_on_delay(delay: u16) {
unsafe { &*IEEE802154::PTR }
.rxon_delay()
.modify(|_, w| unsafe { w.rxon_delay().bits(delay) });
}
#[inline(always)]
pub(crate) fn enable_events(events: u16) {
unsafe { &*IEEE802154::PTR }
.event_en()
.modify(|r, w| unsafe { w.event_en().bits(r.event_en().bits() | events) });
}
#[inline(always)]
pub(crate) fn disable_events(events: u16) {
unsafe { &*IEEE802154::PTR }
.event_en()
.modify(|r, w| unsafe { w.event_en().bits(r.event_en().bits() & !events) });
}
#[inline(always)]
pub(crate) fn enable_tx_abort_events(events: u32) {
unsafe { &*IEEE802154::PTR }
.tx_abort_interrupt_control()
.modify(|r, w| unsafe {
w.tx_abort_interrupt_control()
.bits(r.tx_abort_interrupt_control().bits() | events)
});
}
#[inline(always)]
pub(crate) fn enable_rx_abort_events(events: u32) {
unsafe { &*IEEE802154::PTR }
.rx_abort_intr_ctrl()
.modify(|r, w| unsafe {
w.rx_abort_intr_ctrl()
.bits(r.rx_abort_intr_ctrl().bits() | events)
});
}
#[inline(always)]
pub(crate) fn set_ed_sample_mode(ed_sample_mode: EdSampleMode) {
unsafe { &*IEEE802154::PTR }
.ed_scan_cfg()
.modify(|_, w| unsafe { w.ed_sample_mode().bits(ed_sample_mode as u8) });
}
#[inline(always)]
pub(crate) fn set_tx_addr(addr: *const u8) {
unsafe { &*IEEE802154::PTR }
.txdma_addr()
.modify(|_, w| unsafe { w.txdma_addr().bits(addr as u32) });
}
#[inline(always)]
pub(crate) fn set_cmd(cmd: Command) {
unsafe { &*IEEE802154::PTR }
.command()
.modify(|_, w| unsafe { w.opcode().bits(cmd as u8) })
}
#[inline(always)]
pub(crate) fn set_freq(freq: u8) {
unsafe { &*IEEE802154::PTR }
.channel()
.modify(|_, w| unsafe { w.hop().bits(freq) });
}
#[inline(always)]
pub(crate) fn get_freq() -> u8 {
unsafe { &*IEEE802154::PTR }.channel().read().hop().bits()
}
#[inline(always)]
pub(crate) fn set_power(power: u8) {
unsafe { &*IEEE802154::PTR }
.tx_power()
.modify(|_, w| unsafe { w.tx_power().bits(power) });
}
#[inline(always)]
pub(crate) fn set_multipan_enable_mask(mask: u8) {
// apparently the REGS are garbage and the struct is right?
unsafe { &*IEEE802154::PTR }
.ctrl_cfg()
.modify(|r, w| unsafe { w.bits(r.bits() & !(0b1111 << 29) | (mask as u32) << 29) })
}
#[inline(always)]
pub(crate) fn set_multipan_panid(index: MultipanIndex, panid: u16) {
unsafe {
let pan_id = (&*IEEE802154::PTR)
.inf0_pan_id()
.as_ptr()
.offset(4 * index as isize);
pan_id.write_volatile(panid as u32);
}
}
#[inline(always)]
pub(crate) fn set_multipan_short_addr(index: MultipanIndex, value: u16) {
unsafe {
let short_addr = (&*IEEE802154::PTR)
.inf0_short_addr()
.as_ptr()
.offset(4 * index as isize);
short_addr.write_volatile(value as u32);
}
}
#[inline(always)]
pub(crate) fn set_multipan_ext_addr(index: MultipanIndex, ext_addr: *const u8) {
unsafe {
let mut ext_addr_ptr = (&*IEEE802154::PTR)
.inf0_extend_addr0()
.as_ptr()
.offset(4 * index as isize);
ext_addr_ptr.write_volatile(
(ext_addr.offset(0).read_volatile() as u32)
| ((ext_addr.offset(1).read_volatile() as u32) << 8)
| ((ext_addr.offset(2).read_volatile() as u32) << 16)
| ((ext_addr.offset(3).read_volatile() as u32) << 24),
);
ext_addr_ptr = ext_addr_ptr.offset(1);
ext_addr_ptr.write_volatile(
(ext_addr.offset(4).read_volatile() as u32)
| ((ext_addr.offset(5).read_volatile() as u32) << 8)
| ((ext_addr.offset(6).read_volatile() as u32) << 16)
| ((ext_addr.offset(7).read_volatile() as u32) << 24),
);
}
}
#[inline(always)]
pub(crate) fn set_cca_mode(cca_mode: CcaMode) {
unsafe { &*IEEE802154::PTR }
.ed_scan_cfg()
.modify(|_, w| unsafe { w.cca_mode().bits(cca_mode as u8) });
}
#[inline(always)]
pub(crate) fn set_cca_threshold(cca_threshold: i8) {
unsafe { &*IEEE802154::PTR }
.ed_scan_cfg()
.modify(|_, w| unsafe { w.cca_ed_threshold().bits(cca_threshold as u8) })
}
#[inline(always)]
pub(crate) fn set_tx_auto_ack(enable: bool) {
unsafe { &*IEEE802154::PTR }
.ctrl_cfg()
.modify(|_, w| w.hw_auto_ack_tx_en().bit(enable));
}
#[inline(always)]
pub(crate) fn get_tx_auto_ack() -> bool {
unsafe { &*IEEE802154::PTR }
.ctrl_cfg()
.read()
.hw_auto_ack_tx_en()
.bit_is_set()
}
#[inline(always)]
pub(crate) fn set_rx_auto_ack(enable: bool) {
unsafe { &*IEEE802154::PTR }
.ctrl_cfg()
.modify(|_, w| w.hw_auto_ack_rx_en().bit(enable));
}
#[inline(always)]
pub(crate) fn set_tx_enhance_ack(enable: bool) {
unsafe { &*IEEE802154::PTR }
.ctrl_cfg()
.modify(|_, w| w.hw_enhance_ack_tx_en().bit(enable));
}
#[inline(always)]
pub(crate) fn get_tx_enhance_ack() -> bool {
unsafe { &*IEEE802154::PTR }
.ctrl_cfg()
.read()
.hw_enhance_ack_tx_en()
.bit_is_set()
}
#[inline(always)]
pub(crate) fn set_coordinator(enable: bool) {
unsafe { &*IEEE802154::PTR }
.ctrl_cfg()
.modify(|_, w| w.pan_coordinator().bit(enable));
}
#[inline(always)]
pub(crate) fn set_promiscuous(enable: bool) {
unsafe { &*IEEE802154::PTR }
.ctrl_cfg()
.modify(|_, w| w.promiscuous_mode().bit(enable));
}
#[inline(always)]
pub(crate) fn set_pending_mode(enable: bool) {
unsafe { &*IEEE802154::PTR }
.ctrl_cfg()
.modify(|_, w| w.autopend_enhance().bit(enable));
}
#[inline(always)]
pub(crate) fn get_events() -> u16 {
unsafe { &*IEEE802154::PTR }.event_status().read().bits() as u16
}
#[inline(always)]
pub(crate) fn clear_events(events: u16) {
unsafe { &*IEEE802154::PTR }
.event_status()
.modify(|r, w| unsafe { w.event_status().bits(r.event_status().bits() & events) });
}
#[inline(always)]
pub(crate) fn set_transmit_security(enable: bool) {
unsafe { &*IEEE802154::PTR }
.sec_ctrl()
.modify(|_, w| w.sec_en().bit(enable));
}
#[inline(always)]
pub(crate) fn set_rx_addr(addr: *mut u8) {
unsafe { &*IEEE802154::PTR }
.rxdma_addr()
.modify(|_, w| unsafe { w.rxdma_addr().bits(addr as u32) });
}
#[inline(always)]
pub(crate) fn abort_tx() {
unsafe { &*IEEE802154::PTR }
.tx_status()
.modify(|_, w| unsafe { w.tx_abort_status().bits(0) });
}
#[inline(always)]
pub(crate) fn abort_rx() {
unsafe { &*IEEE802154::PTR }
.rx_status()
.modify(|_, w| unsafe { w.rx_abort_status().bits(0) });
}

349
esp-ieee802154/src/lib.rs Normal file
View File

@ -0,0 +1,349 @@
//! Low-level [IEEE 802.15.4] driver for the ESP32-C6 and ESP32-H2
//!
//! Implements the PHY/MAC layers of the IEEE 802.15.4 protocol stack, and
//! supports sending and receiving of raw frames.
//!
//! [IEEE 802.15.4]: https://en.wikipedia.org/wiki/IEEE_802.15.4
#![no_std]
#![feature(c_variadic)]
use core::{cell::RefCell, marker::PhantomData};
use byte::{BytesExt, TryRead};
use critical_section::Mutex;
use esp_hal::peripherals::{IEEE802154, RADIO_CLK};
use heapless::Vec;
use ieee802154::mac::{self, FooterMode, FrameSerDesContext};
use self::{
frame::FRAME_SIZE,
pib::{CONFIG_IEEE802154_CCA_THRESHOLD, IEEE802154_FRAME_EXT_ADDR_SIZE},
raw::*,
};
pub use self::{
frame::{Frame, ReceivedFrame},
pib::{CcaMode, PendingMode},
raw::RawReceived,
};
mod compat;
mod frame;
mod hal;
mod pib;
mod raw;
#[no_mangle]
extern "C" fn rtc_clk_xtal_freq_get() -> i32 {
0
}
/// IEEE 802.15.4 errors
#[derive(Debug, Clone, Copy)]
pub enum Error {
/// The requested data is bigger than available range, and/or the offset is
/// invalid
Incomplete,
/// The requested data content is invalid
BadInput,
}
impl From<byte::Error> for Error {
fn from(err: byte::Error) -> Self {
match err {
byte::Error::Incomplete | byte::Error::BadOffset(_) => Error::Incomplete,
byte::Error::BadInput { .. } => Error::BadInput,
}
}
}
/// IEEE 802.15.4 driver configuration
#[derive(Debug, Clone, Copy)]
pub struct Config {
pub auto_ack_tx: bool,
pub auto_ack_rx: bool,
pub enhance_ack_tx: bool,
pub promiscuous: bool,
pub coordinator: bool,
pub rx_when_idle: bool,
pub txpower: i8,
pub channel: u8,
pub cca_threshold: i8,
pub cca_mode: CcaMode,
pub pan_id: Option<u16>,
pub short_addr: Option<u16>,
pub ext_addr: Option<u64>,
}
impl Default for Config {
fn default() -> Self {
Self {
auto_ack_tx: Default::default(),
auto_ack_rx: Default::default(),
enhance_ack_tx: Default::default(),
promiscuous: Default::default(),
coordinator: Default::default(),
rx_when_idle: Default::default(),
txpower: 10,
channel: 15,
cca_threshold: CONFIG_IEEE802154_CCA_THRESHOLD,
cca_mode: CcaMode::Ed,
pan_id: None,
short_addr: None,
ext_addr: None,
}
}
}
/// IEEE 802.15.4 driver
#[derive(Debug)]
pub struct Ieee802154<'a> {
_align: u32,
transmit_buffer: [u8; FRAME_SIZE],
_phantom1: PhantomData<&'a ()>,
//_phantom2:PhantomData< &'b ()>,
}
impl<'a> Ieee802154<'a> {
/// Construct a new driver, enabling the IEEE 802.15.4 radio in the process
pub fn new(_radio: IEEE802154, radio_clocks: &mut RADIO_CLK) -> Self {
esp_ieee802154_enable(radio_clocks);
Self {
_align: 0,
transmit_buffer: [0u8; FRAME_SIZE],
_phantom1: PhantomData::default(),
//_phantom2: PhantomData::default(),
}
}
/// Set the configuration for the driver
pub fn set_config(&mut self, cfg: Config) {
set_auto_ack_tx(cfg.auto_ack_tx);
set_auto_ack_rx(cfg.auto_ack_rx);
set_enhance_ack_tx(cfg.enhance_ack_tx);
set_promiscuous(cfg.promiscuous);
set_coordinator(cfg.coordinator);
set_rx_when_idle(cfg.rx_when_idle);
set_tx_power(cfg.txpower);
set_channel(cfg.channel);
set_cca_theshold(cfg.cca_threshold);
set_cca_mode(cfg.cca_mode);
if let Some(pan_id) = cfg.pan_id {
set_panid(0, pan_id);
}
if let Some(short_addr) = cfg.short_addr {
set_short_address(0, short_addr);
}
if let Some(ext_addr) = cfg.ext_addr {
let mut address = [0u8; IEEE802154_FRAME_EXT_ADDR_SIZE];
address.copy_from_slice(&ext_addr.to_be_bytes()); // LE or BE?
set_extended_address(0, address);
}
}
/// Start receiving frames
pub fn start_receive(&mut self) {
ieee802154_receive();
}
/// Return the raw data of a received frame
pub fn get_raw_received(&mut self) -> Option<RawReceived> {
ieee802154_poll()
}
/// Get a received frame, if available
pub fn get_received(&mut self) -> Option<Result<ReceivedFrame, Error>> {
if let Some(raw) = ieee802154_poll() {
let maybe_decoded =
mac::Frame::try_read(&raw.data[1..][..raw.data[0] as usize], FooterMode::Explicit);
let result = match maybe_decoded {
Ok((decoded, _)) => {
let rssi = raw.data[raw.data[0] as usize - 1] as i8; // crc is not written to rx buffer
Ok(ReceivedFrame {
frame: Frame {
header: decoded.header,
content: decoded.content,
payload: Vec::from_slice(decoded.payload).unwrap(),
footer: decoded.footer,
},
channel: raw.channel,
rssi,
lqi: rssi_to_lqi(rssi),
})
}
Err(err) => Err(err.into()),
};
Some(result)
} else {
None
}
}
/// Transmit a frame
pub fn transmit(&mut self, frame: &Frame) -> Result<(), Error> {
let frm = mac::Frame {
header: frame.header,
content: frame.content,
payload: &frame.payload,
footer: frame.footer,
};
let mut offset = 1usize;
self.transmit_buffer
.write_with(
&mut offset,
frm,
&mut FrameSerDesContext::no_security(FooterMode::Explicit),
)
.unwrap();
self.transmit_buffer[0] = (offset - 1) as u8;
ieee802154_transmit(self.transmit_buffer.as_ptr() as *const u8, false); // what about CCA?
Ok(())
}
/// Transmit a raw frame
pub fn transmit_raw(&mut self, frame: &[u8]) -> Result<(), Error> {
self.transmit_buffer[1..][..frame.len()].copy_from_slice(frame);
self.transmit_buffer[0] = frame.len() as u8;
ieee802154_transmit(self.transmit_buffer.as_ptr() as *const u8, false); // what about CCA?
Ok(())
}
pub fn set_tx_done_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
critical_section::with(|cs| {
let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
tx_done_callback.replace(unsafe { core::mem::transmute(callback) });
});
}
pub fn clear_tx_done_callback(&mut self) {
critical_section::with(|cs| {
let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
tx_done_callback.take();
});
}
pub fn set_rx_available_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
critical_section::with(|cs| {
let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
rx_available_callback.replace(unsafe { core::mem::transmute(callback) });
});
}
pub fn clear_rx_available_callback(&mut self) {
critical_section::with(|cs| {
let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
rx_available_callback.take();
});
}
pub fn set_tx_done_callback_fn(&mut self, callback: fn()) {
critical_section::with(|cs| {
let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
tx_done_callback_fn.replace(callback);
});
}
pub fn clear_tx_done_callback_fn(&mut self) {
critical_section::with(|cs| {
let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
tx_done_callback_fn.take();
});
}
pub fn set_rx_available_callback_fn(&mut self, callback: fn()) {
critical_section::with(|cs| {
let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
rx_available_callback_fn.replace(unsafe { core::mem::transmute(callback) });
});
}
pub fn clear_rx_available_callback_fn(&mut self) {
critical_section::with(|cs| {
let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
rx_available_callback_fn.take();
});
}
}
impl<'a> Drop for Ieee802154<'a> {
fn drop(&mut self) {
self.clear_tx_done_callback();
self.clear_tx_done_callback_fn();
self.clear_rx_available_callback();
self.clear_rx_available_callback_fn();
}
}
pub fn rssi_to_lqi(rssi: i8) -> u8 {
if rssi < -80 {
0
} else if rssi > -30 {
0xff
} else {
let lqi_convert = ((rssi as u32).wrapping_add(80)) * 255;
(lqi_convert / 50) as u8
}
}
static TX_DONE_CALLBACK: Mutex<RefCell<Option<&'static mut (dyn FnMut() + Send)>>> =
Mutex::new(RefCell::new(None));
static RX_AVAILABLE_CALLBACK: Mutex<RefCell<Option<&'static mut (dyn FnMut() + Send)>>> =
Mutex::new(RefCell::new(None));
static TX_DONE_CALLBACK_FN: Mutex<RefCell<Option<fn()>>> = Mutex::new(RefCell::new(None));
static RX_AVAILABLE_CALLBACK_FN: Mutex<RefCell<Option<fn()>>> = Mutex::new(RefCell::new(None));
fn tx_done() {
log::trace!("tx_done callback");
critical_section::with(|cs| {
let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
let tx_done_callback = tx_done_callback.as_mut();
if let Some(tx_done_callback) = tx_done_callback {
tx_done_callback();
}
let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
let tx_done_callback_fn = tx_done_callback_fn.as_mut();
if let Some(tx_done_callback_fn) = tx_done_callback_fn {
tx_done_callback_fn();
}
});
}
fn rx_available() {
log::trace!("rx available callback");
critical_section::with(|cs| {
let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
let rx_available_callback = rx_available_callback.as_mut();
if let Some(rx_available_callback) = rx_available_callback {
rx_available_callback();
}
let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
let rx_available_callback_fn = rx_available_callback_fn.as_mut();
if let Some(rx_available_callback_fn) = rx_available_callback_fn {
rx_available_callback_fn();
}
});
}

241
esp-ieee802154/src/pib.rs Normal file
View File

@ -0,0 +1,241 @@
use core::cell::RefCell;
use critical_section::Mutex;
use crate::hal::{
set_cca_mode,
set_cca_threshold,
set_coordinator,
set_freq,
set_multipan_enable_mask,
set_multipan_ext_addr,
set_multipan_panid,
set_multipan_short_addr,
set_pending_mode,
set_power,
set_promiscuous,
set_rx_auto_ack,
set_tx_auto_ack,
set_tx_enhance_ack,
};
pub(crate) const CONFIG_IEEE802154_CCA_THRESHOLD: i8 = 1;
pub(crate) const IEEE802154_FRAME_EXT_ADDR_SIZE: usize = 8;
const IEEE802154_MULTIPAN_0: u8 = 0;
const IEEE802154_MULTIPAN_MAX: usize = 4;
static PIB: Mutex<RefCell<Option<Pib>>> = Mutex::new(RefCell::new(None));
/// Frame pending mode
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub enum PendingMode {
/// Frame pending bit always set to 1 in the ack to Data Request
#[default]
Disable = 0,
/// Frame pending bit set to 1 if src address matches, in the ack to Data
/// Request
Enable = 1,
/// Frame pending bit set to 1 if src address matches, in all ack frames
Enhanced = 2,
/// Frame pending bit set to 0 only if src address is short address and
/// matches in table, in the ack to Data Request
Zigbee = 3,
}
/// CCA mode
#[derive(Debug, Default, Clone, Copy)]
pub enum CcaMode {
/// Carrier only
#[default]
Carrier = 0x00,
/// Energy Detect only
Ed = 0x01,
/// Carrier or Energy Detect
CarrierOrEd = 0x02,
/// Carrier and Energy Detect
CarrierAndEd = 0x03,
}
#[derive(Debug, Default, Clone, Copy)]
struct Pib {
auto_ack_tx: bool,
auto_ack_rx: bool,
enhance_ack_tx: bool,
promiscuous: bool,
coordinator: bool,
rx_when_idle: bool,
txpower: i8,
channel: u8,
pending_mode: PendingMode,
multipan_mask: u8,
panid: [u16; IEEE802154_MULTIPAN_MAX],
short_addr: [u16; IEEE802154_MULTIPAN_MAX],
ext_addr: [[u8; IEEE802154_FRAME_EXT_ADDR_SIZE]; IEEE802154_MULTIPAN_MAX],
cca_threshold: i8,
cca_mode: CcaMode,
}
pub(crate) fn ieee802154_pib_init() {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).replace(Pib {
auto_ack_tx: true,
auto_ack_rx: true,
enhance_ack_tx: true,
coordinator: false,
promiscuous: true,
rx_when_idle: false,
txpower: 10,
channel: 11,
pending_mode: PendingMode::Disable,
multipan_mask: 1 << IEEE802154_MULTIPAN_0,
panid: [0u16; 4],
short_addr: [0u16; IEEE802154_MULTIPAN_MAX],
ext_addr: [[0xffu8; IEEE802154_FRAME_EXT_ADDR_SIZE]; IEEE802154_MULTIPAN_MAX],
cca_threshold: CONFIG_IEEE802154_CCA_THRESHOLD,
cca_mode: CcaMode::Ed,
});
});
}
pub(crate) fn ieee802154_pib_set_panid(index: u8, panid: u16) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().panid[index as usize] = panid;
});
}
pub(crate) fn ieee802154_pib_set_promiscuous(enable: bool) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().promiscuous = enable;
});
}
pub(crate) fn ieee802154_pib_set_auto_ack_tx(enable: bool) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().auto_ack_tx = enable;
});
}
pub(crate) fn ieee802154_pib_set_auto_ack_rx(enable: bool) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().auto_ack_rx = enable;
});
}
pub(crate) fn ieee802154_pib_set_enhance_ack_tx(enable: bool) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().enhance_ack_tx = enable;
});
}
pub(crate) fn ieee802154_pib_set_coordinator(enable: bool) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().coordinator = enable;
});
}
pub(crate) fn ieee802154_pib_set_rx_when_idle(enable: bool) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().rx_when_idle = enable;
});
}
pub(crate) fn ieee802154_pib_get_rx_when_idle() -> bool {
critical_section::with(|cs| PIB.borrow_ref_mut(cs).as_mut().unwrap().rx_when_idle)
}
pub(crate) fn ieee802154_pib_set_tx_power(power: i8) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().txpower = power;
});
}
pub(crate) fn ieee802154_pib_set_channel(channel: u8) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().channel = channel;
});
}
pub(crate) fn ieee802154_pib_set_pending_mode(mode: PendingMode) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().pending_mode = mode;
});
}
pub(crate) fn ieee802154_pib_set_short_address(index: u8, address: u16) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().short_addr[index as usize] = address;
});
}
pub(crate) fn ieee802154_pib_set_extended_address(
index: u8,
address: [u8; IEEE802154_FRAME_EXT_ADDR_SIZE],
) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().ext_addr[index as usize] = address;
});
}
pub(crate) fn ieee802154_pib_set_cca_theshold(cca_threshold: i8) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().cca_threshold = cca_threshold;
});
}
pub(crate) fn ieee802154_pib_set_cca_mode(mode: CcaMode) {
critical_section::with(|cs| {
PIB.borrow_ref_mut(cs).as_mut().unwrap().cca_mode = mode;
});
}
pub(crate) fn ieee802154_pib_update() {
critical_section::with(|cs| {
let mut pib = PIB.borrow_ref_mut(cs);
let pib = pib.as_mut().unwrap();
set_freq(channel_to_freq(pib.channel));
set_power(ieee802154_txpower_convert(pib.txpower));
set_multipan_enable_mask(pib.multipan_mask);
ieee802154_set_multipan_hal(pib);
set_cca_mode(pib.cca_mode);
set_cca_threshold(pib.cca_threshold);
set_tx_auto_ack(pib.auto_ack_tx);
set_rx_auto_ack(pib.auto_ack_rx);
set_tx_enhance_ack(pib.enhance_ack_tx);
set_coordinator(pib.coordinator);
set_promiscuous(pib.promiscuous);
set_pending_mode(pib.pending_mode == PendingMode::Enhanced);
});
}
fn channel_to_freq(channel: u8) -> u8 {
(channel - 11) * 5 + 3
}
fn ieee802154_set_multipan_hal(pib: &Pib) {
for index in 0..IEEE802154_MULTIPAN_MAX {
if (pib.multipan_mask & (1 << index)) != 0 {
set_multipan_panid(index.into(), pib.panid[index]);
set_multipan_short_addr(index.into(), pib.short_addr[index]);
set_multipan_ext_addr(index.into(), pib.ext_addr[index].as_ptr() as *const u8);
}
}
}
fn ieee802154_txpower_convert(txpower: i8) -> u8 {
const IEEE802154_TXPOWER_VALUE_MAX: i8 = 13;
const IEEE802154_TXPOWER_VALUE_MIN: i8 = -32;
if txpower > IEEE802154_TXPOWER_VALUE_MAX {
15
} else if txpower < IEEE802154_TXPOWER_VALUE_MIN {
0
} else {
((txpower - IEEE802154_TXPOWER_VALUE_MIN) / 3) as u8
}
}

439
esp-ieee802154/src/raw.rs Normal file
View File

@ -0,0 +1,439 @@
use core::cell::RefCell;
use critical_section::Mutex;
use esp_hal::{
interrupt::Priority,
peripherals::RADIO_CLK,
prelude::handler,
system::{RadioClockController, RadioPeripherals},
};
use esp_wifi_sys::include::{
esp_phy_calibration_data_t,
esp_phy_calibration_mode_t_PHY_RF_CAL_FULL,
ieee802154_coex_event_t,
ieee802154_coex_event_t_IEEE802154_IDLE,
ieee802154_coex_event_t_IEEE802154_LOW,
ieee802154_coex_event_t_IEEE802154_MIDDLE,
register_chipv7_phy,
};
use heapless::spsc::Queue;
use crate::{
frame::{frame_get_version, frame_is_ack_required, FRAME_VERSION_1, FRAME_VERSION_2},
hal::*,
pib::*,
};
pub(crate) const FRAME_SIZE: usize = 129;
const PHY_ENABLE_VERSION_PRINT: u32 = 1;
static mut RX_BUFFER: [u8; FRAME_SIZE] = [0u8; FRAME_SIZE];
static RX_QUEUE: Mutex<RefCell<Queue<RawReceived, 20>>> = Mutex::new(RefCell::new(Queue::new()));
static STATE: Mutex<RefCell<Ieee802154State>> = Mutex::new(RefCell::new(Ieee802154State::Idle));
extern "C" {
fn bt_bb_v2_init_cmplx(print_version: u32); // from libbtbb.a
fn bt_bb_set_zb_tx_on_delay(time: u16); // from libbtbb.a
fn esp_coex_ieee802154_ack_pti_set(event: ieee802154_coex_event_t); // from ???
fn esp_coex_ieee802154_txrx_pti_set(event: ieee802154_coex_event_t); // from ???
fn phy_version_print(); // from libphy.a
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum Ieee802154State {
Idle,
Receive,
Transmit,
TxAck,
}
#[allow(unused)]
#[derive(Debug, Clone, Copy, PartialEq)]
enum Ieee802154TxRxScene {
Idle,
Tx,
Rx,
TxAt,
RxAt,
}
/// A raw payload received on some channel
#[derive(Debug)]
pub struct RawReceived {
/// Payload
pub data: [u8; FRAME_SIZE],
/// Receiver channel
pub channel: u8,
}
pub(crate) fn esp_ieee802154_enable(radio_clock_control: &mut RADIO_CLK) {
radio_clock_control.init_clocks();
radio_clock_control.enable(RadioPeripherals::Phy);
radio_clock_control.enable(RadioPeripherals::Ieee802154);
esp_phy_enable();
esp_btbb_enable();
ieee802154_mac_init();
unsafe { phy_version_print() }; // libphy.a
log::info!("date={:x}", mac_date());
}
fn esp_phy_enable() {
unsafe {
let mut calibration_data = esp_phy_calibration_data_t {
version: [0u8; 4],
mac: [0u8; 6],
opaque: [0u8; 1894],
};
register_chipv7_phy(
core::ptr::null(),
&mut calibration_data as *mut esp_phy_calibration_data_t,
esp_phy_calibration_mode_t_PHY_RF_CAL_FULL,
);
}
}
fn esp_btbb_enable() {
unsafe { bt_bb_v2_init_cmplx(PHY_ENABLE_VERSION_PRINT) };
}
fn ieee802154_mac_init() {
#[cfg(feature = "esp32c6")]
unsafe {
extern "C" {
static mut coex_pti_tab_ptr: u32;
static coex_pti_tab: u8;
}
// Manually set `coex_pti_tab_ptr` pointing to `coex_pti_tab`
core::ptr::addr_of_mut!(coex_pti_tab_ptr).write_volatile(&coex_pti_tab as *const _ as u32);
}
ieee802154_pib_init();
enable_events(Event::mask());
disable_events(Event::Timer0Overflow | Event::Timer1Overflow);
enable_tx_abort_events(
TxAbortReason::RxAckTimeout
| TxAbortReason::TxCoexBreak
| TxAbortReason::TxSecurityError
| TxAbortReason::CcaFailed
| TxAbortReason::CcaBusy
| TxAbortReason::TxStop,
);
enable_rx_abort_events(
RxAbortReason::TxAckTimeout | RxAbortReason::TxAckCoexBreak | RxAbortReason::RxStop,
);
set_ed_sample_mode(EdSampleMode::Avg);
unsafe { esp_coex_ieee802154_ack_pti_set(ieee802154_coex_event_t_IEEE802154_MIDDLE) };
ieee802154_set_txrx_pti(Ieee802154TxRxScene::Idle);
unsafe {
bt_bb_set_zb_tx_on_delay(50); // set tx on delay for libbtbb.a
}
set_rx_on_delay(50);
// memset(s_rx_frame, 0, sizeof(s_rx_frame));
// s_ieee802154_state = IEEE802154_STATE_IDLE;
unsafe {
esp_hal::interrupt::bind_interrupt(
esp_hal::peripherals::Interrupt::ZB_MAC,
ZB_MAC.handler(),
);
}
esp_hal::interrupt::enable(esp_hal::peripherals::Interrupt::ZB_MAC, ZB_MAC.priority()).unwrap();
}
fn ieee802154_set_txrx_pti(txrx_scene: Ieee802154TxRxScene) {
match txrx_scene {
Ieee802154TxRxScene::Idle => {
unsafe { esp_coex_ieee802154_txrx_pti_set(ieee802154_coex_event_t_IEEE802154_IDLE) };
}
Ieee802154TxRxScene::Tx | Ieee802154TxRxScene::Rx => {
unsafe { esp_coex_ieee802154_txrx_pti_set(ieee802154_coex_event_t_IEEE802154_LOW) };
}
Ieee802154TxRxScene::TxAt | Ieee802154TxRxScene::RxAt => {
unsafe { esp_coex_ieee802154_txrx_pti_set(ieee802154_coex_event_t_IEEE802154_MIDDLE) };
}
}
}
pub fn tx_init(frame: *const u8) {
let tx_frame = frame;
stop_current_operation();
ieee802154_pib_update();
ieee802154_sec_update();
set_tx_addr(tx_frame);
if true
// ieee802154_frame_is_ack_required(frame)
{
// set rx pointer for ack frame
set_next_rx_buffer();
}
}
pub fn ieee802154_transmit(frame: *const u8, cca: bool) -> i32 {
critical_section::with(|cs| {
tx_init(frame);
ieee802154_set_txrx_pti(Ieee802154TxRxScene::Tx);
if cca {
// disable_events(IEEE802154_EVENT_ED_DONE);
// set_cmd(IEEE802154_CMD_CCA_TX_START);
// ieee802154_state = IEEE802154_STATE_TX_CCA;
} else {
set_cmd(Command::TxStart);
// if (ieee802154_frame_get_type(frame) == IEEE802154_FRAME_TYPE_ACK
// && ieee802154_frame_get_version(frame) == IEEE802154_FRAME_VERSION_2)
// {
// ieee802154_state = IEEE802154_STATE_TX_ENH_ACK;
// } else {
*STATE.borrow_ref_mut(cs) = Ieee802154State::Transmit;
// }
}
});
0 // ESP_OK
}
pub fn ieee802154_receive() -> i32 {
critical_section::with(|cs| {
if *STATE.borrow_ref(cs) == Ieee802154State::Receive {
return;
}
rx_init();
enable_rx();
*STATE.borrow_ref_mut(cs) = Ieee802154State::Receive;
});
0 // ESP-OK
}
pub fn ieee802154_poll() -> Option<RawReceived> {
critical_section::with(|cs| {
let mut queue = RX_QUEUE.borrow_ref_mut(cs);
queue.dequeue()
})
}
fn rx_init() {
stop_current_operation();
ieee802154_pib_update();
}
fn enable_rx() {
set_next_rx_buffer();
ieee802154_set_txrx_pti(Ieee802154TxRxScene::Rx);
set_cmd(Command::RxStart);
// ieee802154_state = IEEE802154_STATE_RX;
}
fn stop_current_operation() {
let events = get_events();
set_cmd(Command::Stop);
clear_events(events);
}
fn set_next_rx_buffer() {
unsafe {
set_rx_addr(RX_BUFFER.as_mut_ptr() as *mut u8);
}
}
pub fn set_promiscuous(enable: bool) {
ieee802154_pib_set_promiscuous(enable);
}
pub fn set_auto_ack_tx(enable: bool) {
ieee802154_pib_set_auto_ack_tx(enable);
}
pub fn set_auto_ack_rx(enable: bool) {
ieee802154_pib_set_auto_ack_rx(enable);
}
pub fn set_enhance_ack_tx(enable: bool) {
ieee802154_pib_set_enhance_ack_tx(enable);
}
pub fn set_coordinator(enable: bool) {
ieee802154_pib_set_coordinator(enable);
}
pub fn set_rx_when_idle(enable: bool) {
ieee802154_pib_set_rx_when_idle(enable);
}
pub fn set_tx_power(power: i8) {
ieee802154_pib_set_tx_power(power);
}
pub fn set_channel(channel: u8) {
ieee802154_pib_set_channel(channel);
}
#[allow(unused)]
pub fn set_pending_mode(mode: PendingMode) {
ieee802154_pib_set_pending_mode(mode);
}
#[allow(unused)]
pub fn set_multipan_enable(mask: u8) {
set_multipan_enable_mask(mask);
}
pub fn set_short_address(index: u8, address: u16) {
ieee802154_pib_set_short_address(index, address);
}
pub fn set_extended_address(index: u8, address: [u8; IEEE802154_FRAME_EXT_ADDR_SIZE]) {
ieee802154_pib_set_extended_address(index, address);
}
pub fn set_cca_theshold(cca_threshold: i8) {
ieee802154_pib_set_cca_theshold(cca_threshold);
}
pub fn set_cca_mode(mode: CcaMode) {
ieee802154_pib_set_cca_mode(mode);
}
pub fn set_panid(index: u8, id: u16) {
ieee802154_pib_set_panid(index, id);
}
#[inline(always)]
fn ieee802154_sec_update() {
let is_security = false;
set_transmit_security(is_security);
// ieee802154_sec_clr_transmit_security();
}
fn next_operation() {
let previous_operation = critical_section::with(|cs| {
let state = STATE.borrow_ref(cs).clone();
if ieee802154_pib_get_rx_when_idle() {
enable_rx();
*STATE.borrow_ref_mut(cs) = Ieee802154State::Receive;
} else {
*STATE.borrow_ref_mut(cs) = Ieee802154State::Idle;
}
state
});
match previous_operation {
Ieee802154State::Receive => crate::rx_available(),
Ieee802154State::Transmit => crate::tx_done(),
Ieee802154State::TxAck => crate::tx_done(),
_ => (),
}
}
#[handler(priority = "Priority::Priority1")]
fn ZB_MAC() {
log::trace!("ZB_MAC interrupt");
let events = get_events();
clear_events(events);
log::trace!("events = {:032b}", events);
if events & Event::RxSfdDone != 0 {
// IEEE802154_STATE_TX && IEEE802154_STATE_TX_CCA && IEEE802154_STATE_TX_ENH_ACK
// for isr processing delay
log::trace!("rx sfd done");
}
if events & Event::TxSfdDone != 0 {
// IEEE802154_STATE_RX for isr processing delay, only 821
// IEEE802154_STATE_TX_ACK for workaround jira ZB-81.
log::trace!("tx sfd done");
}
if events & Event::TxDone != 0 {
log::trace!("tx done");
next_operation();
}
if events & Event::RxDone != 0 {
log::trace!("rx done");
unsafe {
log::trace!("Received raw {:x?}", RX_BUFFER);
critical_section::with(|cs| {
let mut queue = RX_QUEUE.borrow_ref_mut(cs);
if !queue.is_full() {
let item = RawReceived {
data: RX_BUFFER,
channel: freq_to_channel(get_freq()),
};
queue.enqueue(item).ok();
} else {
log::warn!("Receive queue full");
}
let frm = &RX_BUFFER[1..][..RX_BUFFER[0] as usize];
if will_auto_send_ack(frm) {
*STATE.borrow_ref_mut(cs) = Ieee802154State::TxAck;
} else if should_send_enhanced_ack(frm) {
// TODO
} else {
// esp_ieee802154_coex_pti_set(IEEE802154_IDLE_RX);
next_operation();
}
});
}
}
if events & Event::AckRxDone != 0 {
log::info!("EventAckRxDone");
}
if events & Event::AckTxDone != 0 {
log::trace!("EventAckTxDone");
next_operation();
}
if events & Event::TxAbort != 0 {
log::trace!("TxAbort");
abort_tx();
}
if events & Event::RxAbort != 0 {
log::trace!("RxAbort");
abort_rx();
}
}
fn freq_to_channel(freq: u8) -> u8 {
(freq - 3) / 5 + 11
}
fn will_auto_send_ack(frame: &[u8]) -> bool {
frame_is_ack_required(frame) && frame_get_version(frame) <= FRAME_VERSION_1 && get_tx_auto_ack()
}
fn should_send_enhanced_ack(frame: &[u8]) -> bool {
frame_is_ack_required(frame)
&& frame_get_version(frame) <= FRAME_VERSION_2
&& get_tx_enhance_ack()
}

View File

@ -11,30 +11,32 @@ cfg-if = "1.0.0"
critical-section = "1.1.2" critical-section = "1.1.2"
crypto-bigint = { version = "0.5.5", default-features = false } crypto-bigint = { version = "0.5.5", default-features = false }
elliptic-curve = { version = "0.13.8", default-features = false, features = ["sec1"] } elliptic-curve = { version = "0.13.8", default-features = false, features = ["sec1"] }
# some examples use a huge amount of stack
embassy-executor = { version = "0.5.0", features = ["task-arena-size-40960"] } embassy-executor = { version = "0.5.0", features = ["task-arena-size-40960"] }
embassy-sync = "0.5.0"
embassy-futures = "0.1.1" embassy-futures = "0.1.1"
embassy-sync = "0.5.0"
embassy-time = "0.3.0" embassy-time = "0.3.0"
embassy-time-driver = { version = "0.1.0", optional = true } embassy-time-driver = { version = "0.1.0", optional = true }
embassy-usb = { version = "0.1.0", default-features = false, optional = true } embassy-usb = { version = "0.1.0", default-features = false, optional = true }
embedded-can = "0.4.1"
embedded-graphics = "0.8.1" embedded-graphics = "0.8.1"
embedded-hal = "1.0.0" embedded-hal = "1.0.0"
embedded-hal-02 = { version = "0.2.7", package = "embedded-hal", features = ["unproven"] } embedded-hal-02 = { version = "0.2.7", package = "embedded-hal", features = ["unproven"] }
embedded-hal-async = "1.0.0" embedded-hal-async = "1.0.0"
embedded-hal-bus = "0.1.0" embedded-hal-bus = "0.1.0"
embedded-io-async = "0.6.1" embedded-io-async = "0.6.1"
embedded-can = "0.4.1"
esp-alloc = { version = "0.3.0", path = "../esp-alloc" } esp-alloc = { version = "0.3.0", path = "../esp-alloc" }
esp-backtrace = { version = "0.11.1", features = ["exception-handler", "panic-handler", "println"] } esp-backtrace = { version = "0.11.1", features = ["exception-handler", "panic-handler", "println"] }
esp-hal = { version = "0.17.0", path = "../esp-hal", features = ["log"] } esp-hal = { version = "0.17.0", path = "../esp-hal", features = ["log"] }
esp-hal-smartled = { version = "0.10.0", path = "../esp-hal-smartled", optional = true } esp-hal-smartled = { version = "0.10.0", path = "../esp-hal-smartled", optional = true }
esp-ieee802154 = { version = "0.1.0", path = "../esp-ieee802154", optional = true }
esp-println = { version = "0.9.1", features = ["log"] } esp-println = { version = "0.9.1", features = ["log"] }
fugit = "0.3.7" fugit = "0.3.7"
heapless = "0.8.0" heapless = "0.8.0"
hex-literal = "0.4.1" hex-literal = "0.4.1"
hmac = { version = "0.12.1", default-features = false } hmac = { version = "0.12.1", default-features = false }
ieee802154 = "0.6.1"
lis3dh-async = "0.9.2" lis3dh-async = "0.9.2"
log = "0.4.21"
nb = "1.1.0" nb = "1.1.0"
p192 = { version = "0.13.0", default-features = false, features = ["arithmetic"] } p192 = { version = "0.13.0", default-features = false, features = ["arithmetic"] }
p256 = { version = "0.13.2", default-features = false, features = ["arithmetic"] } p256 = { version = "0.13.2", default-features = false, features = ["arithmetic"] }
@ -45,14 +47,13 @@ ssd1306 = "0.8.4"
static_cell = { version = "2.0.0", features = ["nightly"] } static_cell = { version = "2.0.0", features = ["nightly"] }
usb-device = "0.3.2" usb-device = "0.3.2"
usbd-serial = "0.2.1" usbd-serial = "0.2.1"
log = "0.4"
[features] [features]
esp32 = ["esp-hal/esp32", "esp-backtrace/esp32", "esp-println/esp32", "esp-hal-smartled/esp32"] esp32 = ["esp-hal/esp32", "esp-backtrace/esp32", "esp-println/esp32", "esp-hal-smartled/esp32"]
esp32c2 = ["esp-hal/esp32c2", "esp-backtrace/esp32c2", "esp-println/esp32c2"] esp32c2 = ["esp-hal/esp32c2", "esp-backtrace/esp32c2", "esp-println/esp32c2"]
esp32c3 = ["esp-hal/esp32c3", "esp-backtrace/esp32c3", "esp-println/esp32c3", "esp-hal-smartled/esp32c3"] esp32c3 = ["esp-hal/esp32c3", "esp-backtrace/esp32c3", "esp-println/esp32c3", "esp-hal-smartled/esp32c3"]
esp32c6 = ["esp-hal/esp32c6", "esp-backtrace/esp32c6", "esp-println/esp32c6", "esp-hal-smartled/esp32c6"] esp32c6 = ["esp-hal/esp32c6", "esp-backtrace/esp32c6", "esp-println/esp32c6", "esp-hal-smartled/esp32c6", "esp-ieee802154/esp32c6"]
esp32h2 = ["esp-hal/esp32h2", "esp-backtrace/esp32h2", "esp-println/esp32h2", "esp-hal-smartled/esp32h2"] esp32h2 = ["esp-hal/esp32h2", "esp-backtrace/esp32h2", "esp-println/esp32h2", "esp-hal-smartled/esp32h2", "esp-ieee802154/esp32h2"]
esp32s2 = ["esp-hal/esp32s2", "esp-backtrace/esp32s2", "esp-println/esp32s2", "esp-hal-smartled/esp32s2"] esp32s2 = ["esp-hal/esp32s2", "esp-backtrace/esp32s2", "esp-println/esp32s2", "esp-hal-smartled/esp32s2"]
esp32s3 = ["esp-hal/esp32s3", "esp-backtrace/esp32s3", "esp-println/esp32s3", "esp-hal-smartled/esp32s3"] esp32s3 = ["esp-hal/esp32s3", "esp-backtrace/esp32s3", "esp-println/esp32s3", "esp-hal-smartled/esp32s3"]

7
examples/build.rs Normal file
View File

@ -0,0 +1,7 @@
fn main() {
if cfg!(feature = "esp32c6") || cfg!(feature = "esp32h2") {
println!("cargo::rustc-link-arg=-Trom_coexist.x");
println!("cargo::rustc-link-arg=-Trom_functions.x");
println!("cargo::rustc-link-arg=-Trom_phy.x");
}
}

View File

@ -0,0 +1,33 @@
//% CHIPS: esp32c6 esp32h2
#![no_std]
#![no_main]
use esp_backtrace as _;
use esp_hal::{peripherals::Peripherals, prelude::*};
use esp_ieee802154::*;
use esp_println::println;
#[entry]
fn main() -> ! {
let mut peripherals = Peripherals::take();
let mut ieee802154 = Ieee802154::new(peripherals.IEEE802154, &mut peripherals.RADIO_CLK);
ieee802154.set_config(Config {
channel: 15,
promiscuous: true,
rx_when_idle: true,
auto_ack_rx: false,
auto_ack_tx: false,
..Config::default()
});
println!("Start receiving:");
ieee802154.start_receive();
loop {
if let Some(frame) = ieee802154.get_received() {
println!("Received {:?}\n", &frame);
}
}
}

View File

@ -0,0 +1,35 @@
//% CHIPS: esp32c6 esp32h2
#![no_std]
#![no_main]
use esp_backtrace as _;
use esp_hal::{peripherals::Peripherals, prelude::*};
use esp_ieee802154::*;
use esp_println::println;
#[entry]
fn main() -> ! {
let mut peripherals = Peripherals::take();
let mut ieee802154 = Ieee802154::new(peripherals.IEEE802154, &mut peripherals.RADIO_CLK);
ieee802154.set_config(Config {
channel: 15,
promiscuous: false,
rx_when_idle: true,
auto_ack_rx: true,
auto_ack_tx: true,
pan_id: Some(0x4242),
short_addr: Some(0x2323),
..Config::default()
});
println!("Start receiving:");
ieee802154.start_receive();
loop {
if let Some(frame) = ieee802154.get_received() {
println!("Received {:?}\n", &frame);
}
}
}

View File

@ -0,0 +1,71 @@
//% CHIPS: esp32c6 esp32h2
#![no_std]
#![no_main]
use esp_backtrace as _;
use esp_hal::{
clock::ClockControl,
delay::Delay,
peripherals::Peripherals,
prelude::*,
system::SystemControl,
};
use esp_ieee802154::*;
use esp_println::println;
use ieee802154::mac::{
Address,
FrameContent,
FrameType,
FrameVersion,
Header,
PanId,
ShortAddress,
};
#[entry]
fn main() -> ! {
let mut peripherals = Peripherals::take();
let system = SystemControl::new(peripherals.SYSTEM);
let clocks = ClockControl::max(system.clock_control).freeze();
let delay = Delay::new(&clocks);
let mut ieee802154 = Ieee802154::new(peripherals.IEEE802154, &mut peripherals.RADIO_CLK);
ieee802154.set_config(Config {
channel: 15,
promiscuous: false,
pan_id: Some(0x4242),
short_addr: Some(0x2323),
..Config::default()
});
let mut seq_number = 0u8;
loop {
ieee802154
.transmit(&Frame {
header: Header {
frame_type: FrameType::Data,
frame_pending: false,
ack_request: false,
pan_id_compress: false,
seq_no_suppress: false,
ie_present: false,
version: FrameVersion::Ieee802154_2003,
seq: seq_number,
destination: Some(Address::Short(PanId(0xffff), ShortAddress(0xffff))),
source: None,
auxiliary_security_header: None,
},
content: FrameContent::Data,
payload: heapless::Vec::from_slice(b"Hello World").unwrap(),
footer: [0u8; 2],
})
.ok();
println!("Send frame with sequence number {seq_number}");
delay.delay_millis(1000u32);
seq_number = seq_number.wrapping_add(1);
}
}

View File

@ -0,0 +1,71 @@
//% CHIPS: esp32c6 esp32h2
#![no_std]
#![no_main]
use esp_backtrace as _;
use esp_hal::{
clock::ClockControl,
delay::Delay,
peripherals::Peripherals,
prelude::*,
system::SystemControl,
};
use esp_ieee802154::*;
use esp_println::println;
use ieee802154::mac::{
Address,
FrameContent,
FrameType,
FrameVersion,
Header,
PanId,
ShortAddress,
};
#[entry]
fn main() -> ! {
let mut peripherals = Peripherals::take();
let system = SystemControl::new(peripherals.SYSTEM);
let clocks = ClockControl::max(system.clock_control).freeze();
let delay = Delay::new(&clocks);
let mut ieee802154 = Ieee802154::new(peripherals.IEEE802154, &mut peripherals.RADIO_CLK);
ieee802154.set_config(Config {
channel: 15,
promiscuous: false,
pan_id: Some(0x4242),
short_addr: Some(0x2222),
..Config::default()
});
let mut seq_number = 0u8;
loop {
ieee802154
.transmit(&Frame {
header: Header {
frame_type: FrameType::Data,
frame_pending: false,
ack_request: true,
pan_id_compress: false,
seq_no_suppress: false,
ie_present: false,
version: FrameVersion::Ieee802154_2003,
seq: seq_number,
destination: Some(Address::Short(PanId(0x4242), ShortAddress(0x2323))),
source: None,
auxiliary_security_header: None,
},
content: FrameContent::Data,
payload: heapless::Vec::from_slice(b"Hello World").unwrap(),
footer: [0u8; 2],
})
.ok();
println!("Send frame with sequence number {seq_number}");
delay.delay_millis(1000u32);
seq_number = seq_number.wrapping_add(1);
}
}