Merge branch 'main' into hci-attempt-3
This commit is contained in:
commit
08ce3a7aba
4
.github/bors.toml
vendored
4
.github/bors.toml
vendored
@ -1,4 +0,0 @@
|
|||||||
status = [
|
|
||||||
"all",
|
|
||||||
]
|
|
||||||
delete_merged_branches = true
|
|
1
.github/ci/doc.sh
vendored
1
.github/ci/doc.sh
vendored
@ -39,6 +39,7 @@ docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/g
|
|||||||
docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup
|
docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup
|
||||||
docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
|
docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
|
||||||
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
|
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
|
||||||
|
docserver-builder -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup
|
||||||
|
|
||||||
export KUBECONFIG=/ci/secrets/kubeconfig.yml
|
export KUBECONFIG=/ci/secrets/kubeconfig.yml
|
||||||
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
||||||
|
2
.github/ci/test.sh
vendored
2
.github/ci/test.sh
vendored
@ -28,3 +28,5 @@ cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --featu
|
|||||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti
|
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti
|
||||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti
|
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti
|
||||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f769ni,exti,time-driver-any,exti
|
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f769ni,exti,time-driver-any,exti
|
||||||
|
|
||||||
|
cargo test --manifest-path ./embassy-net-adin1110/Cargo.toml
|
||||||
|
13
ci.sh
13
ci.sh
@ -19,6 +19,19 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread,integrated-timers \
|
||||||
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
||||||
|
@ -11,7 +11,7 @@ log = ["dep:log"]
|
|||||||
firmware-logs = []
|
firmware-logs = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time"}
|
embassy-time = { version = "0.1.3", path = "../embassy-time"}
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
||||||
|
@ -148,7 +148,7 @@ where
|
|||||||
cmd_buf[0] = cmd;
|
cmd_buf[0] = cmd;
|
||||||
cmd_buf[1..][..buf.len()].copy_from_slice(buf);
|
cmd_buf[1..][..buf.len()].copy_from_slice(buf);
|
||||||
|
|
||||||
self.status = self.spi.cmd_write(&cmd_buf).await;
|
self.status = self.spi.cmd_write(&cmd_buf[..buf.len() + 1]).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
@ -117,6 +117,7 @@ pub(crate) const IOCTL_CMD_UP: u32 = 2;
|
|||||||
pub(crate) const IOCTL_CMD_DOWN: u32 = 3;
|
pub(crate) const IOCTL_CMD_DOWN: u32 = 3;
|
||||||
pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26;
|
pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26;
|
||||||
pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30;
|
pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30;
|
||||||
|
pub(crate) const IOCTL_CMD_DISASSOC: u32 = 52;
|
||||||
pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64;
|
pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64;
|
||||||
pub(crate) const IOCTL_CMD_SET_AP: u32 = 118;
|
pub(crate) const IOCTL_CMD_SET_AP: u32 = 118;
|
||||||
pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263;
|
pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263;
|
||||||
|
@ -126,7 +126,7 @@ impl<'a> Control<'a> {
|
|||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after(Duration::from_millis(100)).await;
|
||||||
|
|
||||||
// set wifi up
|
// set wifi up
|
||||||
self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
|
self.up().await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after(Duration::from_millis(100)).await;
|
||||||
|
|
||||||
@ -145,6 +145,16 @@ impl<'a> Control<'a> {
|
|||||||
debug!("cyw43 control init done");
|
debug!("cyw43 control init done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the WiFi interface up.
|
||||||
|
async fn up(&mut self) {
|
||||||
|
self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the interface down.
|
||||||
|
async fn down(&mut self) {
|
||||||
|
self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn set_power_management(&mut self, mode: PowerManagementMode) {
|
pub async fn set_power_management(&mut self, mode: PowerManagementMode) {
|
||||||
// power save mode
|
// power save mode
|
||||||
let mode_num = mode.mode();
|
let mode_num = mode.mode();
|
||||||
@ -263,13 +273,13 @@ impl<'a> Control<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Temporarily set wifi down
|
// Temporarily set wifi down
|
||||||
self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await;
|
self.down().await;
|
||||||
|
|
||||||
// Turn off APSTA mode
|
// Turn off APSTA mode
|
||||||
self.set_iovar_u32("apsta", 0).await;
|
self.set_iovar_u32("apsta", 0).await;
|
||||||
|
|
||||||
// Set wifi up again
|
// Set wifi up again
|
||||||
self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
|
self.up().await;
|
||||||
|
|
||||||
// Turn on AP mode
|
// Turn on AP mode
|
||||||
self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await;
|
self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await;
|
||||||
@ -430,6 +440,11 @@ impl<'a> Control<'a> {
|
|||||||
events: &self.events,
|
events: &self.events,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Leave the wifi, with which we are currently associated.
|
||||||
|
pub async fn leave(&mut self) {
|
||||||
|
self.ioctl(IoctlType::Set, IOCTL_CMD_DISASSOC, 0, &mut []).await;
|
||||||
|
info!("Disassociated")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Scanner<'a> {
|
pub struct Scanner<'a> {
|
||||||
|
@ -83,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +229,8 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Bytes<'a>(pub &'a [u8]);
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
impl<'a> Debug for Bytes<'a> {
|
impl<'a> Debug for Bytes<'a> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
@ -27,7 +27,7 @@ use ioctl::IoctlState;
|
|||||||
|
|
||||||
use crate::bus::Bus;
|
use crate::bus::Bus;
|
||||||
pub use crate::bus::SpiBusCyw43;
|
pub use crate::bus::SpiBusCyw43;
|
||||||
pub use crate::control::{Control, Error as ControlError};
|
pub use crate::control::{Control, Error as ControlError, Scanner};
|
||||||
pub use crate::runner::Runner;
|
pub use crate::runner::Runner;
|
||||||
pub use crate::structs::BssInfo;
|
pub use crate::structs::BssInfo;
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ version = "0.1.0"
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-executor = { version = "0.2.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
embassy-executor = { version = "0.3.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||||
embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
|
embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
|
||||||
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] }
|
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] }
|
||||||
embassy-executor = { version = "0.2.0", features = ["nightly", "arch-cortex-m", "executor-thread"] }
|
embassy-executor = { version = "0.3.0", features = ["nightly", "arch-cortex-m", "executor-thread"] }
|
||||||
|
|
||||||
defmt = "0.3.0"
|
defmt = "0.3.0"
|
||||||
defmt-rtt = "0.3.0"
|
defmt-rtt = "0.3.0"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,7 +21,7 @@ default = ["time"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true }
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
||||||
"unproven",
|
"unproven",
|
||||||
] }
|
] }
|
||||||
|
@ -5,9 +5,12 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## Unreleased
|
## 0.3.0 - 2023-08-25
|
||||||
|
|
||||||
- Replaced Pender. Implementations now must define an extern function called `__pender`.
|
- Replaced Pender. Implementations now must define an extern function called `__pender`.
|
||||||
|
- Made `raw::AvailableTask` public
|
||||||
|
- Made `SpawnToken::new_failed` public
|
||||||
|
- You can now use arbitrary expressions to specify `#[task(pool_size = X)]`
|
||||||
|
|
||||||
## 0.2.1 - 2023-08-10
|
## 0.2.1 - 2023-08-10
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-executor"
|
name = "embassy-executor"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "async/await executor designed for embedded usage"
|
description = "async/await executor designed for embedded usage"
|
||||||
@ -58,8 +58,8 @@ log = { version = "0.4.14", optional = true }
|
|||||||
rtos-trace = { version = "0.1.2", optional = true }
|
rtos-trace = { version = "0.1.2", optional = true }
|
||||||
|
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
|
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true}
|
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true}
|
||||||
atomic-polyfill = "1.0.1"
|
atomic-polyfill = "1.0.1"
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
static_cell = "1.1"
|
static_cell = "1.1"
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
const THREAD_PENDER: usize = usize::MAX;
|
|
||||||
|
|
||||||
#[export_name = "__pender"]
|
#[export_name = "__pender"]
|
||||||
#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))]
|
#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))]
|
||||||
fn __pender(context: *mut ()) {
|
fn __pender(context: *mut ()) {
|
||||||
@ -48,13 +46,14 @@ fn __pender(context: *mut ()) {
|
|||||||
pub use thread::*;
|
pub use thread::*;
|
||||||
#[cfg(feature = "executor-thread")]
|
#[cfg(feature = "executor-thread")]
|
||||||
mod thread {
|
mod thread {
|
||||||
|
pub(super) const THREAD_PENDER: usize = usize::MAX;
|
||||||
|
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_macros::main_cortex_m as main;
|
pub use embassy_macros::main_cortex_m as main;
|
||||||
|
|
||||||
use crate::arch::THREAD_PENDER;
|
|
||||||
use crate::{raw, Spawner};
|
use crate::{raw, Spawner};
|
||||||
|
|
||||||
/// Thread mode executor, using WFE/SEV.
|
/// Thread mode executor, using WFE/SEV.
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -147,10 +147,7 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
|
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
|
||||||
let task = AvailableTask::claim(self);
|
let task = AvailableTask::claim(self);
|
||||||
match task {
|
match task {
|
||||||
Some(task) => {
|
Some(task) => task.initialize(future),
|
||||||
let task = task.initialize(future);
|
|
||||||
unsafe { SpawnToken::<F>::new(task) }
|
|
||||||
}
|
|
||||||
None => SpawnToken::new_failed(),
|
None => SpawnToken::new_failed(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,12 +183,16 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AvailableTask<F: Future + 'static> {
|
/// An uninitialized [`TaskStorage`].
|
||||||
|
pub struct AvailableTask<F: Future + 'static> {
|
||||||
task: &'static TaskStorage<F>,
|
task: &'static TaskStorage<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Future + 'static> AvailableTask<F> {
|
impl<F: Future + 'static> AvailableTask<F> {
|
||||||
fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
|
/// Try to claim a [`TaskStorage`].
|
||||||
|
///
|
||||||
|
/// This function returns `None` if a task has already been spawned and has not finished running.
|
||||||
|
pub fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
|
||||||
task.raw
|
task.raw
|
||||||
.state
|
.state
|
||||||
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
|
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
|
||||||
@ -199,61 +200,30 @@ impl<F: Future + 'static> AvailableTask<F> {
|
|||||||
.map(|_| Self { task })
|
.map(|_| Self { task })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize(self, future: impl FnOnce() -> F) -> TaskRef {
|
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
|
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
|
||||||
self.task.future.write(future());
|
self.task.future.write(future());
|
||||||
}
|
|
||||||
TaskRef::new(self.task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Raw storage that can hold up to N tasks of the same type.
|
let task = TaskRef::new(self.task);
|
||||||
///
|
|
||||||
/// This is essentially a `[TaskStorage<F>; N]`.
|
|
||||||
pub struct TaskPool<F: Future + 'static, const N: usize> {
|
|
||||||
pool: [TaskStorage<F>; N],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
|
SpawnToken::new(task)
|
||||||
/// Create a new TaskPool, with all tasks in non-spawned state.
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
pool: [TaskStorage::NEW; N],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to spawn a task in the pool.
|
/// Initialize the [`TaskStorage`] to run the given future.
|
||||||
///
|
pub fn initialize(self, future: impl FnOnce() -> F) -> SpawnToken<F> {
|
||||||
/// See [`TaskStorage::spawn()`] for details.
|
self.initialize_impl::<F>(future)
|
||||||
///
|
|
||||||
/// This will loop over the pool and spawn the task in the first storage that
|
|
||||||
/// is currently free. If none is free, a "poisoned" SpawnToken is returned,
|
|
||||||
/// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
|
|
||||||
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
|
|
||||||
let task = self.pool.iter().find_map(AvailableTask::claim);
|
|
||||||
match task {
|
|
||||||
Some(task) => {
|
|
||||||
let task = task.initialize(future);
|
|
||||||
unsafe { SpawnToken::<F>::new(task) }
|
|
||||||
}
|
|
||||||
None => SpawnToken::new_failed(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like spawn(), but allows the task to be send-spawned if the args are Send even if
|
/// Initialize the [`TaskStorage`] to run the given future.
|
||||||
/// the future is !Send.
|
|
||||||
///
|
///
|
||||||
/// Not covered by semver guarantees. DO NOT call this directly. Intended to be used
|
/// # Safety
|
||||||
/// by the Embassy macros ONLY.
|
|
||||||
///
|
///
|
||||||
/// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
|
/// `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
|
||||||
/// is an `async fn`, NOT a hand-written `Future`.
|
/// is an `async fn`, NOT a hand-written `Future`.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized>
|
pub unsafe fn __initialize_async_fn<FutFn>(self, future: impl FnOnce() -> F) -> SpawnToken<FutFn> {
|
||||||
where
|
|
||||||
FutFn: FnOnce() -> F,
|
|
||||||
{
|
|
||||||
// When send-spawning a task, we construct the future in this thread, and effectively
|
// When send-spawning a task, we construct the future in this thread, and effectively
|
||||||
// "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory,
|
// "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory,
|
||||||
// send-spawning should require the future `F` to be `Send`.
|
// send-spawning should require the future `F` to be `Send`.
|
||||||
@ -279,16 +249,59 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
|
|||||||
//
|
//
|
||||||
// This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly
|
// This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly
|
||||||
// by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken<F>`.
|
// by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken<F>`.
|
||||||
|
self.initialize_impl::<FutFn>(future)
|
||||||
let task = self.pool.iter().find_map(AvailableTask::claim);
|
|
||||||
match task {
|
|
||||||
Some(task) => {
|
|
||||||
let task = task.initialize(future);
|
|
||||||
unsafe { SpawnToken::<FutFn>::new(task) }
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Raw storage that can hold up to N tasks of the same type.
|
||||||
|
///
|
||||||
|
/// This is essentially a `[TaskStorage<F>; N]`.
|
||||||
|
pub struct TaskPool<F: Future + 'static, const N: usize> {
|
||||||
|
pool: [TaskStorage<F>; N],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
|
||||||
|
/// Create a new TaskPool, with all tasks in non-spawned state.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
pool: [TaskStorage::NEW; N],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_impl<T>(&'static self, future: impl FnOnce() -> F) -> SpawnToken<T> {
|
||||||
|
match self.pool.iter().find_map(AvailableTask::claim) {
|
||||||
|
Some(task) => task.initialize_impl::<T>(future),
|
||||||
None => SpawnToken::new_failed(),
|
None => SpawnToken::new_failed(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to spawn a task in the pool.
|
||||||
|
///
|
||||||
|
/// See [`TaskStorage::spawn()`] for details.
|
||||||
|
///
|
||||||
|
/// This will loop over the pool and spawn the task in the first storage that
|
||||||
|
/// is currently free. If none is free, a "poisoned" SpawnToken is returned,
|
||||||
|
/// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
|
||||||
|
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
|
||||||
|
self.spawn_impl::<F>(future)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Like spawn(), but allows the task to be send-spawned if the args are Send even if
|
||||||
|
/// the future is !Send.
|
||||||
|
///
|
||||||
|
/// Not covered by semver guarantees. DO NOT call this directly. Intended to be used
|
||||||
|
/// by the Embassy macros ONLY.
|
||||||
|
///
|
||||||
|
/// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
|
||||||
|
/// is an `async fn`, NOT a hand-written `Future`.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized>
|
||||||
|
where
|
||||||
|
FutFn: FnOnce() -> F,
|
||||||
|
{
|
||||||
|
// See the comment in AvailableTask::__initialize_async_fn for explanation.
|
||||||
|
self.spawn_impl::<FutFn>(future)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -33,7 +33,8 @@ impl<S> SpawnToken<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_failed() -> Self {
|
/// Return a SpawnToken that represents a failed spawn.
|
||||||
|
pub fn new_failed() -> Self {
|
||||||
Self {
|
Self {
|
||||||
raw_task: None,
|
raw_task: None,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,7 +20,7 @@ defmt = ["dep:defmt", "lorawan-device/defmt"]
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-macros"
|
name = "embassy-macros"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "macros for creating the entry point and tasks for embassy-executor"
|
description = "macros for creating the entry point and tasks for embassy-executor"
|
||||||
|
41
embassy-net-adin1110/Cargo.toml
Normal file
41
embassy-net-adin1110/Cargo.toml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
[package]
|
||||||
|
name = "embassy-net-adin1110"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "embassy-net driver for the ADIN1110 ethernet chip"
|
||||||
|
keywords = ["embedded", "ADIN1110", "embassy-net", "embedded-hal-async", "ethernet", "async"]
|
||||||
|
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
heapless = "0.7.16"
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
log = { version = "0.4.4", default-features = false, optional = true }
|
||||||
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||||
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
|
embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] }
|
||||||
|
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
||||||
|
embassy-time = { version = "0.1.0" }
|
||||||
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
|
bitfield = "0.14.0"
|
||||||
|
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
# reenable when https://github.com/dbrgn/embedded-hal-mock/pull/86 is merged.
|
||||||
|
#embedded-hal-mock = { git = "https://github.com/dbrgn/embedded-hal-mock", branch = "1-alpha", features = ["embedded-hal-async", "eh1"] }] }
|
||||||
|
embedded-hal-mock = { git = "https://github.com/newAM/embedded-hal-mock", branch = "eh1-rc.1", features = ["embedded-hal-async", "eh1"] }
|
||||||
|
crc = "3.0.1"
|
||||||
|
env_logger = "0.10"
|
||||||
|
critical-section = { version = "1.1.1", features = ["std"] }
|
||||||
|
futures-test = "0.3.17"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = [ ]
|
||||||
|
defmt = [ "dep:defmt" ]
|
||||||
|
|
||||||
|
[package.metadata.embassy_docs]
|
||||||
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-adin1110-v$VERSION/embassy-net-adin1110/src/"
|
||||||
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-adin1110/src/"
|
||||||
|
target = "thumbv7em-none-eabi"
|
55
embassy-net-adin1110/README.md
Normal file
55
embassy-net-adin1110/README.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# SPE ADIN1110 `embassy-net` integration
|
||||||
|
|
||||||
|
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the `Analog ADIN1110` SPI SPE ethernet chips.
|
||||||
|
|
||||||
|
## What is SPE or Single Pair Ethernet / 10 BASE-T1L
|
||||||
|
|
||||||
|
SPE stands for Single Pair Ethernet. As the names implies, SPE uses differential signalling with 2 wires (a twisted-pair) in a cable as the physical medium.
|
||||||
|
SPE is full-duplex - it can transmit and receive ethernet packets at the same time. SPE is still ethernet, only the physical layer is different.
|
||||||
|
|
||||||
|
SPE also supports [`PoDL (Power over Data Line)`](https://www.ti.com/lit/an/snla395/snla395.pdf), power delivery from 0.5 up to 50 Watts, similar to [`PoE`](https://en.wikipedia.org/wiki/Power_over_Ethernet), but an additional hardware and handshake protocol are needed.
|
||||||
|
|
||||||
|
SPE has many link speeds but only `10 BASE-T1L` is able to reach cable lengths up to 1000 meters in `2.4 Vpp` transmit amplitude.
|
||||||
|
Currently in 2023, none of the standards are compatible with each other.
|
||||||
|
Thus `10 BASE-T1L` won't work with a `10 BASE-T1S`, `100 BASE-T1` or any standard `x BASE-T`.
|
||||||
|
|
||||||
|
In the industry SPE is also called [`APL (Advanced Physical Layer)`](https://www.ethernet-apl.org), and is based on the `10 BASE-T1L` standard.
|
||||||
|
|
||||||
|
APL can be used in [`intrinsic safety applications/explosion hazardous areas`](https://en.wikipedia.org/wiki/Electrical_equipment_in_hazardous_areas) which has its own name and standard called [`2-WISE (2-wire intrinsically safe ethernet) IEC TS 60079-47:2021`](https://webstore.iec.ch/publication/64292).
|
||||||
|
|
||||||
|
`10 BASE-T1L` and `ADIN1110` are designed to support intrinsic safety applications. The power supply energy is fixed and PDoL is not supported.
|
||||||
|
|
||||||
|
## Supported SPI modes
|
||||||
|
|
||||||
|
`ADIN1110` supports two SPI modes. `Generic` and [`OPEN Alliance 10BASE-T1x MAC-PHY serial interface`](https://opensig.org/download/document/OPEN_Alliance_10BASET1x_MAC-PHY_Serial_Interface_V1.1.pdf)
|
||||||
|
|
||||||
|
Both modes support with and without additional CRC.
|
||||||
|
Currently only `Generic` SPI with or without CRC is supported.
|
||||||
|
|
||||||
|
*NOTE:* SPI Mode is selected by the hardware pins `SPI_CFG0` and `SPI_CFG1`. Software can't detect nor change the mode.
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
- Tested on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html) with an `STM32L4S5QII3P`, see [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) dor an example.
|
||||||
|
- [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit`](https://www.sparkfun.com/products/19628), supporting multiple microcontrollers. **Make sure to check if it's a microcontroller that is supported by Embassy!**
|
||||||
|
|
||||||
|
## Other SPE chips
|
||||||
|
|
||||||
|
* [`Analog ADIN2111`](https://www.analog.com/en/products/adin2111.html) 2 Port SPI version. Can work with this driver.
|
||||||
|
* [`Analog ADIN1100`](https://www.analog.com/en/products/adin1100.html) RGMII version.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
ADIN1110 library can tested on the host with a mock SPI driver.
|
||||||
|
|
||||||
|
$ `cargo test --target x86_64-unknown-linux-gnu`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is 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.
|
358
embassy-net-adin1110/src/crc32.rs
Normal file
358
embassy-net-adin1110/src/crc32.rs
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
pub const CRC32R_LOOKUP_TABLE: [u32; 256] = [
|
||||||
|
0x0000_0000,
|
||||||
|
0x7707_3096,
|
||||||
|
0xEE0E_612C,
|
||||||
|
0x9909_51BA,
|
||||||
|
0x076D_C419,
|
||||||
|
0x706A_F48F,
|
||||||
|
0xE963_A535,
|
||||||
|
0x9E64_95A3,
|
||||||
|
0x0EDB_8832,
|
||||||
|
0x79DC_B8A4,
|
||||||
|
0xE0D5_E91E,
|
||||||
|
0x97D2_D988,
|
||||||
|
0x09B6_4C2B,
|
||||||
|
0x7EB1_7CBD,
|
||||||
|
0xE7B8_2D07,
|
||||||
|
0x90BF_1D91,
|
||||||
|
0x1DB7_1064,
|
||||||
|
0x6AB0_20F2,
|
||||||
|
0xF3B9_7148,
|
||||||
|
0x84BE_41DE,
|
||||||
|
0x1ADA_D47D,
|
||||||
|
0x6DDD_E4EB,
|
||||||
|
0xF4D4_B551,
|
||||||
|
0x83D3_85C7,
|
||||||
|
0x136C_9856,
|
||||||
|
0x646B_A8C0,
|
||||||
|
0xFD62_F97A,
|
||||||
|
0x8A65_C9EC,
|
||||||
|
0x1401_5C4F,
|
||||||
|
0x6306_6CD9,
|
||||||
|
0xFA0F_3D63,
|
||||||
|
0x8D08_0DF5,
|
||||||
|
0x3B6E_20C8,
|
||||||
|
0x4C69_105E,
|
||||||
|
0xD560_41E4,
|
||||||
|
0xA267_7172,
|
||||||
|
0x3C03_E4D1,
|
||||||
|
0x4B04_D447,
|
||||||
|
0xD20D_85FD,
|
||||||
|
0xA50A_B56B,
|
||||||
|
0x35B5_A8FA,
|
||||||
|
0x42B2_986C,
|
||||||
|
0xDBBB_C9D6,
|
||||||
|
0xACBC_F940,
|
||||||
|
0x32D8_6CE3,
|
||||||
|
0x45DF_5C75,
|
||||||
|
0xDCD6_0DCF,
|
||||||
|
0xABD1_3D59,
|
||||||
|
0x26D9_30AC,
|
||||||
|
0x51DE_003A,
|
||||||
|
0xC8D7_5180,
|
||||||
|
0xBFD0_6116,
|
||||||
|
0x21B4_F4B5,
|
||||||
|
0x56B3_C423,
|
||||||
|
0xCFBA_9599,
|
||||||
|
0xB8BD_A50F,
|
||||||
|
0x2802_B89E,
|
||||||
|
0x5F05_8808,
|
||||||
|
0xC60C_D9B2,
|
||||||
|
0xB10B_E924,
|
||||||
|
0x2F6F_7C87,
|
||||||
|
0x5868_4C11,
|
||||||
|
0xC161_1DAB,
|
||||||
|
0xB666_2D3D,
|
||||||
|
0x76DC_4190,
|
||||||
|
0x01DB_7106,
|
||||||
|
0x98D2_20BC,
|
||||||
|
0xEFD5_102A,
|
||||||
|
0x71B1_8589,
|
||||||
|
0x06B6_B51F,
|
||||||
|
0x9FBF_E4A5,
|
||||||
|
0xE8B8_D433,
|
||||||
|
0x7807_C9A2,
|
||||||
|
0x0F00_F934,
|
||||||
|
0x9609_A88E,
|
||||||
|
0xE10E_9818,
|
||||||
|
0x7F6A_0DBB,
|
||||||
|
0x086D_3D2D,
|
||||||
|
0x9164_6C97,
|
||||||
|
0xE663_5C01,
|
||||||
|
0x6B6B_51F4,
|
||||||
|
0x1C6C_6162,
|
||||||
|
0x8565_30D8,
|
||||||
|
0xF262_004E,
|
||||||
|
0x6C06_95ED,
|
||||||
|
0x1B01_A57B,
|
||||||
|
0x8208_F4C1,
|
||||||
|
0xF50F_C457,
|
||||||
|
0x65B0_D9C6,
|
||||||
|
0x12B7_E950,
|
||||||
|
0x8BBE_B8EA,
|
||||||
|
0xFCB9_887C,
|
||||||
|
0x62DD_1DDF,
|
||||||
|
0x15DA_2D49,
|
||||||
|
0x8CD3_7CF3,
|
||||||
|
0xFBD4_4C65,
|
||||||
|
0x4DB2_6158,
|
||||||
|
0x3AB5_51CE,
|
||||||
|
0xA3BC_0074,
|
||||||
|
0xD4BB_30E2,
|
||||||
|
0x4ADF_A541,
|
||||||
|
0x3DD8_95D7,
|
||||||
|
0xA4D1_C46D,
|
||||||
|
0xD3D6_F4FB,
|
||||||
|
0x4369_E96A,
|
||||||
|
0x346E_D9FC,
|
||||||
|
0xAD67_8846,
|
||||||
|
0xDA60_B8D0,
|
||||||
|
0x4404_2D73,
|
||||||
|
0x3303_1DE5,
|
||||||
|
0xAA0A_4C5F,
|
||||||
|
0xDD0D_7CC9,
|
||||||
|
0x5005_713C,
|
||||||
|
0x2702_41AA,
|
||||||
|
0xBE0B_1010,
|
||||||
|
0xC90C_2086,
|
||||||
|
0x5768_B525,
|
||||||
|
0x206F_85B3,
|
||||||
|
0xB966_D409,
|
||||||
|
0xCE61_E49F,
|
||||||
|
0x5EDE_F90E,
|
||||||
|
0x29D9_C998,
|
||||||
|
0xB0D0_9822,
|
||||||
|
0xC7D7_A8B4,
|
||||||
|
0x59B3_3D17,
|
||||||
|
0x2EB4_0D81,
|
||||||
|
0xB7BD_5C3B,
|
||||||
|
0xC0BA_6CAD,
|
||||||
|
0xEDB8_8320,
|
||||||
|
0x9ABF_B3B6,
|
||||||
|
0x03B6_E20C,
|
||||||
|
0x74B1_D29A,
|
||||||
|
0xEAD5_4739,
|
||||||
|
0x9DD2_77AF,
|
||||||
|
0x04DB_2615,
|
||||||
|
0x73DC_1683,
|
||||||
|
0xE363_0B12,
|
||||||
|
0x9464_3B84,
|
||||||
|
0x0D6D_6A3E,
|
||||||
|
0x7A6A_5AA8,
|
||||||
|
0xE40E_CF0B,
|
||||||
|
0x9309_FF9D,
|
||||||
|
0x0A00_AE27,
|
||||||
|
0x7D07_9EB1,
|
||||||
|
0xF00F_9344,
|
||||||
|
0x8708_A3D2,
|
||||||
|
0x1E01_F268,
|
||||||
|
0x6906_C2FE,
|
||||||
|
0xF762_575D,
|
||||||
|
0x8065_67CB,
|
||||||
|
0x196C_3671,
|
||||||
|
0x6E6B_06E7,
|
||||||
|
0xFED4_1B76,
|
||||||
|
0x89D3_2BE0,
|
||||||
|
0x10DA_7A5A,
|
||||||
|
0x67DD_4ACC,
|
||||||
|
0xF9B9_DF6F,
|
||||||
|
0x8EBE_EFF9,
|
||||||
|
0x17B7_BE43,
|
||||||
|
0x60B0_8ED5,
|
||||||
|
0xD6D6_A3E8,
|
||||||
|
0xA1D1_937E,
|
||||||
|
0x38D8_C2C4,
|
||||||
|
0x4FDF_F252,
|
||||||
|
0xD1BB_67F1,
|
||||||
|
0xA6BC_5767,
|
||||||
|
0x3FB5_06DD,
|
||||||
|
0x48B2_364B,
|
||||||
|
0xD80D_2BDA,
|
||||||
|
0xAF0A_1B4C,
|
||||||
|
0x3603_4AF6,
|
||||||
|
0x4104_7A60,
|
||||||
|
0xDF60_EFC3,
|
||||||
|
0xA867_DF55,
|
||||||
|
0x316E_8EEF,
|
||||||
|
0x4669_BE79,
|
||||||
|
0xCB61_B38C,
|
||||||
|
0xBC66_831A,
|
||||||
|
0x256F_D2A0,
|
||||||
|
0x5268_E236,
|
||||||
|
0xCC0C_7795,
|
||||||
|
0xBB0B_4703,
|
||||||
|
0x2202_16B9,
|
||||||
|
0x5505_262F,
|
||||||
|
0xC5BA_3BBE,
|
||||||
|
0xB2BD_0B28,
|
||||||
|
0x2BB4_5A92,
|
||||||
|
0x5CB3_6A04,
|
||||||
|
0xC2D7_FFA7,
|
||||||
|
0xB5D0_CF31,
|
||||||
|
0x2CD9_9E8B,
|
||||||
|
0x5BDE_AE1D,
|
||||||
|
0x9B64_C2B0,
|
||||||
|
0xEC63_F226,
|
||||||
|
0x756A_A39C,
|
||||||
|
0x026D_930A,
|
||||||
|
0x9C09_06A9,
|
||||||
|
0xEB0E_363F,
|
||||||
|
0x7207_6785,
|
||||||
|
0x0500_5713,
|
||||||
|
0x95BF_4A82,
|
||||||
|
0xE2B8_7A14,
|
||||||
|
0x7BB1_2BAE,
|
||||||
|
0x0CB6_1B38,
|
||||||
|
0x92D2_8E9B,
|
||||||
|
0xE5D5_BE0D,
|
||||||
|
0x7CDC_EFB7,
|
||||||
|
0x0BDB_DF21,
|
||||||
|
0x86D3_D2D4,
|
||||||
|
0xF1D4_E242,
|
||||||
|
0x68DD_B3F8,
|
||||||
|
0x1FDA_836E,
|
||||||
|
0x81BE_16CD,
|
||||||
|
0xF6B9_265B,
|
||||||
|
0x6FB0_77E1,
|
||||||
|
0x18B7_4777,
|
||||||
|
0x8808_5AE6,
|
||||||
|
0xFF0F_6A70,
|
||||||
|
0x6606_3BCA,
|
||||||
|
0x1101_0B5C,
|
||||||
|
0x8F65_9EFF,
|
||||||
|
0xF862_AE69,
|
||||||
|
0x616B_FFD3,
|
||||||
|
0x166C_CF45,
|
||||||
|
0xA00A_E278,
|
||||||
|
0xD70D_D2EE,
|
||||||
|
0x4E04_8354,
|
||||||
|
0x3903_B3C2,
|
||||||
|
0xA767_2661,
|
||||||
|
0xD060_16F7,
|
||||||
|
0x4969_474D,
|
||||||
|
0x3E6E_77DB,
|
||||||
|
0xAED1_6A4A,
|
||||||
|
0xD9D6_5ADC,
|
||||||
|
0x40DF_0B66,
|
||||||
|
0x37D8_3BF0,
|
||||||
|
0xA9BC_AE53,
|
||||||
|
0xDEBB_9EC5,
|
||||||
|
0x47B2_CF7F,
|
||||||
|
0x30B5_FFE9,
|
||||||
|
0xBDBD_F21C,
|
||||||
|
0xCABA_C28A,
|
||||||
|
0x53B3_9330,
|
||||||
|
0x24B4_A3A6,
|
||||||
|
0xBAD0_3605,
|
||||||
|
0xCDD7_0693,
|
||||||
|
0x54DE_5729,
|
||||||
|
0x23D9_67BF,
|
||||||
|
0xB366_7A2E,
|
||||||
|
0xC461_4AB8,
|
||||||
|
0x5D68_1B02,
|
||||||
|
0x2A6F_2B94,
|
||||||
|
0xB40B_BE37,
|
||||||
|
0xC30C_8EA1,
|
||||||
|
0x5A05_DF1B,
|
||||||
|
0x2D02_EF8D,
|
||||||
|
];
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ETH_FSC(pub u32);
|
||||||
|
|
||||||
|
impl ETH_FSC {
|
||||||
|
pub const CRC32_OK: u32 = 0x2144_df1c;
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(data: &[u8]) -> Self {
|
||||||
|
let fsc = data.iter().fold(u32::MAX, |crc, byte| {
|
||||||
|
let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte;
|
||||||
|
CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8)
|
||||||
|
}) ^ u32::MAX;
|
||||||
|
Self(fsc)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn update(self, data: &[u8]) -> Self {
|
||||||
|
let fsc = data.iter().fold(self.0 ^ u32::MAX, |crc, byte| {
|
||||||
|
let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte;
|
||||||
|
CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8)
|
||||||
|
}) ^ u32::MAX;
|
||||||
|
Self(fsc)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn crc_ok(&self) -> bool {
|
||||||
|
self.0 == Self::CRC32_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn hton_bytes(&self) -> [u8; 4] {
|
||||||
|
self.0.to_le_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn hton(&self) -> u32 {
|
||||||
|
self.0.to_le()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crc32_ethernet_frame() {
|
||||||
|
let packet_a = &[
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xff, 0x06, 0x00, 0x01, 0x08, 0x00,
|
||||||
|
0x06, 0x04, 0x00, 0x01, 0x00, 0xe0, 0x4c, 0x68, 0x0e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0xc0, 0xa8, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x65, 0x90, 0x3d,
|
||||||
|
];
|
||||||
|
|
||||||
|
let packet_b = &[
|
||||||
|
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xdd, 0x06, 0x00, 0x01, 0x08, 0x00,
|
||||||
|
0x06, 0x04, 0x00, 0x02, 0x00, 0xe0, 0x4c, 0x68, 0x09, 0xde, 0xc0, 0xa8, 0x01, 0x02, 0x12, 0x34, 0x56, 0x78,
|
||||||
|
0x9a, 0xbc, 0xc0, 0xa8, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, 0x67, 0x7c,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Packet A
|
||||||
|
let own_crc = ETH_FSC::new(&packet_a[0..60]);
|
||||||
|
let crc_bytes = own_crc.hton_bytes();
|
||||||
|
println!("{:08x} {:02x?}", own_crc.0, crc_bytes);
|
||||||
|
assert_eq!(&crc_bytes, &packet_a[60..64]);
|
||||||
|
|
||||||
|
let own_crc = ETH_FSC::new(packet_a);
|
||||||
|
println!("{:08x}", own_crc.0);
|
||||||
|
assert_eq!(own_crc.0, ETH_FSC::CRC32_OK);
|
||||||
|
|
||||||
|
// Packet B
|
||||||
|
let own_crc = ETH_FSC::new(&packet_b[0..60]);
|
||||||
|
let crc_bytes = own_crc.hton_bytes();
|
||||||
|
println!("{:08x} {:02x?}", own_crc.0, crc_bytes);
|
||||||
|
assert_eq!(&crc_bytes, &packet_b[60..64]);
|
||||||
|
|
||||||
|
let own_crc = ETH_FSC::new(packet_b);
|
||||||
|
println!("{:08x}", own_crc.0);
|
||||||
|
assert_eq!(own_crc.0, ETH_FSC::CRC32_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crc32_update() {
|
||||||
|
let full_data = &[
|
||||||
|
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xdd, 0x06, 0x00, 0x01, 0x08, 0x00,
|
||||||
|
0x06, 0x04, 0x00, 0x02, 0x00, 0xe0, 0x4c, 0x68, 0x09, 0xde, 0xc0, 0xa8, 0x01, 0x02, 0x12, 0x34, 0x56, 0x78,
|
||||||
|
0x9a, 0xbc, 0xc0, 0xa8, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, 0x67, 0x7c,
|
||||||
|
];
|
||||||
|
|
||||||
|
let (part_a, part_b) = full_data.split_at(16);
|
||||||
|
let crc_partially = ETH_FSC::new(part_a).update(part_b);
|
||||||
|
|
||||||
|
let crc_full = ETH_FSC::new(full_data);
|
||||||
|
|
||||||
|
assert_eq!(crc_full.0, crc_partially.0);
|
||||||
|
}
|
||||||
|
}
|
53
embassy-net-adin1110/src/crc8.rs
Normal file
53
embassy-net-adin1110/src/crc8.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/// CRC-8/ITU
|
||||||
|
const CRC8X_TABLE: [u8; 256] = [
|
||||||
|
0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e,
|
||||||
|
0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb,
|
||||||
|
0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8,
|
||||||
|
0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6,
|
||||||
|
0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d,
|
||||||
|
0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50,
|
||||||
|
0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80, 0x95,
|
||||||
|
0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,
|
||||||
|
0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f,
|
||||||
|
0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a,
|
||||||
|
0x33, 0x34, 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e,
|
||||||
|
0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,
|
||||||
|
0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc,
|
||||||
|
0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Calculate the crc of a pease of data.
|
||||||
|
pub fn crc8(data: &[u8]) -> u8 {
|
||||||
|
data.iter().fold(0, |crc, &byte| CRC8X_TABLE[usize::from(byte ^ crc)])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use ::crc::{Crc, CRC_8_SMBUS};
|
||||||
|
|
||||||
|
use super::crc8;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn spi_header_crc8() {
|
||||||
|
let data = &[0x80, 0x00];
|
||||||
|
|
||||||
|
let c = Crc::<u8>::new(&CRC_8_SMBUS);
|
||||||
|
let mut dig = c.digest();
|
||||||
|
dig.update(data);
|
||||||
|
let sw_crc = dig.finalize();
|
||||||
|
|
||||||
|
let own_crc = crc8(data);
|
||||||
|
|
||||||
|
assert_eq!(own_crc, sw_crc);
|
||||||
|
assert_eq!(own_crc, 182);
|
||||||
|
|
||||||
|
let data = &[0x80, 0x01];
|
||||||
|
let mut dig = c.digest();
|
||||||
|
dig.update(data);
|
||||||
|
let sw_crc = dig.finalize();
|
||||||
|
let own_crc = crc8(data);
|
||||||
|
|
||||||
|
assert_eq!(own_crc, sw_crc);
|
||||||
|
assert_eq!(own_crc, 177);
|
||||||
|
}
|
||||||
|
}
|
1046
embassy-net-adin1110/src/lib.rs
Normal file
1046
embassy-net-adin1110/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
175
embassy-net-adin1110/src/mdio.rs
Normal file
175
embassy-net-adin1110/src/mdio.rs
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
/// PHY Address: (0..=0x1F), 5-bits long.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
type PhyAddr = u8;
|
||||||
|
|
||||||
|
/// PHY Register: (0..=0x1F), 5-bits long.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
type RegC22 = u8;
|
||||||
|
|
||||||
|
/// PHY Register Clause 45.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
type RegC45 = u16;
|
||||||
|
|
||||||
|
/// PHY Register Value
|
||||||
|
#[allow(dead_code)]
|
||||||
|
type RegVal = u16;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const REG13: RegC22 = 13;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const REG14: RegC22 = 14;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const PHYADDR_MASK: u8 = 0x1f;
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const DEV_MASK: u8 = 0x1f;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(u16)]
|
||||||
|
enum Reg13Op {
|
||||||
|
Addr = 0b00 << 14,
|
||||||
|
Write = 0b01 << 14,
|
||||||
|
PostReadIncAddr = 0b10 << 14,
|
||||||
|
Read = 0b11 << 14,
|
||||||
|
}
|
||||||
|
/// `MdioBus` trait
|
||||||
|
/// Driver needs to implement the Clause 22
|
||||||
|
/// Optional Clause 45 is the device supports this.
|
||||||
|
///
|
||||||
|
/// Claus 45 methodes are bases on <https://www.ieee802.org/3/efm/public/nov02/oam/pannell_oam_1_1102.pdf>
|
||||||
|
pub trait MdioBus {
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
/// Read, Clause 22
|
||||||
|
async fn read_cl22(&mut self, phy_id: PhyAddr, reg: RegC22) -> Result<RegVal, Self::Error>;
|
||||||
|
|
||||||
|
/// Write, Clause 22
|
||||||
|
async fn write_cl22(&mut self, phy_id: PhyAddr, reg: RegC22, reg_val: RegVal) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
|
/// Read, Clause 45
|
||||||
|
/// This is the default implementation.
|
||||||
|
/// Many hardware these days support direct Clause 45 operations.
|
||||||
|
/// Implement this function when your hardware supports it.
|
||||||
|
async fn read_cl45(&mut self, phy_id: PhyAddr, regc45: (u8, RegC45)) -> Result<RegVal, Self::Error> {
|
||||||
|
// Write FN
|
||||||
|
let val = (Reg13Op::Addr as RegVal) | RegVal::from(regc45.0 & DEV_MASK);
|
||||||
|
|
||||||
|
self.write_cl22(phy_id, REG13, val).await?;
|
||||||
|
// Write Addr
|
||||||
|
self.write_cl22(phy_id, REG14, regc45.1).await?;
|
||||||
|
|
||||||
|
// Write FN
|
||||||
|
let val = (Reg13Op::Read as RegVal) | RegVal::from(regc45.0 & DEV_MASK);
|
||||||
|
self.write_cl22(phy_id, REG13, val).await?;
|
||||||
|
// Write Addr
|
||||||
|
self.read_cl22(phy_id, REG14).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write, Clause 45
|
||||||
|
/// This is the default implementation.
|
||||||
|
/// Many hardware these days support direct Clause 45 operations.
|
||||||
|
/// Implement this function when your hardware supports it.
|
||||||
|
async fn write_cl45(&mut self, phy_id: PhyAddr, regc45: (u8, RegC45), reg_val: RegVal) -> Result<(), Self::Error> {
|
||||||
|
let dev_addr = RegVal::from(regc45.0 & DEV_MASK);
|
||||||
|
let reg = regc45.1;
|
||||||
|
|
||||||
|
// Write FN
|
||||||
|
let val = (Reg13Op::Addr as RegVal) | dev_addr;
|
||||||
|
self.write_cl22(phy_id, REG13, val).await?;
|
||||||
|
// Write Addr
|
||||||
|
self.write_cl22(phy_id, REG14, reg).await?;
|
||||||
|
|
||||||
|
// Write FN
|
||||||
|
let val = (Reg13Op::Write as RegVal) | dev_addr;
|
||||||
|
self.write_cl22(phy_id, REG13, val).await?;
|
||||||
|
// Write Addr
|
||||||
|
self.write_cl22(phy_id, REG14, reg_val).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests {
|
||||||
|
// use core::convert::Infallible;
|
||||||
|
|
||||||
|
// use super::{MdioBus, PhyAddr, RegC22, RegVal};
|
||||||
|
|
||||||
|
// #[derive(Debug, PartialEq, Eq)]
|
||||||
|
// enum A {
|
||||||
|
// Read(PhyAddr, RegC22),
|
||||||
|
// Write(PhyAddr, RegC22, RegVal),
|
||||||
|
// }
|
||||||
|
|
||||||
|
// struct MockMdioBus(Vec<A>);
|
||||||
|
|
||||||
|
// impl MockMdioBus {
|
||||||
|
// pub fn clear(&mut self) {
|
||||||
|
// self.0.clear();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// impl MdioBus for MockMdioBus {
|
||||||
|
// type Error = Infallible;
|
||||||
|
|
||||||
|
// fn write_cl22(
|
||||||
|
// &mut self,
|
||||||
|
// phy_id: super::PhyAddr,
|
||||||
|
// reg: super::RegC22,
|
||||||
|
// reg_val: super::RegVal,
|
||||||
|
// ) -> Result<(), Self::Error> {
|
||||||
|
// self.0.push(A::Write(phy_id, reg, reg_val));
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn read_cl22(
|
||||||
|
// &mut self,
|
||||||
|
// phy_id: super::PhyAddr,
|
||||||
|
// reg: super::RegC22,
|
||||||
|
// ) -> Result<super::RegVal, Self::Error> {
|
||||||
|
// self.0.push(A::Read(phy_id, reg));
|
||||||
|
// Ok(0)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn read_test() {
|
||||||
|
// let mut mdiobus = MockMdioBus(Vec::with_capacity(20));
|
||||||
|
|
||||||
|
// mdiobus.clear();
|
||||||
|
// mdiobus.read_cl22(0x01, 0x00).unwrap();
|
||||||
|
// assert_eq!(mdiobus.0, vec![A::Read(0x01, 0x00)]);
|
||||||
|
|
||||||
|
// mdiobus.clear();
|
||||||
|
// mdiobus.read_cl45(0x01, (0xBB, 0x1234)).unwrap();
|
||||||
|
// assert_eq!(
|
||||||
|
// mdiobus.0,
|
||||||
|
// vec![
|
||||||
|
// #[allow(clippy::identity_op)]
|
||||||
|
// A::Write(0x01, 13, (0b00 << 14) | 27),
|
||||||
|
// A::Write(0x01, 14, 0x1234),
|
||||||
|
// A::Write(0x01, 13, (0b11 << 14) | 27),
|
||||||
|
// A::Read(0x01, 14)
|
||||||
|
// ]
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn write_test() {
|
||||||
|
// let mut mdiobus = MockMdioBus(Vec::with_capacity(20));
|
||||||
|
|
||||||
|
// mdiobus.clear();
|
||||||
|
// mdiobus.write_cl22(0x01, 0x00, 0xABCD).unwrap();
|
||||||
|
// assert_eq!(mdiobus.0, vec![A::Write(0x01, 0x00, 0xABCD)]);
|
||||||
|
|
||||||
|
// mdiobus.clear();
|
||||||
|
// mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).unwrap();
|
||||||
|
// assert_eq!(
|
||||||
|
// mdiobus.0,
|
||||||
|
// vec![
|
||||||
|
// A::Write(0x01, 13, 27),
|
||||||
|
// A::Write(0x01, 14, 0x1234),
|
||||||
|
// A::Write(0x01, 13, (0b01 << 14) | 27),
|
||||||
|
// A::Write(0x01, 14, 0xABCD)
|
||||||
|
// ]
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
142
embassy-net-adin1110/src/phy.rs
Normal file
142
embassy-net-adin1110/src/phy.rs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
use crate::mdio::MdioBus;
|
||||||
|
|
||||||
|
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
|
#[repr(u8)]
|
||||||
|
/// Clause 22 Registers
|
||||||
|
pub enum RegsC22 {
|
||||||
|
/// MII Control Register
|
||||||
|
CONTROL = 0x00,
|
||||||
|
/// MII Status Register
|
||||||
|
STATUS = 0x01,
|
||||||
|
/// PHY Identifier 1 Register
|
||||||
|
PHY_ID1 = 0x02,
|
||||||
|
/// PHY Identifier 2 Register.
|
||||||
|
PHY_ID2 = 0x03,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clause 45 Registers
|
||||||
|
#[allow(non_snake_case, dead_code)]
|
||||||
|
pub mod RegsC45 {
|
||||||
|
/// Device Address: 0x01
|
||||||
|
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum DA1 {
|
||||||
|
/// PMA/PMD Control 1 Register
|
||||||
|
PMA_PMD_CNTRL1 = 0x0000,
|
||||||
|
/// PMA/PMD Status 1 Register
|
||||||
|
PMA_PMD_STAT1 = 0x0001,
|
||||||
|
/// MSE Value Register
|
||||||
|
MSE_VAL = 0x830B,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DA1 {
|
||||||
|
#[must_use]
|
||||||
|
pub fn into(self) -> (u8, u16) {
|
||||||
|
(0x01, self as u16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Device Address: 0x03
|
||||||
|
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum DA3 {
|
||||||
|
/// PCS Control 1 Register
|
||||||
|
PCS_CNTRL1 = 0x0000,
|
||||||
|
/// PCS Status 1 Register
|
||||||
|
PCS_STAT1 = 0x0001,
|
||||||
|
/// PCS Status 2 Register
|
||||||
|
PCS_STAT2 = 0x0008,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DA3 {
|
||||||
|
#[must_use]
|
||||||
|
pub fn into(self) -> (u8, u16) {
|
||||||
|
(0x03, self as u16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Device Address: 0x07
|
||||||
|
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum DA7 {
|
||||||
|
/// Extra Autonegotiation Status Register
|
||||||
|
AN_STATUS_EXTRA = 0x8001,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DA7 {
|
||||||
|
#[must_use]
|
||||||
|
pub fn into(self) -> (u8, u16) {
|
||||||
|
(0x07, self as u16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Device Address: 0x1E
|
||||||
|
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum DA1E {
|
||||||
|
/// System Interrupt Status Register
|
||||||
|
CRSM_IRQ_STATUS = 0x0010,
|
||||||
|
/// System Interrupt Mask Register
|
||||||
|
CRSM_IRQ_MASK = 0x0020,
|
||||||
|
/// Pin Mux Configuration 1 Register
|
||||||
|
DIGIO_PINMUX = 0x8c56,
|
||||||
|
/// LED Control Register.
|
||||||
|
LED_CNTRL = 0x8C82,
|
||||||
|
/// LED Polarity Register
|
||||||
|
LED_POLARITY = 0x8C83,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DA1E {
|
||||||
|
#[must_use]
|
||||||
|
pub fn into(self) -> (u8, u16) {
|
||||||
|
(0x1e, self as u16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Device Address: 0x1F
|
||||||
|
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum DA1F {
|
||||||
|
/// PHY Subsystem Interrupt Status Register
|
||||||
|
PHY_SYBSYS_IRQ_STATUS = 0x0011,
|
||||||
|
/// PHY Subsystem Interrupt Mask Register
|
||||||
|
PHY_SYBSYS_IRQ_MASK = 0x0021,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DA1F {
|
||||||
|
#[must_use]
|
||||||
|
pub fn into(self) -> (u8, u16) {
|
||||||
|
(0x1f, self as u16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Phy10BaseT1x(u8);
|
||||||
|
|
||||||
|
impl Default for Phy10BaseT1x {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(0x01)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Phy10BaseT1x {
|
||||||
|
/// Get the both parts of the PHYID.
|
||||||
|
pub async fn get_id<MDIOBUS, MDE>(&self, mdiobus: &mut MDIOBUS) -> Result<u32, MDE>
|
||||||
|
where
|
||||||
|
MDIOBUS: MdioBus<Error = MDE>,
|
||||||
|
MDE: core::fmt::Debug,
|
||||||
|
{
|
||||||
|
let mut phyid = u32::from(mdiobus.read_cl22(self.0, RegsC22::PHY_ID1 as u8).await?) << 16;
|
||||||
|
phyid |= u32::from(mdiobus.read_cl22(self.0, RegsC22::PHY_ID2 as u8).await?);
|
||||||
|
Ok(phyid)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the Mean Squared Error Value.
|
||||||
|
pub async fn get_sqi<MDIOBUS, MDE>(&self, mdiobus: &mut MDIOBUS) -> Result<u16, MDE>
|
||||||
|
where
|
||||||
|
MDIOBUS: MdioBus<Error = MDE>,
|
||||||
|
MDE: core::fmt::Debug,
|
||||||
|
{
|
||||||
|
mdiobus.read_cl45(self.0, RegsC45::DA1::MSE_VAL.into()).await
|
||||||
|
}
|
||||||
|
}
|
408
embassy-net-adin1110/src/regs.rs
Normal file
408
embassy-net-adin1110/src/regs.rs
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
use bitfield::{bitfield, bitfield_bitrange, bitfield_fields};
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[repr(u16)]
|
||||||
|
/// SPI REGISTER DETAILS
|
||||||
|
/// Table 38.
|
||||||
|
pub enum SpiRegisters {
|
||||||
|
IDVER = 0x00,
|
||||||
|
PHYID = 0x01,
|
||||||
|
CAPABILITY = 0x02,
|
||||||
|
RESET = 0x03,
|
||||||
|
CONFIG0 = 0x04,
|
||||||
|
CONFIG2 = 0x06,
|
||||||
|
STATUS0 = 0x08,
|
||||||
|
STATUS1 = 0x09,
|
||||||
|
IMASK0 = 0x0C,
|
||||||
|
IMASK1 = 0x0D,
|
||||||
|
MDIO_ACC = 0x20,
|
||||||
|
TX_FSIZE = 0x30,
|
||||||
|
TX = 0x31,
|
||||||
|
TX_SPACE = 0x32,
|
||||||
|
FIFO_CLR = 0x36,
|
||||||
|
ADDR_FILT_UPR0 = 0x50,
|
||||||
|
ADDR_FILT_LWR0 = 0x51,
|
||||||
|
ADDR_FILT_UPR1 = 0x52,
|
||||||
|
ADDR_FILT_LWR1 = 0x53,
|
||||||
|
ADDR_MSK_LWR0 = 0x70,
|
||||||
|
ADDR_MSK_UPR0 = 0x71,
|
||||||
|
ADDR_MSK_LWR1 = 0x72,
|
||||||
|
ADDR_MSK_UPR1 = 0x73,
|
||||||
|
RX_FSIZE = 0x90,
|
||||||
|
RX = 0x91,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SpiRegisters> for u16 {
|
||||||
|
fn from(val: SpiRegisters) -> Self {
|
||||||
|
val as u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for SpiRegisters {
|
||||||
|
fn from(value: u16) -> Self {
|
||||||
|
match value {
|
||||||
|
0x00 => Self::IDVER,
|
||||||
|
0x01 => Self::PHYID,
|
||||||
|
0x02 => Self::CAPABILITY,
|
||||||
|
0x03 => Self::RESET,
|
||||||
|
0x04 => Self::CONFIG0,
|
||||||
|
0x06 => Self::CONFIG2,
|
||||||
|
0x08 => Self::STATUS0,
|
||||||
|
0x09 => Self::STATUS1,
|
||||||
|
0x0C => Self::IMASK0,
|
||||||
|
0x0D => Self::IMASK1,
|
||||||
|
0x20 => Self::MDIO_ACC,
|
||||||
|
0x30 => Self::TX_FSIZE,
|
||||||
|
0x31 => Self::TX,
|
||||||
|
0x32 => Self::TX_SPACE,
|
||||||
|
0x36 => Self::FIFO_CLR,
|
||||||
|
0x50 => Self::ADDR_FILT_UPR0,
|
||||||
|
0x51 => Self::ADDR_FILT_LWR0,
|
||||||
|
0x52 => Self::ADDR_FILT_UPR1,
|
||||||
|
0x53 => Self::ADDR_FILT_LWR1,
|
||||||
|
0x70 => Self::ADDR_MSK_LWR0,
|
||||||
|
0x71 => Self::ADDR_MSK_UPR0,
|
||||||
|
0x72 => Self::ADDR_MSK_LWR1,
|
||||||
|
0x73 => Self::ADDR_MSK_UPR1,
|
||||||
|
0x90 => Self::RX_FSIZE,
|
||||||
|
0x91 => Self::RX,
|
||||||
|
e => panic!("Unknown value {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register definitions
|
||||||
|
bitfield! {
|
||||||
|
/// Status0 Register bits
|
||||||
|
pub struct Status0(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
/// Control Data Protection Error
|
||||||
|
pub cdpe, _ : 12;
|
||||||
|
/// Transmit Frame Check Squence Error
|
||||||
|
pub txfcse, _: 11;
|
||||||
|
/// Transmit Time Stamp Capture Available C
|
||||||
|
pub ttscac, _ : 10;
|
||||||
|
/// Transmit Time Stamp Capture Available B
|
||||||
|
pub ttscab, _ : 9;
|
||||||
|
/// Transmit Time Stamp Capture Available A
|
||||||
|
pub ttscaa, _ : 8;
|
||||||
|
/// PHY Interrupt for Port 1
|
||||||
|
pub phyint, _ : 7;
|
||||||
|
/// Reset Complete
|
||||||
|
pub resetc, _ : 6;
|
||||||
|
/// Header error
|
||||||
|
pub hdre, _ : 5;
|
||||||
|
/// Loss of Frame Error
|
||||||
|
pub lofe, _ : 4;
|
||||||
|
/// Receiver Buffer Overflow Error
|
||||||
|
pub rxboe, _ : 3;
|
||||||
|
/// Host Tx FIFO Under Run Error
|
||||||
|
pub txbue, _ : 2;
|
||||||
|
/// Host Tx FIFO Overflow
|
||||||
|
pub txboe, _ : 1;
|
||||||
|
/// Transmit Protocol Error
|
||||||
|
pub txpe, _ : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
/// Status1 Register bits
|
||||||
|
pub struct Status1(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
/// ECC Error on Reading the Frame Size from a Tx FIFO
|
||||||
|
pub tx_ecc_err, set_tx_ecc_err: 12;
|
||||||
|
/// ECC Error on Reading the Frame Size from an Rx FIFO
|
||||||
|
pub rx_ecc_err, set_rx_ecc_err : 11;
|
||||||
|
/// Detected an Error on an SPI Transaction
|
||||||
|
pub spi_err, set_spi_err: 10;
|
||||||
|
/// Rx MAC Interframe Gap Error
|
||||||
|
pub p1_rx_ifg_err, set_p1_rx_ifg_err : 8;
|
||||||
|
/// Port1 Rx Ready High Priority
|
||||||
|
pub p1_rx_rdy_hi, set_p1_rx_rdy_hi : 5;
|
||||||
|
/// Port 1 Rx FIFO Contains Data
|
||||||
|
pub p1_rx_rdy, set_p1_rx_rdy : 4;
|
||||||
|
/// Tx Ready
|
||||||
|
pub tx_rdy, set_tx_rdy : 3;
|
||||||
|
/// Link Status Changed
|
||||||
|
pub link_change, set_link_change : 1;
|
||||||
|
/// Port 1 Link Status
|
||||||
|
pub p1_link_status, _ : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
/// Config0 Register bits
|
||||||
|
pub struct Config0(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
/// Configuration Synchronization
|
||||||
|
pub sync, set_sync : 15;
|
||||||
|
/// Transmit Frame Check Sequence Validation Enable
|
||||||
|
pub txfcsve, set_txfcsve : 14;
|
||||||
|
/// !CS Align Receive Frame Enable
|
||||||
|
pub csarfe, set_csarfe : 13;
|
||||||
|
/// Zero Align Receive Frame Enable
|
||||||
|
pub zarfe, set_zarfe : 12;
|
||||||
|
/// Transmit Credit Threshold
|
||||||
|
pub tcxthresh, set_tcxthresh : 11, 10;
|
||||||
|
/// Transmit Cut Through Enable
|
||||||
|
pub txcte, set_txcte : 9;
|
||||||
|
/// Receive Cut Through Enable
|
||||||
|
pub rxcte, set_rxcte : 8;
|
||||||
|
/// Frame Time Stamp Enable
|
||||||
|
pub ftse, set_ftse : 7;
|
||||||
|
/// Receive Frame Time Stamp Select
|
||||||
|
pub ftss, set_ftss : 6;
|
||||||
|
/// Enable Control Data Read Write Protection
|
||||||
|
pub prote, set_prote : 5;
|
||||||
|
/// Enable TX Data Chunk Sequence and Retry
|
||||||
|
pub seqe, set_seqe : 4;
|
||||||
|
/// Chunk Payload Selector (N).
|
||||||
|
pub cps, set_cps : 2, 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
/// Config2 Register bits
|
||||||
|
pub struct Config2(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
/// Assert TX_RDY When the Tx FIFO is Empty
|
||||||
|
pub tx_rdy_on_empty, set_tx_rdy_on_empty : 8;
|
||||||
|
/// Determines If the SFD is Detected in the PHY or MAC
|
||||||
|
pub sdf_detect_src, set_sdf_detect_src : 7;
|
||||||
|
/// Statistics Clear on Reading
|
||||||
|
pub stats_clr_on_rd, set_stats_clr_on_rd : 6;
|
||||||
|
/// Enable CRC Append
|
||||||
|
pub crc_append, set_crc_append : 5;
|
||||||
|
/// Admit Frames with IFG Errors on Port 1 (P1)
|
||||||
|
pub p1_rcv_ifg_err_frm, set_p1_rcv_ifg_err_frm : 4;
|
||||||
|
/// Forward Frames Not Matching Any MAC Address to the Host
|
||||||
|
pub p1_fwd_unk2host, set_p1_fwd_unk2host : 2;
|
||||||
|
/// SPI to MDIO Bridge MDC Clock Speed
|
||||||
|
pub mspeed, set_mspeed : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
/// IMASK0 Register bits
|
||||||
|
pub struct IMask0(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
/// Control Data Protection Error Mask
|
||||||
|
pub cppem, set_cppem : 12;
|
||||||
|
/// Transmit Frame Check Sequence Error Mask
|
||||||
|
pub txfcsem, set_txfcsem : 11;
|
||||||
|
/// Transmit Time Stamp Capture Available C Mask
|
||||||
|
pub ttscacm, set_ttscacm : 10;
|
||||||
|
/// Transmit Time Stamp Capture Available B Mask
|
||||||
|
pub ttscabm, set_ttscabm : 9;
|
||||||
|
/// Transmit Time Stamp Capture Available A Mask
|
||||||
|
pub ttscaam, set_ttscaam : 8;
|
||||||
|
/// Physical Layer Interrupt Mask
|
||||||
|
pub phyintm, set_phyintm : 7;
|
||||||
|
/// RESET Complete Mask
|
||||||
|
pub resetcm, set_resetcm : 6;
|
||||||
|
/// Header Error Mask
|
||||||
|
pub hdrem, set_hdrem : 5;
|
||||||
|
/// Loss of Frame Error Mask
|
||||||
|
pub lofem, set_lofem : 4;
|
||||||
|
/// Receive Buffer Overflow Error Mask
|
||||||
|
pub rxboem, set_rxboem : 3;
|
||||||
|
/// Transmit Buffer Underflow Error Mask
|
||||||
|
pub txbuem, set_txbuem : 2;
|
||||||
|
/// Transmit Buffer Overflow Error Mask
|
||||||
|
pub txboem, set_txboem : 1;
|
||||||
|
/// Transmit Protocol Error Mask
|
||||||
|
pub txpem, set_txpem : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
/// IMASK1 Register bits
|
||||||
|
pub struct IMask1(u32);
|
||||||
|
impl Debug;
|
||||||
|
u32;
|
||||||
|
/// Mask Bit for TXF_ECC_ERR
|
||||||
|
pub tx_ecc_err_mask, set_tx_ecc_err_mask : 12;
|
||||||
|
/// Mask Bit for RXF_ECC_ERR
|
||||||
|
pub rx_ecc_err_mask, set_rx_ecc_err_mask : 11;
|
||||||
|
/// Mask Bit for SPI_ERR
|
||||||
|
/// This field is only used with the generic SPI protocol
|
||||||
|
pub spi_err_mask, set_spi_err_mask : 10;
|
||||||
|
/// Mask Bit for RX_IFG_ERR
|
||||||
|
pub p1_rx_ifg_err_mask, set_p1_rx_ifg_err_mask : 8;
|
||||||
|
/// Mask Bit for P1_RX_RDY
|
||||||
|
/// This field is only used with the generic SPI protocol
|
||||||
|
pub p1_rx_rdy_mask, set_p1_rx_rdy_mask : 4;
|
||||||
|
/// Mask Bit for TX_FRM_DONE
|
||||||
|
/// This field is only used with the generic SPI protocol
|
||||||
|
pub tx_rdy_mask, set_tx_rdy_mask : 3;
|
||||||
|
/// Mask Bit for LINK_CHANGE
|
||||||
|
pub link_change_mask, set_link_change_mask : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// LED Functions
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum LedFunc {
|
||||||
|
LinkupTxRxActicity = 0,
|
||||||
|
LinkupTxActicity,
|
||||||
|
LinkupRxActicity,
|
||||||
|
LinkupOnly,
|
||||||
|
TxRxActivity,
|
||||||
|
TxActivity,
|
||||||
|
RxActivity,
|
||||||
|
LinkupRxEr,
|
||||||
|
LinkupRxTxEr,
|
||||||
|
RxEr,
|
||||||
|
RxTxEr,
|
||||||
|
TxSop,
|
||||||
|
RxSop,
|
||||||
|
On,
|
||||||
|
Off,
|
||||||
|
Blink,
|
||||||
|
TxLevel2P4,
|
||||||
|
TxLevel1P0,
|
||||||
|
Master,
|
||||||
|
Slave,
|
||||||
|
IncompatiableLinkCfg,
|
||||||
|
AnLinkGood,
|
||||||
|
AnComplete,
|
||||||
|
TsTimer,
|
||||||
|
LocRcvrStatus,
|
||||||
|
RemRcvrStatus,
|
||||||
|
Clk25Ref,
|
||||||
|
TxTCLK,
|
||||||
|
Clk120MHz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LedFunc> for u8 {
|
||||||
|
fn from(val: LedFunc) -> Self {
|
||||||
|
val as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for LedFunc {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => LedFunc::LinkupTxRxActicity,
|
||||||
|
1 => LedFunc::LinkupTxActicity,
|
||||||
|
2 => LedFunc::LinkupRxActicity,
|
||||||
|
3 => LedFunc::LinkupOnly,
|
||||||
|
4 => LedFunc::TxRxActivity,
|
||||||
|
5 => LedFunc::TxActivity,
|
||||||
|
6 => LedFunc::RxActivity,
|
||||||
|
7 => LedFunc::LinkupRxEr,
|
||||||
|
8 => LedFunc::LinkupRxTxEr,
|
||||||
|
9 => LedFunc::RxEr,
|
||||||
|
10 => LedFunc::RxTxEr,
|
||||||
|
11 => LedFunc::TxSop,
|
||||||
|
12 => LedFunc::RxSop,
|
||||||
|
13 => LedFunc::On,
|
||||||
|
14 => LedFunc::Off,
|
||||||
|
15 => LedFunc::Blink,
|
||||||
|
16 => LedFunc::TxLevel2P4,
|
||||||
|
17 => LedFunc::TxLevel1P0,
|
||||||
|
18 => LedFunc::Master,
|
||||||
|
19 => LedFunc::Slave,
|
||||||
|
20 => LedFunc::IncompatiableLinkCfg,
|
||||||
|
21 => LedFunc::AnLinkGood,
|
||||||
|
22 => LedFunc::AnComplete,
|
||||||
|
23 => LedFunc::TsTimer,
|
||||||
|
24 => LedFunc::LocRcvrStatus,
|
||||||
|
25 => LedFunc::RemRcvrStatus,
|
||||||
|
26 => LedFunc::Clk25Ref,
|
||||||
|
27 => LedFunc::TxTCLK,
|
||||||
|
28 => LedFunc::Clk120MHz,
|
||||||
|
e => panic!("Invalid value {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// LED Control Register
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct LedCntrl(pub u16);
|
||||||
|
bitfield_bitrange! {struct LedCntrl(u16)}
|
||||||
|
|
||||||
|
impl LedCntrl {
|
||||||
|
bitfield_fields! {
|
||||||
|
u8;
|
||||||
|
/// LED 0 Pin Function
|
||||||
|
pub from into LedFunc, led0_function, set_led0_function: 4, 0;
|
||||||
|
/// LED 0 Mode Selection
|
||||||
|
pub led0_mode, set_led0_mode: 5;
|
||||||
|
/// Qualify Certain LED 0 Options with Link Status.
|
||||||
|
pub led0_link_st_qualify, set_led0_link_st_qualify: 6;
|
||||||
|
/// LED 0 Enable
|
||||||
|
pub led0_en, set_led0_en: 7;
|
||||||
|
/// LED 1 Pin Function
|
||||||
|
pub from into LedFunc, led1_function, set_led1_function: 12, 8;
|
||||||
|
/// /// LED 1 Mode Selection
|
||||||
|
pub led1_mode, set_led1_mode: 13;
|
||||||
|
/// Qualify Certain LED 1 Options with Link Status.
|
||||||
|
pub led1_link_st_qualify, set_led1_link_st_qualify: 14;
|
||||||
|
/// LED 1 Enable
|
||||||
|
pub led1_en, set_led1_en: 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
LedCntrl(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LED Polarity
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum LedPol {
|
||||||
|
AutoSense = 0,
|
||||||
|
ActiveHigh,
|
||||||
|
ActiveLow,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LedPol> for u8 {
|
||||||
|
fn from(val: LedPol) -> Self {
|
||||||
|
val as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for LedPol {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => LedPol::AutoSense,
|
||||||
|
1 => LedPol::ActiveHigh,
|
||||||
|
2 => LedPol::ActiveLow,
|
||||||
|
e => panic!("Invalid value {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// LED Control Register
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct LedPolarity(pub u16);
|
||||||
|
bitfield_bitrange! {struct LedPolarity(u16)}
|
||||||
|
|
||||||
|
impl LedPolarity {
|
||||||
|
bitfield_fields! {
|
||||||
|
u8;
|
||||||
|
/// LED 1 Polarity
|
||||||
|
pub from into LedPol, led1_polarity, set_led1_polarity: 3, 2;
|
||||||
|
/// LED 0 Polarity
|
||||||
|
pub from into LedPol, led0_polarity, set_led0_polarity: 1, 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SPI Header
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct SpiHeader(pub u16);
|
||||||
|
bitfield_bitrange! {struct SpiHeader(u16)}
|
||||||
|
|
||||||
|
impl SpiHeader {
|
||||||
|
bitfield_fields! {
|
||||||
|
u16;
|
||||||
|
/// Mask Bit for TXF_ECC_ERR
|
||||||
|
pub control, set_control : 15;
|
||||||
|
pub full_duplex, set_full_duplex : 14;
|
||||||
|
/// Read or Write to register
|
||||||
|
pub write, set_write : 13;
|
||||||
|
/// Registers ID/addr
|
||||||
|
pub from into SpiRegisters, addr, set_addr: 11, 0;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ use core::cell::RefCell;
|
|||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
|
use driver::HardwareAddress;
|
||||||
pub use embassy_net_driver as driver;
|
pub use embassy_net_driver as driver;
|
||||||
use embassy_net_driver::{Capabilities, LinkState, Medium};
|
use embassy_net_driver::{Capabilities, LinkState, Medium};
|
||||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||||
@ -73,6 +74,18 @@ impl<'d, const MTU: usize> Runner<'d, MTU> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn borrow_split(&mut self) -> (StateRunner<'_>, RxRunner<'_, MTU>, TxRunner<'_, MTU>) {
|
||||||
|
(
|
||||||
|
StateRunner { shared: self.shared },
|
||||||
|
RxRunner {
|
||||||
|
rx_chan: self.rx_chan.borrow(),
|
||||||
|
},
|
||||||
|
TxRunner {
|
||||||
|
tx_chan: self.tx_chan.borrow(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn state_runner(&self) -> StateRunner<'d> {
|
pub fn state_runner(&self) -> StateRunner<'d> {
|
||||||
StateRunner { shared: self.shared }
|
StateRunner { shared: self.shared }
|
||||||
}
|
}
|
||||||
@ -218,7 +231,11 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
|||||||
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
|
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
|
||||||
let mut caps = Capabilities::default();
|
let mut caps = Capabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
caps.max_transmission_unit = MTU;
|
||||||
caps.medium = Medium::Ethernet;
|
caps.medium = match &hardware_address {
|
||||||
|
HardwareAddress::Ethernet(_) => Medium::Ethernet,
|
||||||
|
HardwareAddress::Ieee802154(_) => Medium::Ieee802154,
|
||||||
|
HardwareAddress::Ip => Medium::Ip,
|
||||||
|
};
|
||||||
|
|
||||||
// safety: this is a self-referential struct, however:
|
// safety: this is a self-referential struct, however:
|
||||||
// - it can't move while the `'d` borrow is active.
|
// - it can't move while the `'d` borrow is active.
|
||||||
|
@ -11,7 +11,7 @@ edition = "2021"
|
|||||||
embedded-hal = { version = "1.0.0-rc.1" }
|
embedded-hal = { version = "1.0.0-rc.1" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time" }
|
embassy-time = { version = "0.1.3", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time" }
|
embassy-time = { version = "0.1.3", path = "../embassy-time" }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
||||||
|
@ -82,7 +82,7 @@ impl<'a> Control<'a> {
|
|||||||
pub async fn disconnect(&mut self) -> Result<(), Error> {
|
pub async fn disconnect(&mut self) -> Result<(), Error> {
|
||||||
let req = proto::CtrlMsgReqGetStatus {};
|
let req = proto::CtrlMsgReqGetStatus {};
|
||||||
ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp);
|
ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp);
|
||||||
self.state_ch.set_link_state(LinkState::Up);
|
self.state_ch.set_link_state(LinkState::Down);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ macro_rules! unreachable {
|
|||||||
#[cfg(feature = "defmt")]
|
#[cfg(feature = "defmt")]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
::defmt::unreachable!($($x)*);
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +229,8 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Bytes<'a>(pub &'a [u8]);
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
impl<'a> Debug for Bytes<'a> {
|
impl<'a> Debug for Bytes<'a> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
28
embassy-net-ppp/Cargo.toml
Normal file
28
embassy-net-ppp/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
name = "embassy-net-ppp"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "embassy-net driver for PPP over Serial"
|
||||||
|
keywords = ["embedded", "ppp", "embassy-net", "embedded-hal-async", "ethernet", "async"]
|
||||||
|
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
defmt = ["dep:defmt", "ppproto/defmt"]
|
||||||
|
log = ["dep:log", "ppproto/log"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
|
embedded-io-async = { version = "0.5.0" }
|
||||||
|
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
||||||
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
|
ppproto = { version = "0.1.2"}
|
||||||
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
|
|
||||||
|
[package.metadata.embassy_docs]
|
||||||
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-ppp-v$VERSION/embassy-net-ppp/src/"
|
||||||
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-ppp/src/"
|
||||||
|
target = "thumbv7em-none-eabi"
|
||||||
|
features = ["defmt"]
|
19
embassy-net-ppp/README.md
Normal file
19
embassy-net-ppp/README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# `embassy-net-ppp`
|
||||||
|
|
||||||
|
[`embassy-net`](https://crates.io/crates/embassy-net) integration for PPP over Serial.
|
||||||
|
|
||||||
|
## Interoperability
|
||||||
|
|
||||||
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
It supports any serial port implementing [`embedded-io-async`](https://crates.io/crates/embedded-io-async).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is 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.
|
258
embassy-net-ppp/src/fmt.rs
Normal file
258
embassy-net-ppp/src/fmt.rs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
#![macro_use]
|
||||||
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
|
macro_rules! assert {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::assert!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::assert!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! assert_eq {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::assert_eq!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::assert_eq!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! assert_ne {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::assert_ne!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::assert_ne!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! debug_assert {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::debug_assert!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::debug_assert!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! debug_assert_eq {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::debug_assert_eq!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::debug_assert_eq!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! debug_assert_ne {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::debug_assert_ne!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::debug_assert_ne!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! todo {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::todo!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::todo!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::core::unreachable!($($x)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! panic {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
::core::panic!($($x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::panic!($($x)*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! trace {
|
||||||
|
($s:literal $(, $x:expr)* $(,)?) => {
|
||||||
|
{
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
::log::trace!($s $(, $x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::trace!($s $(, $x)*);
|
||||||
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
|
let _ = ($( & $x ),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! debug {
|
||||||
|
($s:literal $(, $x:expr)* $(,)?) => {
|
||||||
|
{
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
::log::debug!($s $(, $x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::debug!($s $(, $x)*);
|
||||||
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
|
let _ = ($( & $x ),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! info {
|
||||||
|
($s:literal $(, $x:expr)* $(,)?) => {
|
||||||
|
{
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
::log::info!($s $(, $x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::info!($s $(, $x)*);
|
||||||
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
|
let _ = ($( & $x ),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! warn {
|
||||||
|
($s:literal $(, $x:expr)* $(,)?) => {
|
||||||
|
{
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
::log::warn!($s $(, $x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::warn!($s $(, $x)*);
|
||||||
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
|
let _ = ($( & $x ),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! error {
|
||||||
|
($s:literal $(, $x:expr)* $(,)?) => {
|
||||||
|
{
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
::log::error!($s $(, $x)*);
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
::defmt::error!($s $(, $x)*);
|
||||||
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
|
let _ = ($( & $x ),*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
macro_rules! unwrap {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unwrap!($($x)*)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
macro_rules! unwrap {
|
||||||
|
($arg:expr) => {
|
||||||
|
match $crate::fmt::Try::into_result($arg) {
|
||||||
|
::core::result::Result::Ok(t) => t,
|
||||||
|
::core::result::Result::Err(e) => {
|
||||||
|
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||||
|
match $crate::fmt::Try::into_result($arg) {
|
||||||
|
::core::result::Result::Ok(t) => t,
|
||||||
|
::core::result::Result::Err(e) => {
|
||||||
|
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub struct NoneError;
|
||||||
|
|
||||||
|
pub trait Try {
|
||||||
|
type Ok;
|
||||||
|
type Error;
|
||||||
|
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Try for Option<T> {
|
||||||
|
type Ok = T;
|
||||||
|
type Error = NoneError;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_result(self) -> Result<T, NoneError> {
|
||||||
|
self.ok_or(NoneError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> Try for Result<T, E> {
|
||||||
|
type Ok = T;
|
||||||
|
type Error = E;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_result(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
185
embassy-net-ppp/src/lib.rs
Normal file
185
embassy-net-ppp/src/lib.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
|
// must be first
|
||||||
|
mod fmt;
|
||||||
|
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
|
use embassy_futures::select::{select, Either};
|
||||||
|
use embassy_net_driver_channel as ch;
|
||||||
|
use embassy_net_driver_channel::driver::LinkState;
|
||||||
|
use embedded_io_async::{BufRead, Write, WriteAllError};
|
||||||
|
use ppproto::pppos::{BufferFullError, PPPoS, PPPoSAction};
|
||||||
|
pub use ppproto::{Config, Ipv4Status};
|
||||||
|
|
||||||
|
const MTU: usize = 1500;
|
||||||
|
|
||||||
|
/// Type alias for the embassy-net driver.
|
||||||
|
pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;
|
||||||
|
|
||||||
|
/// Internal state for the embassy-net integration.
|
||||||
|
pub struct State<const N_RX: usize, const N_TX: usize> {
|
||||||
|
ch_state: ch::State<MTU, N_RX, N_TX>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {
|
||||||
|
/// Create a new `State`.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
ch_state: ch::State::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Background runner for the driver.
|
||||||
|
///
|
||||||
|
/// You must call `.run()` in a background task for the driver to operate.
|
||||||
|
pub struct Runner<'d> {
|
||||||
|
ch: ch::Runner<'d, MTU>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error returned by [`Runner::run`].
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum RunError<E> {
|
||||||
|
/// Reading from the serial port failed.
|
||||||
|
Read(E),
|
||||||
|
/// Writing to the serial port failed.
|
||||||
|
Write(E),
|
||||||
|
/// Writing to the serial port wrote zero bytes, indicating it can't accept more data.
|
||||||
|
WriteZero,
|
||||||
|
/// Writing to the serial got EOF.
|
||||||
|
Eof,
|
||||||
|
/// PPP protocol was terminated by the peer
|
||||||
|
Terminated,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> From<WriteAllError<E>> for RunError<E> {
|
||||||
|
fn from(value: WriteAllError<E>) -> Self {
|
||||||
|
match value {
|
||||||
|
WriteAllError::Other(e) => Self::Write(e),
|
||||||
|
WriteAllError::WriteZero => Self::WriteZero,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Runner<'d> {
|
||||||
|
/// You must call this in a background task for the driver to operate.
|
||||||
|
///
|
||||||
|
/// If reading/writing to the underlying serial port fails, the link state
|
||||||
|
/// is set to Down and the error is returned.
|
||||||
|
///
|
||||||
|
/// It is allowed to cancel this function's future (i.e. drop it). This will terminate
|
||||||
|
/// the PPP connection and set the link state to Down.
|
||||||
|
///
|
||||||
|
/// After this function returns or is canceled, you can call it again to establish
|
||||||
|
/// a new PPP connection.
|
||||||
|
pub async fn run<RW: BufRead + Write>(
|
||||||
|
&mut self,
|
||||||
|
mut rw: RW,
|
||||||
|
config: ppproto::Config<'_>,
|
||||||
|
mut on_ipv4_up: impl FnMut(Ipv4Status),
|
||||||
|
) -> Result<Infallible, RunError<RW::Error>> {
|
||||||
|
let mut ppp = PPPoS::new(config);
|
||||||
|
ppp.open().unwrap();
|
||||||
|
|
||||||
|
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.borrow_split();
|
||||||
|
state_chan.set_link_state(LinkState::Down);
|
||||||
|
let _ondrop = OnDrop::new(|| state_chan.set_link_state(LinkState::Down));
|
||||||
|
|
||||||
|
let mut rx_buf = [0; 2048];
|
||||||
|
let mut tx_buf = [0; 2048];
|
||||||
|
|
||||||
|
let mut needs_poll = true;
|
||||||
|
let mut was_up = false;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let rx_fut = async {
|
||||||
|
let buf = rx_chan.rx_buf().await;
|
||||||
|
let rx_data = match needs_poll {
|
||||||
|
true => &[][..],
|
||||||
|
false => match rw.fill_buf().await {
|
||||||
|
Ok(rx_data) if rx_data.len() == 0 => return Err(RunError::Eof),
|
||||||
|
Ok(rx_data) => rx_data,
|
||||||
|
Err(e) => return Err(RunError::Read(e)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok((buf, rx_data))
|
||||||
|
};
|
||||||
|
let tx_fut = tx_chan.tx_buf();
|
||||||
|
match select(rx_fut, tx_fut).await {
|
||||||
|
Either::First(r) => {
|
||||||
|
needs_poll = false;
|
||||||
|
|
||||||
|
let (buf, rx_data) = r?;
|
||||||
|
let n = ppp.consume(rx_data, &mut rx_buf);
|
||||||
|
rw.consume(n);
|
||||||
|
|
||||||
|
match ppp.poll(&mut tx_buf, &mut rx_buf) {
|
||||||
|
PPPoSAction::None => {}
|
||||||
|
PPPoSAction::Received(rg) => {
|
||||||
|
let pkt = &rx_buf[rg];
|
||||||
|
buf[..pkt.len()].copy_from_slice(pkt);
|
||||||
|
rx_chan.rx_done(pkt.len());
|
||||||
|
}
|
||||||
|
PPPoSAction::Transmit(n) => rw.write_all(&tx_buf[..n]).await?,
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = ppp.status();
|
||||||
|
match status.phase {
|
||||||
|
ppproto::Phase::Dead => {
|
||||||
|
return Err(RunError::Terminated);
|
||||||
|
}
|
||||||
|
ppproto::Phase::Open => {
|
||||||
|
if !was_up {
|
||||||
|
on_ipv4_up(status.ipv4.unwrap());
|
||||||
|
}
|
||||||
|
was_up = true;
|
||||||
|
state_chan.set_link_state(LinkState::Up);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
was_up = false;
|
||||||
|
state_chan.set_link_state(LinkState::Down);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Either::Second(pkt) => {
|
||||||
|
match ppp.send(pkt, &mut tx_buf) {
|
||||||
|
Ok(n) => rw.write_all(&tx_buf[..n]).await?,
|
||||||
|
Err(BufferFullError) => unreachable!(),
|
||||||
|
}
|
||||||
|
tx_chan.tx_done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a PPP embassy-net driver instance.
|
||||||
|
///
|
||||||
|
/// This returns two structs:
|
||||||
|
/// - a `Device` that you must pass to the `embassy-net` stack.
|
||||||
|
/// - a `Runner`. You must call `.run()` on it in a background task.
|
||||||
|
pub fn new<'a, const N_RX: usize, const N_TX: usize>(state: &'a mut State<N_RX, N_TX>) -> (Device<'a>, Runner<'a>) {
|
||||||
|
let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ip);
|
||||||
|
(device, Runner { ch: runner })
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OnDrop<F: FnOnce()> {
|
||||||
|
f: MaybeUninit<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce()> OnDrop<F> {
|
||||||
|
fn new(f: F) -> Self {
|
||||||
|
Self { f: MaybeUninit::new(f) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { self.f.as_ptr().read()() }
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ edition = "2021"
|
|||||||
embedded-hal = { version = "1.0.0-rc.1" }
|
embedded-hal = { version = "1.0.0-rc.1" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time" }
|
embassy-time = { version = "0.1.3", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ categories = [
|
|||||||
"embedded",
|
"embedded",
|
||||||
"no-std",
|
"no-std",
|
||||||
"asynchronous",
|
"asynchronous",
|
||||||
|
"network-programming",
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
@ -50,7 +51,7 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time" }
|
embassy-time = { version = "0.1.3", path = "../embassy-time" }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embedded-io-async = { version = "0.5.0", optional = true }
|
embedded-io-async = { version = "0.5.0", optional = true }
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
|
#[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))]
|
||||||
|
compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6");
|
||||||
|
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
|
|
||||||
@ -20,7 +23,7 @@ use core::future::{poll_fn, Future};
|
|||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
pub use embassy_net_driver as driver;
|
pub use embassy_net_driver as driver;
|
||||||
use embassy_net_driver::{Driver, LinkState, Medium};
|
use embassy_net_driver::{Driver, LinkState};
|
||||||
use embassy_sync::waitqueue::WakerRegistration;
|
use embassy_sync::waitqueue::WakerRegistration;
|
||||||
use embassy_time::{Instant, Timer};
|
use embassy_time::{Instant, Timer};
|
||||||
use futures::pin_mut;
|
use futures::pin_mut;
|
||||||
@ -133,6 +136,8 @@ impl Default for DhcpConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Network stack configuration.
|
/// Network stack configuration.
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// IPv4 configuration
|
/// IPv4 configuration
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
@ -181,23 +186,27 @@ impl Config {
|
|||||||
|
|
||||||
/// Network stack IPv4 configuration.
|
/// Network stack IPv4 configuration.
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
pub enum ConfigV4 {
|
pub enum ConfigV4 {
|
||||||
|
/// Do not configure IPv4.
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
/// Use a static IPv4 address configuration.
|
/// Use a static IPv4 address configuration.
|
||||||
Static(StaticConfigV4),
|
Static(StaticConfigV4),
|
||||||
/// Use DHCP to obtain an IP address configuration.
|
/// Use DHCP to obtain an IP address configuration.
|
||||||
#[cfg(feature = "dhcpv4")]
|
#[cfg(feature = "dhcpv4")]
|
||||||
Dhcp(DhcpConfig),
|
Dhcp(DhcpConfig),
|
||||||
/// Do not configure IPv6.
|
|
||||||
None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Network stack IPv6 configuration.
|
/// Network stack IPv6 configuration.
|
||||||
#[cfg(feature = "proto-ipv6")]
|
#[cfg(feature = "proto-ipv6")]
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
pub enum ConfigV6 {
|
pub enum ConfigV6 {
|
||||||
|
/// Do not configure IPv6.
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
/// Use a static IPv6 address configuration.
|
/// Use a static IPv6 address configuration.
|
||||||
Static(StaticConfigV6),
|
Static(StaticConfigV6),
|
||||||
/// Do not configure IPv6.
|
|
||||||
None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A network stack.
|
/// A network stack.
|
||||||
@ -240,7 +249,10 @@ fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress
|
|||||||
driver::HardwareAddress::Ip => HardwareAddress::Ip,
|
driver::HardwareAddress::Ip => HardwareAddress::Ip,
|
||||||
|
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => panic!("Unsupported address {:?}. Make sure to enable medium-ethernet or medium-ieee802154 in embassy-net's Cargo features.", addr),
|
_ => panic!(
|
||||||
|
"Unsupported medium {:?}. Make sure to enable the right medium feature in embassy-net's Cargo features.",
|
||||||
|
addr
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +288,6 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
next_local_port,
|
next_local_port,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
|
|
||||||
let mut inner = Inner {
|
let mut inner = Inner {
|
||||||
device,
|
device,
|
||||||
link_up: false,
|
link_up: false,
|
||||||
@ -295,30 +306,11 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
dns_waker: WakerRegistration::new(),
|
dns_waker: WakerRegistration::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "medium-ieee802154")]
|
|
||||||
let _ = config;
|
|
||||||
|
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
match config.ipv4 {
|
inner.set_config_v4(&mut socket, config.ipv4);
|
||||||
ConfigV4::Static(config) => {
|
|
||||||
inner.apply_config_v4(&mut socket, config);
|
|
||||||
}
|
|
||||||
#[cfg(feature = "dhcpv4")]
|
|
||||||
ConfigV4::Dhcp(config) => {
|
|
||||||
let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new();
|
|
||||||
inner.apply_dhcp_config(&mut dhcp_socket, config);
|
|
||||||
let handle = socket.sockets.add(dhcp_socket);
|
|
||||||
inner.dhcp_socket = Some(handle);
|
|
||||||
}
|
|
||||||
ConfigV4::None => {}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "proto-ipv6")]
|
#[cfg(feature = "proto-ipv6")]
|
||||||
match config.ipv6 {
|
inner.set_config_v6(&mut socket, config.ipv6);
|
||||||
ConfigV6::Static(config) => {
|
inner.apply_static_config(&mut socket);
|
||||||
inner.apply_config_v6(&mut socket, config);
|
|
||||||
}
|
|
||||||
ConfigV6::None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
socket: RefCell::new(socket),
|
socket: RefCell::new(socket),
|
||||||
@ -372,15 +364,36 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current IPv4 configuration.
|
/// Get the current IPv4 configuration.
|
||||||
|
///
|
||||||
|
/// If using DHCP, this will be None if DHCP hasn't been able to
|
||||||
|
/// acquire an IP address, or Some if it has.
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
pub fn config_v4(&self) -> Option<StaticConfigV4> {
|
pub fn config_v4(&self) -> Option<StaticConfigV4> {
|
||||||
self.with(|_s, i| i.static_v4.clone())
|
self.with(|_, i| i.static_v4.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current IPv6 configuration.
|
/// Get the current IPv6 configuration.
|
||||||
#[cfg(feature = "proto-ipv6")]
|
#[cfg(feature = "proto-ipv6")]
|
||||||
pub fn config_v6(&self) -> Option<StaticConfigV6> {
|
pub fn config_v6(&self) -> Option<StaticConfigV6> {
|
||||||
self.with(|_s, i| i.static_v6.clone())
|
self.with(|_, i| i.static_v6.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the IPv4 configuration.
|
||||||
|
#[cfg(feature = "proto-ipv4")]
|
||||||
|
pub fn set_config_v4(&self, config: ConfigV4) {
|
||||||
|
self.with_mut(|s, i| {
|
||||||
|
i.set_config_v4(s, config);
|
||||||
|
i.apply_static_config(s);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the IPv6 configuration.
|
||||||
|
#[cfg(feature = "proto-ipv6")]
|
||||||
|
pub fn set_config_v6(&self, config: ConfigV6) {
|
||||||
|
self.with_mut(|s, i| {
|
||||||
|
i.set_config_v6(s, config);
|
||||||
|
i.apply_static_config(s);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the network stack.
|
/// Run the network stack.
|
||||||
@ -582,166 +595,125 @@ impl SocketStack {
|
|||||||
|
|
||||||
impl<D: Driver + 'static> Inner<D> {
|
impl<D: Driver + 'static> Inner<D> {
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) {
|
pub fn set_config_v4(&mut self, _s: &mut SocketStack, config: ConfigV4) {
|
||||||
debug!("Acquired IP configuration:");
|
// Handle static config.
|
||||||
|
self.static_v4 = match config.clone() {
|
||||||
|
ConfigV4::None => None,
|
||||||
|
#[cfg(feature = "dhcpv4")]
|
||||||
|
ConfigV4::Dhcp(_) => None,
|
||||||
|
ConfigV4::Static(c) => Some(c),
|
||||||
|
};
|
||||||
|
|
||||||
debug!(" IP address: {}", config.address);
|
// Handle DHCP config.
|
||||||
s.iface.update_ip_addrs(|addrs| {
|
#[cfg(feature = "dhcpv4")]
|
||||||
if let Some((index, _)) = addrs
|
match config {
|
||||||
.iter()
|
ConfigV4::Dhcp(c) => {
|
||||||
.enumerate()
|
// Create the socket if it doesn't exist.
|
||||||
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
|
if self.dhcp_socket.is_none() {
|
||||||
{
|
let socket = smoltcp::socket::dhcpv4::Socket::new();
|
||||||
addrs.remove(index);
|
let handle = _s.sockets.add(socket);
|
||||||
|
self.dhcp_socket = Some(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure it
|
||||||
|
let socket = _s.sockets.get_mut::<dhcpv4::Socket>(self.dhcp_socket.unwrap());
|
||||||
|
socket.set_ignore_naks(c.ignore_naks);
|
||||||
|
socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp));
|
||||||
|
socket.set_ports(c.server_port, c.client_port);
|
||||||
|
socket.set_retry_config(c.retry_config);
|
||||||
|
socket.reset();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Remove DHCP socket if any.
|
||||||
|
if let Some(socket) = self.dhcp_socket {
|
||||||
|
_s.sockets.remove(socket);
|
||||||
|
self.dhcp_socket = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "proto-ipv6")]
|
||||||
|
pub fn set_config_v6(&mut self, _s: &mut SocketStack, config: ConfigV6) {
|
||||||
|
self.static_v6 = match config {
|
||||||
|
ConfigV6::None => None,
|
||||||
|
ConfigV6::Static(c) => Some(c),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_static_config(&mut self, s: &mut SocketStack) {
|
||||||
|
let mut addrs = Vec::new();
|
||||||
|
#[cfg(feature = "dns")]
|
||||||
|
let mut dns_servers: Vec<_, 6> = Vec::new();
|
||||||
|
#[cfg(feature = "proto-ipv4")]
|
||||||
|
let mut gateway_v4 = None;
|
||||||
|
#[cfg(feature = "proto-ipv6")]
|
||||||
|
let mut gateway_v6 = None;
|
||||||
|
|
||||||
|
#[cfg(feature = "proto-ipv4")]
|
||||||
|
if let Some(config) = &self.static_v4 {
|
||||||
|
debug!("IPv4: UP");
|
||||||
|
debug!(" IP address: {:?}", config.address);
|
||||||
|
debug!(" Default gateway: {:?}", config.gateway);
|
||||||
|
|
||||||
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
||||||
});
|
gateway_v4 = config.gateway.into();
|
||||||
|
#[cfg(feature = "dns")]
|
||||||
|
for s in &config.dns_servers {
|
||||||
|
debug!(" DNS server: {:?}", s);
|
||||||
|
dns_servers.push(s.clone().into()).unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("IPv4: DOWN");
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "medium-ip")]
|
#[cfg(feature = "proto-ipv6")]
|
||||||
let skip_gateway = self.device.capabilities().medium != Medium::Ip;
|
if let Some(config) = &self.static_v6 {
|
||||||
#[cfg(not(feature = "medium-ip"))]
|
debug!("IPv6: UP");
|
||||||
let skip_gateway = false;
|
debug!(" IP address: {:?}", config.address);
|
||||||
|
debug!(" Default gateway: {:?}", config.gateway);
|
||||||
|
|
||||||
if !skip_gateway {
|
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
||||||
if let Some(gateway) = config.gateway {
|
gateway_v6 = config.gateway.into();
|
||||||
debug!(" Default gateway: {}", gateway);
|
#[cfg(feature = "dns")]
|
||||||
|
for s in &config.dns_servers {
|
||||||
|
debug!(" DNS server: {:?}", s);
|
||||||
|
dns_servers.push(s.clone().into()).unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("IPv6: DOWN");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply addresses
|
||||||
|
s.iface.update_ip_addrs(|a| *a = addrs);
|
||||||
|
|
||||||
|
// Apply gateways
|
||||||
|
#[cfg(feature = "proto-ipv4")]
|
||||||
|
if let Some(gateway) = gateway_v4 {
|
||||||
s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
|
s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
|
||||||
} else {
|
} else {
|
||||||
debug!(" Default gateway: None");
|
|
||||||
s.iface.routes_mut().remove_default_ipv4_route();
|
s.iface.routes_mut().remove_default_ipv4_route();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for (i, s) in config.dns_servers.iter().enumerate() {
|
|
||||||
debug!(" DNS server {}: {}", i, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.static_v4 = Some(config);
|
|
||||||
|
|
||||||
#[cfg(feature = "dns")]
|
|
||||||
{
|
|
||||||
self.update_dns_servers(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replaces the current IPv6 static configuration with a newly supplied config.
|
|
||||||
#[cfg(feature = "proto-ipv6")]
|
#[cfg(feature = "proto-ipv6")]
|
||||||
fn apply_config_v6(&mut self, s: &mut SocketStack, config: StaticConfigV6) {
|
if let Some(gateway) = gateway_v6 {
|
||||||
#[cfg(feature = "medium-ethernet")]
|
|
||||||
let medium = self.device.capabilities().medium;
|
|
||||||
|
|
||||||
debug!("Acquired IPv6 configuration:");
|
|
||||||
|
|
||||||
debug!(" IP address: {}", config.address);
|
|
||||||
s.iface.update_ip_addrs(|addrs| {
|
|
||||||
if let Some((index, _)) = addrs
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_)))
|
|
||||||
{
|
|
||||||
addrs.remove(index);
|
|
||||||
}
|
|
||||||
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
|
||||||
if Medium::Ethernet == medium {
|
|
||||||
if let Some(gateway) = config.gateway {
|
|
||||||
debug!(" Default gateway: {}", gateway);
|
|
||||||
s.iface.routes_mut().add_default_ipv6_route(gateway).unwrap();
|
s.iface.routes_mut().add_default_ipv6_route(gateway).unwrap();
|
||||||
} else {
|
} else {
|
||||||
debug!(" Default gateway: None");
|
|
||||||
s.iface.routes_mut().remove_default_ipv6_route();
|
s.iface.routes_mut().remove_default_ipv6_route();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for (i, s) in config.dns_servers.iter().enumerate() {
|
|
||||||
debug!(" DNS server {}: {}", i, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.static_v6 = Some(config);
|
|
||||||
|
|
||||||
|
// Apply DNS servers
|
||||||
#[cfg(feature = "dns")]
|
#[cfg(feature = "dns")]
|
||||||
{
|
s.sockets
|
||||||
self.update_dns_servers(s)
|
.get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket)
|
||||||
}
|
.update_servers(&dns_servers[..]);
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "dns")]
|
|
||||||
fn update_dns_servers(&mut self, s: &mut SocketStack) {
|
|
||||||
let socket = s.sockets.get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket);
|
|
||||||
|
|
||||||
let servers_v4;
|
|
||||||
#[cfg(feature = "proto-ipv4")]
|
|
||||||
{
|
|
||||||
servers_v4 = self
|
|
||||||
.static_v4
|
|
||||||
.iter()
|
|
||||||
.flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)));
|
|
||||||
};
|
|
||||||
#[cfg(not(feature = "proto-ipv4"))]
|
|
||||||
{
|
|
||||||
servers_v4 = core::iter::empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
let servers_v6;
|
|
||||||
#[cfg(feature = "proto-ipv6")]
|
|
||||||
{
|
|
||||||
servers_v6 = self
|
|
||||||
.static_v6
|
|
||||||
.iter()
|
|
||||||
.flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv6(*c)));
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "proto-ipv6"))]
|
|
||||||
{
|
|
||||||
servers_v6 = core::iter::empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the v6 DNS servers over the v4 servers
|
|
||||||
let servers: Vec<IpAddress, 6> = servers_v6.chain(servers_v4).collect();
|
|
||||||
socket.update_servers(&servers[..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "dhcpv4")]
|
|
||||||
fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) {
|
|
||||||
socket.set_ignore_naks(config.ignore_naks);
|
|
||||||
socket.set_max_lease_duration(config.max_lease_duration.map(crate::time::duration_to_smoltcp));
|
|
||||||
socket.set_ports(config.server_port, config.client_port);
|
|
||||||
socket.set_retry_config(config.retry_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "dhcpv4")]
|
|
||||||
fn unapply_config_v4(&mut self, s: &mut SocketStack) {
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
|
||||||
let medium = self.device.capabilities().medium;
|
|
||||||
debug!("Lost IP configuration");
|
|
||||||
s.iface.update_ip_addrs(|ip_addrs| {
|
|
||||||
#[cfg(feature = "proto-ipv4")]
|
|
||||||
if let Some((index, _)) = ip_addrs
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
|
|
||||||
{
|
|
||||||
ip_addrs.remove(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
|
||||||
if medium == Medium::Ethernet {
|
|
||||||
#[cfg(feature = "proto-ipv4")]
|
|
||||||
{
|
|
||||||
s.iface.routes_mut().remove_default_ipv4_route();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "proto-ipv4")]
|
|
||||||
{
|
|
||||||
self.static_v4 = None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
|
fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
|
||||||
s.waker.register(cx.waker());
|
s.waker.register(cx.waker());
|
||||||
|
|
||||||
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
|
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
|
||||||
if self.device.capabilities().medium == Medium::Ethernet
|
if self.device.capabilities().medium == embassy_net_driver::Medium::Ethernet
|
||||||
|| self.device.capabilities().medium == Medium::Ieee802154
|
|| self.device.capabilities().medium == embassy_net_driver::Medium::Ieee802154
|
||||||
{
|
{
|
||||||
s.iface
|
s.iface
|
||||||
.set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address()));
|
.set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address()));
|
||||||
@ -763,6 +735,9 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
info!("link_up = {:?}", self.link_up);
|
info!("link_up = {:?}", self.link_up);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut apply_config = false;
|
||||||
|
|
||||||
#[cfg(feature = "dhcpv4")]
|
#[cfg(feature = "dhcpv4")]
|
||||||
if let Some(dhcp_handle) = self.dhcp_socket {
|
if let Some(dhcp_handle) = self.dhcp_socket {
|
||||||
let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
|
let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
|
||||||
@ -770,25 +745,29 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
if self.link_up {
|
if self.link_up {
|
||||||
match socket.poll() {
|
match socket.poll() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s),
|
Some(dhcpv4::Event::Deconfigured) => {
|
||||||
|
self.static_v4 = None;
|
||||||
|
apply_config = true;
|
||||||
|
}
|
||||||
Some(dhcpv4::Event::Configured(config)) => {
|
Some(dhcpv4::Event::Configured(config)) => {
|
||||||
let config = StaticConfigV4 {
|
self.static_v4 = Some(StaticConfigV4 {
|
||||||
address: config.address,
|
address: config.address,
|
||||||
gateway: config.router,
|
gateway: config.router,
|
||||||
dns_servers: config.dns_servers,
|
dns_servers: config.dns_servers,
|
||||||
};
|
});
|
||||||
self.apply_config_v4(s, config)
|
apply_config = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if old_link_up {
|
} else if old_link_up {
|
||||||
socket.reset();
|
socket.reset();
|
||||||
self.unapply_config_v4(s);
|
self.static_v4 = None;
|
||||||
|
apply_config = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//if old_link_up || self.link_up {
|
|
||||||
// self.poll_configurator(timestamp)
|
if apply_config {
|
||||||
//}
|
self.apply_static_config(s);
|
||||||
//
|
}
|
||||||
|
|
||||||
if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) {
|
if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) {
|
||||||
let t = Timer::at(instant_from_smoltcp(poll_at));
|
let t = Timer::at(instant_from_smoltcp(poll_at));
|
||||||
|
@ -91,7 +91,7 @@ _dppi = []
|
|||||||
_gpio-p1 = []
|
_gpio-p1 = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
|
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
|
||||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -60,7 +60,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] }
|
embassy-time = { version = "0.1.3", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
|
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
|
||||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
@ -95,5 +95,5 @@ pio = {version= "0.2.1" }
|
|||||||
rp2040-boot2 = "0.3"
|
rp2040-boot2 = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] }
|
embassy-executor = { version = "0.3.0", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] }
|
||||||
static_cell = "1.1"
|
static_cell = "1.1"
|
||||||
|
@ -94,6 +94,7 @@ impl ClockConfig {
|
|||||||
post_div1: 6,
|
post_div1: 6,
|
||||||
post_div2: 5,
|
post_div2: 5,
|
||||||
}),
|
}),
|
||||||
|
delay_multiplier: 128,
|
||||||
}),
|
}),
|
||||||
ref_clk: RefClkConfig {
|
ref_clk: RefClkConfig {
|
||||||
src: RefClkSrc::Xosc,
|
src: RefClkSrc::Xosc,
|
||||||
@ -203,6 +204,7 @@ pub struct XoscConfig {
|
|||||||
pub hz: u32,
|
pub hz: u32,
|
||||||
pub sys_pll: Option<PllConfig>,
|
pub sys_pll: Option<PllConfig>,
|
||||||
pub usb_pll: Option<PllConfig>,
|
pub usb_pll: Option<PllConfig>,
|
||||||
|
pub delay_multiplier: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PllConfig {
|
pub struct PllConfig {
|
||||||
@ -363,7 +365,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
|||||||
// start XOSC
|
// start XOSC
|
||||||
// datasheet mentions support for clock inputs into XIN, but doesn't go into
|
// datasheet mentions support for clock inputs into XIN, but doesn't go into
|
||||||
// how this is achieved. pico-sdk doesn't support this at all.
|
// how this is achieved. pico-sdk doesn't support this at all.
|
||||||
start_xosc(config.hz);
|
start_xosc(config.hz, config.delay_multiplier);
|
||||||
|
|
||||||
let pll_sys_freq = match config.sys_pll {
|
let pll_sys_freq = match config.sys_pll {
|
||||||
Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config),
|
Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config),
|
||||||
@ -624,12 +626,12 @@ pub fn clk_rtc_freq() -> u16 {
|
|||||||
CLOCKS.rtc.load(Ordering::Relaxed)
|
CLOCKS.rtc.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_xosc(crystal_hz: u32) {
|
fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
|
||||||
pac::XOSC
|
pac::XOSC
|
||||||
.ctrl()
|
.ctrl()
|
||||||
.write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ));
|
.write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ));
|
||||||
|
|
||||||
let startup_delay = ((crystal_hz / 1000) + 128) / 256;
|
let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256;
|
||||||
pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16));
|
pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16));
|
||||||
pac::XOSC.ctrl().write(|w| {
|
pac::XOSC.ctrl().write(|w| {
|
||||||
w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ);
|
w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ features = ["stm32wb55rg"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" }
|
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" }
|
embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" }
|
||||||
embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
|
embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -28,7 +28,9 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> {
|
|||||||
type TxToken<'a> = TxToken<'d> where Self: 'a;
|
type TxToken<'a> = TxToken<'d> where Self: 'a;
|
||||||
|
|
||||||
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||||
if self.runner.rx_channel.poll_ready_to_receive(cx) && self.runner.tx_buf_channel.poll_ready_to_receive(cx) {
|
if self.runner.rx_channel.poll_ready_to_receive(cx).is_ready()
|
||||||
|
&& self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready()
|
||||||
|
{
|
||||||
Some((
|
Some((
|
||||||
RxToken {
|
RxToken {
|
||||||
rx: &self.runner.rx_channel,
|
rx: &self.runner.rx_channel,
|
||||||
@ -44,7 +46,7 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
|
fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
|
||||||
if self.runner.tx_buf_channel.poll_ready_to_receive(cx) {
|
if self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() {
|
||||||
Some(TxToken {
|
Some(TxToken {
|
||||||
tx: &self.runner.tx_channel,
|
tx: &self.runner.tx_channel,
|
||||||
tx_buf: &self.runner.tx_buf_channel,
|
tx_buf: &self.runner.tx_buf_channel,
|
||||||
@ -91,7 +93,7 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> {
|
|||||||
{
|
{
|
||||||
// Only valid data events should be put into the queue
|
// Only valid data events should be put into the queue
|
||||||
|
|
||||||
let data_event = match self.rx.try_recv().unwrap() {
|
let data_event = match self.rx.try_receive().unwrap() {
|
||||||
MacEvent::McpsDataInd(data_event) => data_event,
|
MacEvent::McpsDataInd(data_event) => data_event,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
@ -111,7 +113,7 @@ impl<'d> embassy_net_driver::TxToken for TxToken<'d> {
|
|||||||
F: FnOnce(&mut [u8]) -> R,
|
F: FnOnce(&mut [u8]) -> R,
|
||||||
{
|
{
|
||||||
// Only valid tx buffers should be put into the queue
|
// Only valid tx buffers should be put into the queue
|
||||||
let buf = self.tx_buf.try_recv().unwrap();
|
let buf = self.tx_buf.try_receive().unwrap();
|
||||||
let r = f(&mut buf[..len]);
|
let r = f(&mut buf[..len]);
|
||||||
|
|
||||||
// The tx channel should always be of equal capacity to the tx_buf channel
|
// The tx channel should always be of equal capacity to the tx_buf channel
|
||||||
|
@ -73,7 +73,7 @@ impl<'a> Runner<'a> {
|
|||||||
let mut msdu_handle = 0x02;
|
let mut msdu_handle = 0x02;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (buf, len) = self.tx_channel.recv().await;
|
let (buf, len) = self.tx_channel.receive().await;
|
||||||
let _wm = self.write_mutex.lock().await;
|
let _wm = self.write_mutex.lock().await;
|
||||||
|
|
||||||
// The mutex should be dropped on the next loop iteration
|
// The mutex should be dropped on the next loop iteration
|
||||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/"
|
||||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/"
|
||||||
|
|
||||||
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"]
|
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time", "low-power"]
|
||||||
flavors = [
|
flavors = [
|
||||||
{ regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" },
|
{ regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" },
|
||||||
{ regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" },
|
{ regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" },
|
||||||
@ -32,12 +32,13 @@ flavors = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
|
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
|
||||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
||||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
|
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
|
||||||
|
embassy-executor = { version = "0.3.0", path = "../embassy-executor", optional = true }
|
||||||
|
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
|
||||||
@ -57,7 +58,7 @@ sdio-host = "0.5.0"
|
|||||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
atomic-polyfill = "1.0.1"
|
atomic-polyfill = "1.0.1"
|
||||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7" }
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9a61a1f090462df8bd1751f89951f04934fdceb3" }
|
||||||
vcell = "0.1.3"
|
vcell = "0.1.3"
|
||||||
bxcan = "0.7.0"
|
bxcan = "0.7.0"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
@ -76,7 +77,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
proc-macro2 = "1.0.36"
|
proc-macro2 = "1.0.36"
|
||||||
quote = "1.0.15"
|
quote = "1.0.15"
|
||||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7", default-features = false, features = ["metadata"]}
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9a61a1f090462df8bd1751f89951f04934fdceb3", default-features = false, features = ["metadata"]}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rt"]
|
default = ["rt"]
|
||||||
@ -88,6 +89,8 @@ rt = ["stm32-metapac/rt"]
|
|||||||
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async?/defmt-03", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"]
|
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async?/defmt-03", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"]
|
||||||
|
|
||||||
exti = []
|
exti = []
|
||||||
|
low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ]
|
||||||
|
embassy-executor = []
|
||||||
|
|
||||||
## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/)
|
## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/)
|
||||||
memory-x = ["stm32-metapac/memory-x"]
|
memory-x = ["stm32-metapac/memory-x"]
|
||||||
|
@ -356,6 +356,8 @@ fn main() {
|
|||||||
}
|
}
|
||||||
fn enable() {
|
fn enable() {
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
crate::rcc::clock_refcount_add();
|
||||||
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
|
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
|
||||||
#after_enable
|
#after_enable
|
||||||
})
|
})
|
||||||
@ -363,6 +365,8 @@ fn main() {
|
|||||||
fn disable() {
|
fn disable() {
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
|
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
crate::rcc::clock_refcount_sub();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn reset() {
|
fn reset() {
|
||||||
|
@ -478,7 +478,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
|
|||||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| {
|
||||||
T::state().err_waker.register(cx.waker());
|
T::state().err_waker.register(cx.waker());
|
||||||
if let Poll::Ready(envelope) = T::state().rx_queue.recv().poll_unpin(cx) {
|
if let Poll::Ready(envelope) = T::state().rx_queue.receive().poll_unpin(cx) {
|
||||||
return Poll::Ready(Ok(envelope));
|
return Poll::Ready(Ok(envelope));
|
||||||
} else if let Some(err) = self.curr_error() {
|
} else if let Some(err) = self.curr_error() {
|
||||||
return Poll::Ready(Err(err));
|
return Poll::Ready(Err(err));
|
||||||
@ -493,7 +493,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
|
|||||||
///
|
///
|
||||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||||
if let Ok(envelope) = T::state().rx_queue.try_recv() {
|
if let Ok(envelope) = T::state().rx_queue.try_receive() {
|
||||||
return Ok(envelope);
|
return Ok(envelope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,14 +506,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
|
|||||||
|
|
||||||
/// Waits while receive queue is empty.
|
/// Waits while receive queue is empty.
|
||||||
pub async fn wait_not_empty(&mut self) {
|
pub async fn wait_not_empty(&mut self) {
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| T::state().rx_queue.poll_ready_to_receive(cx)).await
|
||||||
if T::state().rx_queue.poll_ready_to_receive(cx) {
|
|
||||||
Poll::Ready(())
|
|
||||||
} else {
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn curr_error(&self) -> Option<BusError> {
|
fn curr_error(&self) -> Option<BusError> {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -47,6 +47,8 @@ pub mod i2c;
|
|||||||
pub mod i2s;
|
pub mod i2s;
|
||||||
#[cfg(stm32wb)]
|
#[cfg(stm32wb)]
|
||||||
pub mod ipcc;
|
pub mod ipcc;
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
pub mod low_power;
|
||||||
#[cfg(quadspi)]
|
#[cfg(quadspi)]
|
||||||
pub mod qspi;
|
pub mod qspi;
|
||||||
#[cfg(rng)]
|
#[cfg(rng)]
|
||||||
@ -195,6 +197,11 @@ pub fn init(config: Config) -> Peripherals {
|
|||||||
// must be after rcc init
|
// must be after rcc init
|
||||||
#[cfg(feature = "_time-driver")]
|
#[cfg(feature = "_time-driver")]
|
||||||
time_driver::init();
|
time_driver::init();
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
while !crate::rcc::low_power_ready() {
|
||||||
|
crate::rcc::clock_refcount_sub();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p
|
p
|
||||||
|
151
embassy-stm32/src/low_power.rs
Normal file
151
embassy-stm32/src/low_power.rs
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
use core::arch::asm;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use cortex_m::peripheral::SCB;
|
||||||
|
use embassy_executor::*;
|
||||||
|
|
||||||
|
use crate::interrupt;
|
||||||
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
use crate::rcc::low_power_ready;
|
||||||
|
use crate::time_driver::{get_driver, RtcDriver};
|
||||||
|
|
||||||
|
const THREAD_PENDER: usize = usize::MAX;
|
||||||
|
|
||||||
|
use crate::rtc::Rtc;
|
||||||
|
|
||||||
|
static mut EXECUTOR: Option<Executor> = None;
|
||||||
|
|
||||||
|
foreach_interrupt! {
|
||||||
|
(RTC, rtc, $block:ident, WKUP, $irq:ident) => {
|
||||||
|
#[interrupt]
|
||||||
|
unsafe fn $irq() {
|
||||||
|
unsafe { EXECUTOR.as_mut().unwrap() }.on_wakeup_irq();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn timer_driver_pause_time() {
|
||||||
|
// pause_time();
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn stop_with_rtc(rtc: &'static Rtc) {
|
||||||
|
unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) {
|
||||||
|
// let rtc_instant = unsafe { EXECUTOR.as_mut().unwrap() }
|
||||||
|
// .rtc
|
||||||
|
// .unwrap()
|
||||||
|
// .start_wakeup_alarm(requested_duration);
|
||||||
|
//
|
||||||
|
// unsafe { EXECUTOR.as_mut().unwrap() }.last_stop = Some(rtc_instant);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn set_sleepdeep() {
|
||||||
|
// unsafe { EXECUTOR.as_mut().unwrap() }.scb.set_sleepdeep();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pub fn stop_wakeup_alarm() -> RtcInstant {
|
||||||
|
// unsafe { EXECUTOR.as_mut().unwrap() }.rtc.unwrap().stop_wakeup_alarm()
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// Thread mode executor, using WFE/SEV.
|
||||||
|
///
|
||||||
|
/// This is the simplest and most common kind of executor. It runs on
|
||||||
|
/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
|
||||||
|
/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
|
||||||
|
/// is executed, to make the `WFE` exit from sleep and poll the task.
|
||||||
|
///
|
||||||
|
/// This executor allows for ultra low power consumption for chips where `WFE`
|
||||||
|
/// triggers low-power sleep without extra steps. If your chip requires extra steps,
|
||||||
|
/// you may use [`raw::Executor`] directly to program custom behavior.
|
||||||
|
pub struct Executor {
|
||||||
|
inner: raw::Executor,
|
||||||
|
not_send: PhantomData<*mut ()>,
|
||||||
|
scb: SCB,
|
||||||
|
time_driver: &'static RtcDriver,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Executor {
|
||||||
|
/// Create a new Executor.
|
||||||
|
pub fn take() -> &'static mut Self {
|
||||||
|
unsafe {
|
||||||
|
assert!(EXECUTOR.is_none());
|
||||||
|
|
||||||
|
EXECUTOR = Some(Self {
|
||||||
|
inner: raw::Executor::new(THREAD_PENDER as *mut ()),
|
||||||
|
not_send: PhantomData,
|
||||||
|
scb: cortex_m::Peripherals::steal().SCB,
|
||||||
|
time_driver: get_driver(),
|
||||||
|
});
|
||||||
|
|
||||||
|
EXECUTOR.as_mut().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn on_wakeup_irq(&mut self) {
|
||||||
|
trace!("low power: on wakeup irq");
|
||||||
|
|
||||||
|
self.time_driver.resume_time();
|
||||||
|
trace!("low power: resume time");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) {
|
||||||
|
trace!("low power: stop with rtc configured");
|
||||||
|
|
||||||
|
self.time_driver.set_rtc(rtc);
|
||||||
|
|
||||||
|
crate::interrupt::typelevel::RTC_WKUP::unpend();
|
||||||
|
unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() };
|
||||||
|
|
||||||
|
rtc.enable_wakeup_line();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_pwr(&mut self) {
|
||||||
|
trace!("low power: configure_pwr");
|
||||||
|
|
||||||
|
self.scb.clear_sleepdeep();
|
||||||
|
if !low_power_ready() {
|
||||||
|
trace!("low power: configure_pwr: low power not ready");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.time_driver.pause_time().is_err() {
|
||||||
|
trace!("low power: configure_pwr: time driver failed to pause");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("low power: enter stop...");
|
||||||
|
self.scb.set_sleepdeep();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the executor.
|
||||||
|
///
|
||||||
|
/// The `init` closure is called with a [`Spawner`] that spawns tasks on
|
||||||
|
/// this executor. Use it to spawn the initial task(s). After `init` returns,
|
||||||
|
/// the executor starts running the tasks.
|
||||||
|
///
|
||||||
|
/// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
|
||||||
|
/// for example by passing it as an argument to the initial tasks.
|
||||||
|
///
|
||||||
|
/// This function requires `&'static mut self`. This means you have to store the
|
||||||
|
/// Executor instance in a place where it'll live forever and grants you mutable
|
||||||
|
/// access. There's a few ways to do this:
|
||||||
|
///
|
||||||
|
/// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
|
||||||
|
/// - a `static mut` (unsafe)
|
||||||
|
/// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
|
||||||
|
///
|
||||||
|
/// This function never returns.
|
||||||
|
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||||
|
init(unsafe { EXECUTOR.as_mut().unwrap() }.inner.spawner());
|
||||||
|
|
||||||
|
loop {
|
||||||
|
unsafe {
|
||||||
|
EXECUTOR.as_mut().unwrap().inner.poll();
|
||||||
|
self.configure_pwr();
|
||||||
|
asm!("wfe");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
126
embassy-stm32/src/rcc/bd.rs
Normal file
126
embassy-stm32/src/rcc/bd.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum RtcClockSource {
|
||||||
|
/// 00: No clock
|
||||||
|
NoClock = 0b00,
|
||||||
|
/// 01: LSE oscillator clock used as RTC clock
|
||||||
|
LSE = 0b01,
|
||||||
|
/// 10: LSI oscillator clock used as RTC clock
|
||||||
|
LSI = 0b10,
|
||||||
|
/// 11: HSE oscillator clock divided by 32 used as RTC clock
|
||||||
|
HSE = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
type Bdcr = crate::pac::rcc::regs::Bdcr;
|
||||||
|
|
||||||
|
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
type Bdcr = crate::pac::rcc::regs::Csr;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct BackupDomain {}
|
||||||
|
|
||||||
|
impl BackupDomain {
|
||||||
|
#[cfg(any(
|
||||||
|
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||||
|
rtc_v3u5
|
||||||
|
))]
|
||||||
|
#[allow(dead_code, unused_variables)]
|
||||||
|
fn modify<R>(f: impl FnOnce(&mut Bdcr) -> R) -> R {
|
||||||
|
#[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))]
|
||||||
|
let cr = crate::pac::PWR.cr();
|
||||||
|
#[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||||
|
let cr = crate::pac::PWR.cr1();
|
||||||
|
|
||||||
|
// TODO: Missing from PAC for l0 and f0?
|
||||||
|
#[cfg(not(any(rtc_v2f0, rtc_v2l0, rtc_v3u5)))]
|
||||||
|
{
|
||||||
|
cr.modify(|w| w.set_dbp(true));
|
||||||
|
while !cr.read().dbp() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||||
|
let cr = crate::pac::RCC.csr();
|
||||||
|
|
||||||
|
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
||||||
|
let cr = crate::pac::RCC.bdcr();
|
||||||
|
|
||||||
|
cr.modify(|w| f(w))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||||
|
rtc_v3u5
|
||||||
|
))]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn read() -> Bdcr {
|
||||||
|
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||||
|
let r = crate::pac::RCC.csr().read();
|
||||||
|
|
||||||
|
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
||||||
|
let r = crate::pac::RCC.bdcr().read();
|
||||||
|
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||||
|
rtc_v3u5
|
||||||
|
))]
|
||||||
|
#[allow(dead_code, unused_variables)]
|
||||||
|
pub fn set_rtc_clock_source(clock_source: RtcClockSource) {
|
||||||
|
let clock_source = clock_source as u8;
|
||||||
|
#[cfg(any(
|
||||||
|
all(not(any(rtc_v3, rtc_v3u5)), not(rtc_v2wb)),
|
||||||
|
all(any(rtc_v3, rtc_v3u5), not(any(rcc_wl5, rcc_wle)))
|
||||||
|
))]
|
||||||
|
let clock_source = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source);
|
||||||
|
|
||||||
|
#[cfg(not(rtc_v2wb))]
|
||||||
|
Self::modify(|w| {
|
||||||
|
// Select RTC source
|
||||||
|
w.set_rtcsel(clock_source);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||||
|
rtc_v3u5
|
||||||
|
))]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn enable_rtc() {
|
||||||
|
let reg = Self::read();
|
||||||
|
|
||||||
|
#[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||||
|
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||||
|
|
||||||
|
if !reg.rtcen() {
|
||||||
|
#[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))]
|
||||||
|
Self::modify(|w| w.set_bdrst(true));
|
||||||
|
|
||||||
|
Self::modify(|w| {
|
||||||
|
// Reset
|
||||||
|
#[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))]
|
||||||
|
w.set_bdrst(false);
|
||||||
|
|
||||||
|
w.set_rtcen(true);
|
||||||
|
w.set_rtcsel(reg.rtcsel());
|
||||||
|
|
||||||
|
// Restore bcdr
|
||||||
|
#[cfg(any(rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||||
|
w.set_lscosel(reg.lscosel());
|
||||||
|
#[cfg(any(rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||||
|
w.set_lscoen(reg.lscoen());
|
||||||
|
|
||||||
|
w.set_lseon(reg.lseon());
|
||||||
|
|
||||||
|
#[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||||
|
w.set_lsedrv(reg.lsedrv());
|
||||||
|
w.set_lsebyp(reg.lsebyp());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
use crate::pac::flash::vals::Latency;
|
use crate::pac::flash::vals::Latency;
|
||||||
use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw};
|
use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw};
|
||||||
use crate::pac::{FLASH, RCC};
|
use crate::pac::{FLASH, RCC};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use core::ops::{Div, Mul};
|
use core::ops::{Div, Mul};
|
||||||
|
|
||||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
use crate::pac::flash::vals::Latency;
|
use crate::pac::flash::vals::Latency;
|
||||||
use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw};
|
use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw};
|
||||||
use crate::pac::{FLASH, RCC};
|
use crate::pac::{FLASH, RCC};
|
||||||
@ -201,7 +201,7 @@ pub struct PLLClocks {
|
|||||||
pub pll48_freq: Hertz,
|
pub pll48_freq: Hertz,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use super::common::VoltageScale;
|
pub use super::bus::VoltageScale;
|
||||||
|
|
||||||
impl VoltageScale {
|
impl VoltageScale {
|
||||||
const fn wait_states(&self, ahb_freq: Hertz) -> Option<Latency> {
|
const fn wait_states(&self, ahb_freq: Hertz) -> Option<Latency> {
|
||||||
|
@ -201,9 +201,9 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
|
|||||||
// Calculates the Multiplier and the Divisor to arrive at
|
// Calculates the Multiplier and the Divisor to arrive at
|
||||||
// the required System clock from PLL source frequency
|
// the required System clock from PLL source frequency
|
||||||
let get_mul_div = |sysclk, pllsrcclk| {
|
let get_mul_div = |sysclk, pllsrcclk| {
|
||||||
let common_div = gcd(sysclk, pllsrcclk);
|
let bus_div = gcd(sysclk, pllsrcclk);
|
||||||
let mut multiplier = sysclk / common_div;
|
let mut multiplier = sysclk / bus_div;
|
||||||
let mut divisor = pllsrcclk / common_div;
|
let mut divisor = pllsrcclk / bus_div;
|
||||||
// Minimum PLL multiplier is two
|
// Minimum PLL multiplier is two
|
||||||
if multiplier == 1 {
|
if multiplier == 1 {
|
||||||
multiplier *= 2;
|
multiplier *= 2;
|
||||||
|
@ -8,8 +8,8 @@ use crate::gpio::sealed::AFType;
|
|||||||
use crate::gpio::Speed;
|
use crate::gpio::Speed;
|
||||||
use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
|
use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
|
||||||
use crate::pac::{FLASH, PWR, RCC};
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
|
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::rtc::{Rtc, RtcClockSource};
|
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::{peripherals, Peripheral};
|
use crate::{peripherals, Peripheral};
|
||||||
|
|
||||||
@ -470,9 +470,14 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config.rtc.map(|clock_source| {
|
config.rtc.map(|clock_source| {
|
||||||
Rtc::set_clock_source(clock_source);
|
BackupDomain::set_rtc_clock_source(clock_source);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let rtc = match config.rtc {
|
||||||
|
Some(RtcClockSource::LSI) => Some(LSI_FREQ),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
set_freqs(Clocks {
|
set_freqs(Clocks {
|
||||||
sys: Hertz(sysclk),
|
sys: Hertz(sysclk),
|
||||||
apb1: Hertz(pclk1),
|
apb1: Hertz(pclk1),
|
||||||
@ -492,6 +497,9 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
|
|
||||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||||
pllsai: None,
|
pllsai: None,
|
||||||
|
|
||||||
|
rtc: rtc,
|
||||||
|
rtc_hse: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
use crate::pac::flash::vals::Latency;
|
use crate::pac::flash::vals::Latency;
|
||||||
use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw};
|
use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw};
|
||||||
use crate::pac::{FLASH, PWR, RCC};
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
|
@ -2,7 +2,7 @@ use stm32_metapac::flash::vals::Latency;
|
|||||||
use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw};
|
use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw};
|
||||||
use stm32_metapac::FLASH;
|
use stm32_metapac::FLASH;
|
||||||
|
|
||||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
use crate::pac::{PWR, RCC};
|
use crate::pac::{PWR, RCC};
|
||||||
use crate::rcc::sealed::RccPeripheral;
|
use crate::rcc::sealed::RccPeripheral;
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
@ -26,7 +26,7 @@ const VCO_MAX: u32 = 420_000_000;
|
|||||||
const VCO_WIDE_MIN: u32 = 128_000_000;
|
const VCO_WIDE_MIN: u32 = 128_000_000;
|
||||||
const VCO_WIDE_MAX: u32 = 560_000_000;
|
const VCO_WIDE_MAX: u32 = 560_000_000;
|
||||||
|
|
||||||
pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale};
|
pub use super::bus::{AHBPrescaler, APBPrescaler, VoltageScale};
|
||||||
|
|
||||||
pub enum HseMode {
|
pub enum HseMode {
|
||||||
/// crystal/ceramic oscillator (HSEBYP=0)
|
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||||
|
@ -24,7 +24,7 @@ pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
|
|||||||
/// LSI speed
|
/// LSI speed
|
||||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||||
|
|
||||||
pub use super::common::VoltageScale;
|
pub use super::bus::VoltageScale;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum AdcClockSource {
|
pub enum AdcClockSource {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||||
use crate::pac::RCC;
|
use crate::pac::RCC;
|
||||||
#[cfg(crs)]
|
#[cfg(crs)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||||
use crate::pac::{FLASH, RCC};
|
use crate::pac::{FLASH, RCC};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
@ -4,13 +4,13 @@ use embassy_hal_internal::into_ref;
|
|||||||
use stm32_metapac::rcc::regs::Cfgr;
|
use stm32_metapac::rcc::regs::Cfgr;
|
||||||
use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel};
|
use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel};
|
||||||
|
|
||||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
use crate::gpio::sealed::AFType;
|
use crate::gpio::sealed::AFType;
|
||||||
use crate::gpio::Speed;
|
use crate::gpio::Speed;
|
||||||
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
|
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
|
||||||
use crate::pac::{FLASH, PWR, RCC};
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
|
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::rtc::{Rtc, RtcClockSource as RCS};
|
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::{peripherals, Peripheral};
|
use crate::{peripherals, Peripheral};
|
||||||
|
|
||||||
@ -254,16 +254,11 @@ impl Default for Config {
|
|||||||
pllsai1: None,
|
pllsai1: None,
|
||||||
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
|
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
|
||||||
hsi48: false,
|
hsi48: false,
|
||||||
rtc_mux: RtcClockSource::LSI32,
|
rtc_mux: RtcClockSource::LSI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum RtcClockSource {
|
|
||||||
LSE32,
|
|
||||||
LSI32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum McoClock {
|
pub enum McoClock {
|
||||||
DIV1,
|
DIV1,
|
||||||
DIV2,
|
DIV2,
|
||||||
@ -413,7 +408,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
RCC.apb1enr1().modify(|w| w.set_pwren(true));
|
RCC.apb1enr1().modify(|w| w.set_pwren(true));
|
||||||
|
|
||||||
match config.rtc_mux {
|
match config.rtc_mux {
|
||||||
RtcClockSource::LSE32 => {
|
RtcClockSource::LSE => {
|
||||||
// 1. Unlock the backup domain
|
// 1. Unlock the backup domain
|
||||||
PWR.cr1().modify(|w| w.set_dbp(true));
|
PWR.cr1().modify(|w| w.set_dbp(true));
|
||||||
|
|
||||||
@ -429,17 +424,18 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
// Wait until LSE is running
|
// Wait until LSE is running
|
||||||
while !RCC.bdcr().read().lserdy() {}
|
while !RCC.bdcr().read().lserdy() {}
|
||||||
|
|
||||||
Rtc::set_clock_source(RCS::LSE);
|
BackupDomain::set_rtc_clock_source(RtcClockSource::LSE);
|
||||||
}
|
}
|
||||||
RtcClockSource::LSI32 => {
|
RtcClockSource::LSI => {
|
||||||
// Turn on the internal 32 kHz LSI oscillator
|
// Turn on the internal 32 kHz LSI oscillator
|
||||||
RCC.csr().modify(|w| w.set_lsion(true));
|
RCC.csr().modify(|w| w.set_lsion(true));
|
||||||
|
|
||||||
// Wait until LSI is running
|
// Wait until LSI is running
|
||||||
while !RCC.csr().read().lsirdy() {}
|
while !RCC.csr().read().lsirdy() {}
|
||||||
|
|
||||||
Rtc::set_clock_source(RCS::LSI);
|
BackupDomain::set_rtc_clock_source(RtcClockSource::LSI);
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
let (sys_clk, sw) = match config.mux {
|
let (sys_clk, sw) = match config.mux {
|
||||||
@ -451,7 +447,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
w.set_msirgsel(true);
|
w.set_msirgsel(true);
|
||||||
w.set_msion(true);
|
w.set_msion(true);
|
||||||
|
|
||||||
if let RtcClockSource::LSE32 = config.rtc_mux {
|
if let RtcClockSource::LSE = config.rtc_mux {
|
||||||
// If LSE is enabled, enable calibration of MSI
|
// If LSE is enabled, enable calibration of MSI
|
||||||
w.set_msipllen(true);
|
w.set_msipllen(true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use stm32_metapac::PWR;
|
use stm32_metapac::PWR;
|
||||||
|
|
||||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
|
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
|
||||||
use crate::pac::{FLASH, RCC};
|
use crate::pac::{FLASH, RCC};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
pub mod common;
|
pub(crate) mod bd;
|
||||||
|
pub mod bus;
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
|
pub use crate::rcc::bd::RtcClockSource;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|
||||||
#[cfg_attr(rcc_f0, path = "f0.rs")]
|
#[cfg_attr(rcc_f0, path = "f0.rs")]
|
||||||
@ -26,6 +27,8 @@ use crate::time::Hertz;
|
|||||||
#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")]
|
#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")]
|
||||||
mod _version;
|
mod _version;
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
use atomic_polyfill::{AtomicU32, Ordering};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
@ -74,9 +77,34 @@ pub struct Clocks {
|
|||||||
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))]
|
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))]
|
||||||
pub adc: Option<Hertz>,
|
pub adc: Option<Hertz>,
|
||||||
|
|
||||||
#[cfg(rcc_wb)]
|
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
|
||||||
/// Set only if the lsi or lse is configured
|
/// Set only if the lsi or lse is configured, indicates stop is supported
|
||||||
pub rtc: Option<Hertz>,
|
pub rtc: Option<Hertz>,
|
||||||
|
|
||||||
|
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
|
||||||
|
/// Set if the hse is configured, indicates stop is not supported
|
||||||
|
pub rtc_hse: Option<Hertz>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0);
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
pub fn low_power_ready() -> bool {
|
||||||
|
trace!("clock refcount: {}", CLOCK_REFCOUNT.load(Ordering::SeqCst));
|
||||||
|
|
||||||
|
CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
pub(crate) fn clock_refcount_add() {
|
||||||
|
// We don't check for overflow because constructing more than u32 peripherals is unlikely
|
||||||
|
CLOCK_REFCOUNT.fetch_add(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
pub(crate) fn clock_refcount_sub() {
|
||||||
|
assert!(CLOCK_REFCOUNT.fetch_sub(1, Ordering::Relaxed) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Frozen clock frequencies
|
/// Frozen clock frequencies
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw};
|
use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw};
|
||||||
|
|
||||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
use crate::pac::{FLASH, RCC};
|
use crate::pac::{FLASH, RCC};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
@ -11,7 +11,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
|||||||
/// LSI speed
|
/// LSI speed
|
||||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||||
|
|
||||||
pub use super::common::VoltageScale;
|
pub use super::bus::VoltageScale;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum ClockSrc {
|
pub enum ClockSrc {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||||
|
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||||
use crate::rcc::Clocks;
|
use crate::rcc::Clocks;
|
||||||
use crate::rtc::{Rtc, RtcClockSource};
|
|
||||||
use crate::time::{khz, mhz, Hertz};
|
use crate::time::{khz, mhz, Hertz};
|
||||||
|
|
||||||
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
|
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
|
||||||
@ -271,6 +271,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks {
|
|||||||
apb1_tim: apb1_tim_clk,
|
apb1_tim: apb1_tim_clk,
|
||||||
apb2_tim: apb2_tim_clk,
|
apb2_tim: apb2_tim_clk,
|
||||||
rtc: rtc_clk,
|
rtc: rtc_clk,
|
||||||
|
rtc_hse: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,5 +376,7 @@ pub(crate) fn configure_clocks(config: &Config) {
|
|||||||
w.set_shdhpre(config.ahb3_pre.into());
|
w.set_shdhpre(config.ahb3_pre.into());
|
||||||
});
|
});
|
||||||
|
|
||||||
config.rtc.map(|clock_source| Rtc::set_clock_source(clock_source));
|
config
|
||||||
|
.rtc
|
||||||
|
.map(|clock_source| BackupDomain::set_rtc_clock_source(clock_source));
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale};
|
pub use super::bus::{AHBPrescaler, APBPrescaler, VoltageScale};
|
||||||
use crate::pac::pwr::vals::Dbp;
|
|
||||||
use crate::pac::{FLASH, PWR, RCC};
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
|
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::rtc::{Rtc, RtcClockSource as RCS};
|
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|
||||||
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
|
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
|
||||||
@ -130,16 +129,11 @@ impl Default for Config {
|
|||||||
apb2_pre: APBPrescaler::NotDivided,
|
apb2_pre: APBPrescaler::NotDivided,
|
||||||
enable_lsi: false,
|
enable_lsi: false,
|
||||||
enable_rtc_apb: false,
|
enable_rtc_apb: false,
|
||||||
rtc_mux: RtcClockSource::LSI32,
|
rtc_mux: RtcClockSource::LSI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum RtcClockSource {
|
|
||||||
LSE32,
|
|
||||||
LSI32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Lsedrv {
|
pub enum Lsedrv {
|
||||||
Low = 0,
|
Low = 0,
|
||||||
@ -215,9 +209,9 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
while FLASH.acr().read().latency() != ws {}
|
while FLASH.acr().read().latency() != ws {}
|
||||||
|
|
||||||
match config.rtc_mux {
|
match config.rtc_mux {
|
||||||
RtcClockSource::LSE32 => {
|
RtcClockSource::LSE => {
|
||||||
// 1. Unlock the backup domain
|
// 1. Unlock the backup domain
|
||||||
PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED));
|
PWR.cr1().modify(|w| w.set_dbp(true));
|
||||||
|
|
||||||
// 2. Setup the LSE
|
// 2. Setup the LSE
|
||||||
RCC.bdcr().modify(|w| {
|
RCC.bdcr().modify(|w| {
|
||||||
@ -231,17 +225,18 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
// Wait until LSE is running
|
// Wait until LSE is running
|
||||||
while !RCC.bdcr().read().lserdy() {}
|
while !RCC.bdcr().read().lserdy() {}
|
||||||
|
|
||||||
Rtc::set_clock_source(RCS::LSE);
|
BackupDomain::set_rtc_clock_source(RtcClockSource::LSE);
|
||||||
}
|
}
|
||||||
RtcClockSource::LSI32 => {
|
RtcClockSource::LSI => {
|
||||||
// Turn on the internal 32 kHz LSI oscillator
|
// Turn on the internal 32 kHz LSI oscillator
|
||||||
RCC.csr().modify(|w| w.set_lsion(true));
|
RCC.csr().modify(|w| w.set_lsion(true));
|
||||||
|
|
||||||
// Wait until LSI is running
|
// Wait until LSI is running
|
||||||
while !RCC.csr().read().lsirdy() {}
|
while !RCC.csr().read().lsirdy() {}
|
||||||
|
|
||||||
Rtc::set_clock_source(RCS::LSI);
|
BackupDomain::set_rtc_clock_source(RtcClockSource::LSI);
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
match config.mux {
|
match config.mux {
|
||||||
@ -266,7 +261,7 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
w.set_msirange(range.into());
|
w.set_msirange(range.into());
|
||||||
w.set_msion(true);
|
w.set_msion(true);
|
||||||
|
|
||||||
if let RtcClockSource::LSE32 = config.rtc_mux {
|
if let RtcClockSource::LSE = config.rtc_mux {
|
||||||
// If LSE is enabled, enable calibration of MSI
|
// If LSE is enabled, enable calibration of MSI
|
||||||
w.set_msipllen(true);
|
w.set_msipllen(true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -119,7 +119,31 @@ impl<'d, T: Instance> Rng<'d, T> {
|
|||||||
|
|
||||||
pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||||
for chunk in dest.chunks_mut(4) {
|
for chunk in dest.chunks_mut(4) {
|
||||||
|
let mut bits = T::regs().sr().read();
|
||||||
|
if !bits.seis() && !bits.ceis() && !bits.drdy() {
|
||||||
|
// wait for interrupt
|
||||||
|
poll_fn(|cx| {
|
||||||
|
// quick check to avoid registration if already done.
|
||||||
let bits = T::regs().sr().read();
|
let bits = T::regs().sr().read();
|
||||||
|
if bits.drdy() || bits.seis() || bits.ceis() {
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
RNG_WAKER.register(cx.waker());
|
||||||
|
T::regs().cr().modify(|reg| reg.set_ie(true));
|
||||||
|
// Need to check condition **after** `register` to avoid a race
|
||||||
|
// condition that would result in lost notifications.
|
||||||
|
let bits = T::regs().sr().read();
|
||||||
|
if bits.drdy() || bits.seis() || bits.ceis() {
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Re-read the status register after wait.
|
||||||
|
bits = T::regs().sr().read()
|
||||||
|
}
|
||||||
if bits.seis() {
|
if bits.seis() {
|
||||||
// in case of noise-source or seed error we try to recover here
|
// in case of noise-source or seed error we try to recover here
|
||||||
// but we must not use the data in DR and we return an error
|
// but we must not use the data in DR and we return an error
|
||||||
@ -143,26 +167,6 @@ impl<'d, T: Instance> Rng<'d, T> {
|
|||||||
for (dest, src) in chunk.iter_mut().zip(random_word.to_be_bytes().iter()) {
|
for (dest, src) in chunk.iter_mut().zip(random_word.to_be_bytes().iter()) {
|
||||||
*dest = *src
|
*dest = *src
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// wait for interrupt
|
|
||||||
poll_fn(|cx| {
|
|
||||||
// quick check to avoid registration if already done.
|
|
||||||
let bits = T::regs().sr().read();
|
|
||||||
if bits.drdy() || bits.seis() || bits.ceis() {
|
|
||||||
return Poll::Ready(());
|
|
||||||
}
|
|
||||||
RNG_WAKER.register(cx.waker());
|
|
||||||
T::regs().cr().modify(|reg| reg.set_ie(true));
|
|
||||||
// Need to check condition **after** `register` to avoid a race
|
|
||||||
// condition that would result in lost notifications.
|
|
||||||
let bits = T::regs().sr().read();
|
|
||||||
if bits.drdy() || bits.seis() || bits.ceis() {
|
|
||||||
Poll::Ready(())
|
|
||||||
} else {
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ pub enum DayOfWeek {
|
|||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
impl From<chrono::Weekday> for DayOfWeek {
|
impl From<chrono::Weekday> for DayOfWeek {
|
||||||
fn from(weekday: Weekday) -> Self {
|
fn from(weekday: Weekday) -> Self {
|
||||||
day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap()
|
day_of_week_from_u8(weekday.num_days_from_monday() as u8).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,18 @@
|
|||||||
//! RTC peripheral abstraction
|
//! RTC peripheral abstraction
|
||||||
mod datetime;
|
mod datetime;
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
use core::cell::Cell;
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
|
||||||
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
||||||
|
use crate::rcc::bd::BackupDomain;
|
||||||
|
pub use crate::rcc::RtcClockSource;
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
/// refer to AN4759 to compare features of RTC2 and RTC3
|
/// refer to AN4759 to compare features of RTC2 and RTC3
|
||||||
#[cfg_attr(any(rtc_v1), path = "v1.rs")]
|
#[cfg_attr(any(rtc_v1), path = "v1.rs")]
|
||||||
@ -13,6 +24,7 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
|||||||
)]
|
)]
|
||||||
#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
|
#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
|
||||||
mod _version;
|
mod _version;
|
||||||
|
#[allow(unused_imports)]
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
use embassy_hal_internal::Peripheral;
|
use embassy_hal_internal::Peripheral;
|
||||||
|
|
||||||
@ -29,60 +41,67 @@ pub enum RtcError {
|
|||||||
NotRunning,
|
NotRunning,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RTC Abstraction
|
#[cfg(feature = "low-power")]
|
||||||
pub struct Rtc {
|
/// Represents an instant in time that can be substracted to compute a duration
|
||||||
rtc_config: RtcConfig,
|
struct RtcInstant {
|
||||||
|
second: u8,
|
||||||
|
subsecond: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[cfg(all(feature = "low-power", feature = "defmt"))]
|
||||||
#[repr(u8)]
|
impl defmt::Format for RtcInstant {
|
||||||
pub enum RtcClockSource {
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
/// 00: No clock
|
defmt::write!(
|
||||||
NoClock = 0b00,
|
fmt,
|
||||||
/// 01: LSE oscillator clock used as RTC clock
|
"{}:{}",
|
||||||
LSE = 0b01,
|
self.second,
|
||||||
/// 10: LSI oscillator clock used as RTC clock
|
RTC::regs().prer().read().prediv_s() - self.subsecond,
|
||||||
LSI = 0b10,
|
)
|
||||||
/// 11: HSE oscillator clock divided by 32 used as RTC clock
|
}
|
||||||
HSE = 0b11,
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
impl core::ops::Sub for RtcInstant {
|
||||||
|
type Output = embassy_time::Duration;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
use embassy_time::{Duration, TICK_HZ};
|
||||||
|
|
||||||
|
let second = if self.second < rhs.second {
|
||||||
|
self.second + 60
|
||||||
|
} else {
|
||||||
|
self.second
|
||||||
|
};
|
||||||
|
|
||||||
|
let psc = RTC::regs().prer().read().prediv_s() as u32;
|
||||||
|
|
||||||
|
let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32);
|
||||||
|
let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32);
|
||||||
|
let rtc_ticks = self_ticks - other_ticks;
|
||||||
|
|
||||||
|
Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RTC Abstraction
|
||||||
|
pub struct Rtc {
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
stop_time: Mutex<CriticalSectionRawMutex, Cell<Option<RtcInstant>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub struct RtcConfig {
|
pub struct RtcConfig {
|
||||||
/// Asynchronous prescaler factor
|
/// The subsecond counter frequency; default is 256
|
||||||
/// This is the asynchronous division factor:
|
///
|
||||||
/// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1)
|
/// A high counter frequency may impact stop power consumption
|
||||||
/// ck_apre drives the subsecond register
|
pub frequency: Hertz,
|
||||||
async_prescaler: u8,
|
|
||||||
/// Synchronous prescaler factor
|
|
||||||
/// This is the synchronous division factor:
|
|
||||||
/// ck_spre frequency = ck_apre frequency/(PREDIV_S+1)
|
|
||||||
/// ck_spre must be 1Hz
|
|
||||||
sync_prescaler: u16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RtcConfig {
|
impl Default for RtcConfig {
|
||||||
/// LSI with prescalers assuming 32.768 kHz.
|
/// LSI with prescalers assuming 32.768 kHz.
|
||||||
/// Raw sub-seconds in 1/256.
|
/// Raw sub-seconds in 1/256.
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
RtcConfig {
|
RtcConfig { frequency: Hertz(256) }
|
||||||
async_prescaler: 127,
|
|
||||||
sync_prescaler: 255,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RtcConfig {
|
|
||||||
/// Set the asynchronous prescaler of RTC config
|
|
||||||
pub fn async_prescaler(mut self, prescaler: u8) -> Self {
|
|
||||||
self.async_prescaler = prescaler;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the synchronous prescaler of RTC config
|
|
||||||
pub fn sync_prescaler(mut self, prescaler: u16) -> Self {
|
|
||||||
self.sync_prescaler = prescaler;
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,16 +124,37 @@ impl Default for RtcCalibrationCyclePeriod {
|
|||||||
|
|
||||||
impl Rtc {
|
impl Rtc {
|
||||||
pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self {
|
pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self {
|
||||||
|
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
|
||||||
|
use crate::rcc::get_freqs;
|
||||||
|
|
||||||
RTC::enable_peripheral_clk();
|
RTC::enable_peripheral_clk();
|
||||||
|
BackupDomain::enable_rtc();
|
||||||
|
|
||||||
let mut rtc_struct = Self { rtc_config };
|
let mut this = Self {
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
|
||||||
|
};
|
||||||
|
|
||||||
Self::enable();
|
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
|
||||||
|
let freqs = unsafe { get_freqs() };
|
||||||
|
|
||||||
rtc_struct.configure(rtc_config);
|
// Load the clock frequency from the rcc mod, if supported
|
||||||
rtc_struct.rtc_config = rtc_config;
|
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
|
||||||
|
let frequency = match freqs.rtc {
|
||||||
|
Some(hertz) => hertz,
|
||||||
|
None => freqs.rtc_hse.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
rtc_struct
|
// Assume the default value, if not supported
|
||||||
|
#[cfg(not(any(rcc_wb, rcc_f4, rcc_f410)))]
|
||||||
|
let frequency = Hertz(32_768);
|
||||||
|
|
||||||
|
let async_psc = ((frequency.0 / rtc_config.frequency.0) - 1) as u8;
|
||||||
|
let sync_psc = (rtc_config.frequency.0 - 1) as u16;
|
||||||
|
|
||||||
|
this.configure(async_psc, sync_psc);
|
||||||
|
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the datetime to a new value.
|
/// Set the datetime to a new value.
|
||||||
@ -129,6 +169,20 @@ impl Rtc {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
/// Return the current instant.
|
||||||
|
fn instant(&self) -> RtcInstant {
|
||||||
|
let r = RTC::regs();
|
||||||
|
let tr = r.tr().read();
|
||||||
|
let subsecond = r.ssr().read().ss();
|
||||||
|
let second = bcd2_to_byte((tr.st(), tr.su()));
|
||||||
|
|
||||||
|
// Unlock the registers
|
||||||
|
r.dr().read();
|
||||||
|
|
||||||
|
RtcInstant { second, subsecond }
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the current datetime.
|
/// Return the current datetime.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@ -165,10 +219,6 @@ impl Rtc {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_config(&self) -> RtcConfig {
|
|
||||||
self.rtc_config
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT;
|
pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT;
|
||||||
|
|
||||||
/// Read content of the backup register.
|
/// Read content of the backup register.
|
||||||
|
@ -1,93 +1,160 @@
|
|||||||
use stm32_metapac::rtc::vals::{Init, Osel, Pol};
|
use stm32_metapac::rtc::vals::{Init, Osel, Pol};
|
||||||
|
|
||||||
use super::{sealed, RtcClockSource, RtcConfig};
|
use super::sealed;
|
||||||
use crate::pac::rtc::Rtc;
|
use crate::pac::rtc::Rtc;
|
||||||
use crate::peripherals::RTC;
|
use crate::peripherals::RTC;
|
||||||
use crate::rtc::sealed::Instance;
|
use crate::rtc::sealed::Instance;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub(crate) enum WakeupPrescaler {
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(stm32wb, stm32f4))]
|
||||||
|
impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
|
||||||
|
fn from(val: WakeupPrescaler) -> Self {
|
||||||
|
use crate::pac::rtc::vals::Wucksel;
|
||||||
|
|
||||||
|
match val {
|
||||||
|
WakeupPrescaler::Div2 => Wucksel::DIV2,
|
||||||
|
WakeupPrescaler::Div4 => Wucksel::DIV4,
|
||||||
|
WakeupPrescaler::Div8 => Wucksel::DIV8,
|
||||||
|
WakeupPrescaler::Div16 => Wucksel::DIV16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(stm32wb, stm32f4))]
|
||||||
|
impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
|
||||||
|
fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
|
||||||
|
use crate::pac::rtc::vals::Wucksel;
|
||||||
|
|
||||||
|
match val {
|
||||||
|
Wucksel::DIV2 => WakeupPrescaler::Div2,
|
||||||
|
Wucksel::DIV4 => WakeupPrescaler::Div4,
|
||||||
|
Wucksel::DIV8 => WakeupPrescaler::Div8,
|
||||||
|
Wucksel::DIV16 => WakeupPrescaler::Div16,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WakeupPrescaler> for u32 {
|
||||||
|
fn from(val: WakeupPrescaler) -> Self {
|
||||||
|
match val {
|
||||||
|
WakeupPrescaler::Div2 => 2,
|
||||||
|
WakeupPrescaler::Div4 => 4,
|
||||||
|
WakeupPrescaler::Div8 => 8,
|
||||||
|
WakeupPrescaler::Div16 => 16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl WakeupPrescaler {
|
||||||
|
pub fn compute_min(val: u32) -> Self {
|
||||||
|
*[
|
||||||
|
WakeupPrescaler::Div2,
|
||||||
|
WakeupPrescaler::Div4,
|
||||||
|
WakeupPrescaler::Div8,
|
||||||
|
WakeupPrescaler::Div16,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.skip_while(|psc| <WakeupPrescaler as Into<u32>>::into(**psc) <= val)
|
||||||
|
.next()
|
||||||
|
.unwrap_or(&WakeupPrescaler::Div16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl super::Rtc {
|
impl super::Rtc {
|
||||||
fn unlock_registers() {
|
#[cfg(feature = "low-power")]
|
||||||
#[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))]
|
/// start the wakeup alarm and wtih a duration that is as close to but less than
|
||||||
let cr = crate::pac::PWR.cr();
|
/// the requested duration, and record the instant the wakeup alarm was started
|
||||||
#[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))]
|
pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) {
|
||||||
let cr = crate::pac::PWR.cr1();
|
use embassy_time::{Duration, TICK_HZ};
|
||||||
|
|
||||||
// TODO: Missing from PAC for l0 and f0?
|
use crate::rcc::get_freqs;
|
||||||
#[cfg(not(any(rtc_v2f0, rtc_v2l0)))]
|
|
||||||
{
|
let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64;
|
||||||
if !cr.read().dbp() {
|
|
||||||
cr.modify(|w| w.set_dbp(true));
|
let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ;
|
||||||
while !cr.read().dbp() {}
|
let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32);
|
||||||
}
|
|
||||||
}
|
// adjust the rtc ticks to the prescaler and subtract one rtc tick
|
||||||
|
let rtc_ticks = rtc_ticks / (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64);
|
||||||
|
let rtc_ticks = if rtc_ticks >= u16::MAX as u64 {
|
||||||
|
u16::MAX - 1
|
||||||
|
} else {
|
||||||
|
rtc_ticks as u16
|
||||||
}
|
}
|
||||||
|
.saturating_sub(1);
|
||||||
|
|
||||||
#[allow(dead_code)]
|
self.write(false, |regs| {
|
||||||
pub(crate) fn set_clock_source(clock_source: RtcClockSource) {
|
regs.cr().modify(|w| w.set_wute(false));
|
||||||
#[cfg(not(rtc_v2wb))]
|
regs.isr().modify(|w| w.set_wutf(false));
|
||||||
use stm32_metapac::rcc::vals::Rtcsel;
|
while !regs.isr().read().wutwf() {}
|
||||||
|
|
||||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
|
||||||
let cr = crate::pac::RCC.bdcr();
|
regs.wutr().write(|w| w.set_wut(rtc_ticks));
|
||||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
regs.cr().modify(|w| w.set_wute(true));
|
||||||
let cr = crate::pac::RCC.csr();
|
regs.cr().modify(|w| w.set_wutie(true));
|
||||||
|
|
||||||
Self::unlock_registers();
|
|
||||||
|
|
||||||
cr.modify(|w| {
|
|
||||||
// Select RTC source
|
|
||||||
#[cfg(not(rtc_v2wb))]
|
|
||||||
w.set_rtcsel(Rtcsel::from_bits(clock_source as u8));
|
|
||||||
#[cfg(rtc_v2wb)]
|
|
||||||
w.set_rtcsel(clock_source as u8);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
"rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
|
||||||
|
Duration::from_ticks(
|
||||||
|
rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz,
|
||||||
|
)
|
||||||
|
.as_millis(),
|
||||||
|
<WakeupPrescaler as Into<u32>>::into(prescaler),
|
||||||
|
rtc_ticks,
|
||||||
|
self.instant(),
|
||||||
|
);
|
||||||
|
|
||||||
|
critical_section::with(|cs| assert!(self.stop_time.borrow(cs).replace(Some(self.instant())).is_none()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn enable() {
|
#[cfg(feature = "low-power")]
|
||||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
pub(crate) fn enable_wakeup_line(&self) {
|
||||||
let reg = crate::pac::RCC.bdcr().read();
|
use crate::pac::EXTI;
|
||||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
|
||||||
let reg = crate::pac::RCC.csr().read();
|
|
||||||
|
|
||||||
#[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))]
|
EXTI.rtsr(0).modify(|w| w.set_line(22, true));
|
||||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
EXTI.imr(0).modify(|w| w.set_line(22, true));
|
||||||
|
}
|
||||||
|
|
||||||
if !reg.rtcen() {
|
#[cfg(feature = "low-power")]
|
||||||
Self::unlock_registers();
|
/// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
|
||||||
|
/// was called, otherwise none
|
||||||
|
pub(crate) fn stop_wakeup_alarm(&self) -> Option<embassy_time::Duration> {
|
||||||
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
|
||||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))]
|
trace!("rtc: stop wakeup alarm at {}", self.instant());
|
||||||
crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
|
|
||||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
|
||||||
let cr = crate::pac::RCC.bdcr();
|
|
||||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
|
||||||
let cr = crate::pac::RCC.csr();
|
|
||||||
|
|
||||||
cr.modify(|w| {
|
self.write(false, |regs| {
|
||||||
// Reset
|
regs.cr().modify(|w| w.set_wutie(false));
|
||||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
regs.cr().modify(|w| w.set_wute(false));
|
||||||
w.set_bdrst(false);
|
regs.isr().modify(|w| w.set_wutf(false));
|
||||||
|
|
||||||
w.set_rtcen(true);
|
crate::pac::EXTI.pr(0).modify(|w| w.set_line(22, true));
|
||||||
w.set_rtcsel(reg.rtcsel());
|
crate::interrupt::typelevel::RTC_WKUP::unpend();
|
||||||
|
|
||||||
// Restore bcdr
|
|
||||||
#[cfg(any(rtc_v2l4, rtc_v2wb))]
|
|
||||||
w.set_lscosel(reg.lscosel());
|
|
||||||
#[cfg(any(rtc_v2l4, rtc_v2wb))]
|
|
||||||
w.set_lscoen(reg.lscoen());
|
|
||||||
|
|
||||||
w.set_lseon(reg.lseon());
|
|
||||||
|
|
||||||
#[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))]
|
|
||||||
w.set_lsedrv(reg.lsedrv());
|
|
||||||
w.set_lsebyp(reg.lsebyp());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
if let Some(stop_time) = self.stop_time.borrow(cs).take() {
|
||||||
|
Some(self.instant() - stop_time)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies the RTC config
|
/// Applies the RTC config
|
||||||
/// It this changes the RTC clock source the time will be reset
|
/// It this changes the RTC clock source the time will be reset
|
||||||
pub(super) fn configure(&mut self, rtc_config: RtcConfig) {
|
pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) {
|
||||||
self.write(true, |rtc| {
|
self.write(true, |rtc| {
|
||||||
rtc.cr().modify(|w| {
|
rtc.cr().modify(|w| {
|
||||||
#[cfg(rtc_v2f2)]
|
#[cfg(rtc_v2f2)]
|
||||||
@ -99,8 +166,8 @@ impl super::Rtc {
|
|||||||
});
|
});
|
||||||
|
|
||||||
rtc.prer().modify(|w| {
|
rtc.prer().modify(|w| {
|
||||||
w.set_prediv_s(rtc_config.sync_prescaler);
|
w.set_prediv_s(sync_psc);
|
||||||
w.set_prediv_a(rtc_config.async_prescaler);
|
w.set_prediv_a(async_psc);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -170,7 +237,7 @@ impl super::Rtc {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
|
pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(&crate::pac::rtc::Rtc) -> R,
|
F: FnOnce(&crate::pac::rtc::Rtc) -> R,
|
||||||
{
|
{
|
||||||
|
@ -1,77 +1,14 @@
|
|||||||
use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType};
|
use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType};
|
||||||
|
|
||||||
use super::{sealed, RtcCalibrationCyclePeriod, RtcClockSource, RtcConfig};
|
use super::{sealed, RtcCalibrationCyclePeriod};
|
||||||
use crate::pac::rtc::Rtc;
|
use crate::pac::rtc::Rtc;
|
||||||
use crate::peripherals::RTC;
|
use crate::peripherals::RTC;
|
||||||
use crate::rtc::sealed::Instance;
|
use crate::rtc::sealed::Instance;
|
||||||
|
|
||||||
impl super::Rtc {
|
impl super::Rtc {
|
||||||
fn unlock_registers() {
|
|
||||||
// Unlock the backup domain
|
|
||||||
#[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))]
|
|
||||||
{
|
|
||||||
if !crate::pac::PWR.cr1().read().dbp() {
|
|
||||||
crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
|
|
||||||
while !crate::pac::PWR.cr1().read().dbp() {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(any(rcc_wl5, rcc_wle))]
|
|
||||||
{
|
|
||||||
use crate::pac::pwr::vals::Dbp;
|
|
||||||
|
|
||||||
if crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {
|
|
||||||
crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED));
|
|
||||||
while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) fn set_clock_source(clock_source: RtcClockSource) {
|
|
||||||
let clock_source = clock_source as u8;
|
|
||||||
#[cfg(not(any(rcc_wl5, rcc_wle)))]
|
|
||||||
let clock_source = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source);
|
|
||||||
|
|
||||||
Self::unlock_registers();
|
|
||||||
|
|
||||||
crate::pac::RCC.bdcr().modify(|w| {
|
|
||||||
// Select RTC source
|
|
||||||
w.set_rtcsel(clock_source);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn enable() {
|
|
||||||
let bdcr = crate::pac::RCC.bdcr();
|
|
||||||
|
|
||||||
let reg = bdcr.read();
|
|
||||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
|
||||||
|
|
||||||
if !reg.rtcen() {
|
|
||||||
Self::unlock_registers();
|
|
||||||
|
|
||||||
bdcr.modify(|w| w.set_bdrst(true));
|
|
||||||
|
|
||||||
bdcr.modify(|w| {
|
|
||||||
// Reset
|
|
||||||
w.set_bdrst(false);
|
|
||||||
|
|
||||||
w.set_rtcen(true);
|
|
||||||
w.set_rtcsel(reg.rtcsel());
|
|
||||||
|
|
||||||
// Restore bcdr
|
|
||||||
w.set_lscosel(reg.lscosel());
|
|
||||||
w.set_lscoen(reg.lscoen());
|
|
||||||
|
|
||||||
w.set_lseon(reg.lseon());
|
|
||||||
w.set_lsedrv(reg.lsedrv());
|
|
||||||
w.set_lsebyp(reg.lsebyp());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Applies the RTC config
|
/// Applies the RTC config
|
||||||
/// It this changes the RTC clock source the time will be reset
|
/// It this changes the RTC clock source the time will be reset
|
||||||
pub(super) fn configure(&mut self, rtc_config: RtcConfig) {
|
pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) {
|
||||||
self.write(true, |rtc| {
|
self.write(true, |rtc| {
|
||||||
rtc.cr().modify(|w| {
|
rtc.cr().modify(|w| {
|
||||||
w.set_fmt(Fmt::TWENTYFOURHOUR);
|
w.set_fmt(Fmt::TWENTYFOURHOUR);
|
||||||
@ -80,8 +17,8 @@ impl super::Rtc {
|
|||||||
});
|
});
|
||||||
|
|
||||||
rtc.prer().modify(|w| {
|
rtc.prer().modify(|w| {
|
||||||
w.set_prediv_s(rtc_config.sync_prescaler);
|
w.set_prediv_s(sync_psc);
|
||||||
w.set_prediv_a(rtc_config.async_prescaler);
|
w.set_prediv_a(async_psc);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: configuration for output pins
|
// TODO: configuration for output pins
|
||||||
|
@ -14,6 +14,8 @@ use stm32_metapac::timer::regs;
|
|||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
use crate::pac::timer::vals;
|
use crate::pac::timer::vals;
|
||||||
use crate::rcc::sealed::RccPeripheral;
|
use crate::rcc::sealed::RccPeripheral;
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
use crate::rtc::Rtc;
|
||||||
use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance};
|
use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance};
|
||||||
use crate::{interrupt, peripherals};
|
use crate::{interrupt, peripherals};
|
||||||
|
|
||||||
@ -130,12 +132,14 @@ impl AlarmState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RtcDriver {
|
pub(crate) struct RtcDriver {
|
||||||
/// Number of 2^15 periods elapsed since boot.
|
/// Number of 2^15 periods elapsed since boot.
|
||||||
period: AtomicU32,
|
period: AtomicU32,
|
||||||
alarm_count: AtomicU8,
|
alarm_count: AtomicU8,
|
||||||
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
||||||
alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
|
alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
|
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
|
||||||
@ -144,6 +148,8 @@ embassy_time::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
|
|||||||
period: AtomicU32::new(0),
|
period: AtomicU32::new(0),
|
||||||
alarm_count: AtomicU8::new(0),
|
alarm_count: AtomicU8::new(0),
|
||||||
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]),
|
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]),
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
|
||||||
});
|
});
|
||||||
|
|
||||||
impl RtcDriver {
|
impl RtcDriver {
|
||||||
@ -259,6 +265,117 @@ impl RtcDriver {
|
|||||||
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
|
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
|
||||||
f(alarm.ctx.get());
|
f(alarm.ctx.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
/// Set the rtc but panic if it's already been set
|
||||||
|
pub(crate) fn set_rtc(&self, rtc: &'static Rtc) {
|
||||||
|
critical_section::with(|cs| assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
/// Compute the approximate amount of time until the next alarm
|
||||||
|
fn time_until_next_alarm(&self) -> embassy_time::Duration {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let now = self.now() + 32;
|
||||||
|
|
||||||
|
embassy_time::Duration::from_ticks(
|
||||||
|
self.alarms
|
||||||
|
.borrow(cs)
|
||||||
|
.iter()
|
||||||
|
.map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now))
|
||||||
|
.min()
|
||||||
|
.unwrap_or(u64::MAX),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
/// Add the given offset to the current time
|
||||||
|
fn add_time(&self, offset: embassy_time::Duration) {
|
||||||
|
let offset = offset.as_ticks();
|
||||||
|
let cnt = T::regs_gp16().cnt().read().cnt() as u32;
|
||||||
|
let period = self.period.load(Ordering::SeqCst);
|
||||||
|
|
||||||
|
// Correct the race, if it exists
|
||||||
|
let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 {
|
||||||
|
period + 1
|
||||||
|
} else {
|
||||||
|
period
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalize to the full overflow
|
||||||
|
let period = (period / 2) * 2;
|
||||||
|
|
||||||
|
// Add the offset
|
||||||
|
let period = period + 2 * (offset / u16::MAX as u64) as u32;
|
||||||
|
let cnt = cnt + (offset % u16::MAX as u64) as u32;
|
||||||
|
|
||||||
|
let (cnt, period) = if cnt > u16::MAX as u32 {
|
||||||
|
(cnt - u16::MAX as u32, period + 2)
|
||||||
|
} else {
|
||||||
|
(cnt, period)
|
||||||
|
};
|
||||||
|
|
||||||
|
let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period };
|
||||||
|
|
||||||
|
self.period.store(period, Ordering::SeqCst);
|
||||||
|
T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16));
|
||||||
|
|
||||||
|
// Now, recompute all alarms
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
for i in 0..ALARM_COUNT {
|
||||||
|
let alarm_handle = unsafe { AlarmHandle::new(i as u8) };
|
||||||
|
let alarm = self.get_alarm(cs, alarm_handle);
|
||||||
|
|
||||||
|
self.set_alarm(alarm_handle, alarm.timestamp.get());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
/// Stop the wakeup alarm, if enabled, and add the appropriate offset
|
||||||
|
fn stop_wakeup_alarm(&self) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
if let Some(offset) = self.rtc.borrow(cs).get().unwrap().stop_wakeup_alarm() {
|
||||||
|
self.add_time(offset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
/// Pause the timer if ready; return err if not
|
||||||
|
pub(crate) fn pause_time(&self) -> Result<(), ()> {
|
||||||
|
/*
|
||||||
|
If the wakeup timer is currently running, then we need to stop it and
|
||||||
|
add the elapsed time to the current time
|
||||||
|
*/
|
||||||
|
self.stop_wakeup_alarm();
|
||||||
|
|
||||||
|
let time_until_next_alarm = self.time_until_next_alarm();
|
||||||
|
if time_until_next_alarm < embassy_time::Duration::from_millis(250) {
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
self.rtc
|
||||||
|
.borrow(cs)
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.start_wakeup_alarm(time_until_next_alarm);
|
||||||
|
});
|
||||||
|
|
||||||
|
T::regs_gp16().cr1().modify(|w| w.set_cen(false));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
/// Resume the timer with the given offset
|
||||||
|
pub(crate) fn resume_time(&self) {
|
||||||
|
self.stop_wakeup_alarm();
|
||||||
|
|
||||||
|
T::regs_gp16().cr1().modify(|w| w.set_cen(true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Driver for RtcDriver {
|
impl Driver for RtcDriver {
|
||||||
@ -329,6 +446,11 @@ impl Driver for RtcDriver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "low-power")]
|
||||||
|
pub(crate) fn get_driver() -> &'static RtcDriver {
|
||||||
|
&DRIVER
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn init() {
|
pub(crate) fn init() {
|
||||||
DRIVER.init()
|
DRIVER.init()
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub mod complementary_pwm;
|
pub mod complementary_pwm;
|
||||||
|
pub mod qei;
|
||||||
pub mod simple_pwm;
|
pub mod simple_pwm;
|
||||||
|
|
||||||
use stm32_metapac::timer::vals;
|
use stm32_metapac::timer::vals;
|
||||||
@ -211,6 +212,7 @@ macro_rules! impl_basic_16bit_timer {
|
|||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
let f = frequency.0;
|
let f = frequency.0;
|
||||||
let timer_f = Self::frequency().0;
|
let timer_f = Self::frequency().0;
|
||||||
|
assert!(f > 0);
|
||||||
let pclk_ticks_per_timer_period = timer_f / f;
|
let pclk_ticks_per_timer_period = timer_f / f;
|
||||||
let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into());
|
let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into());
|
||||||
let arr: u16 = unwrap!((pclk_ticks_per_timer_period / (u32::from(psc) + 1)).try_into());
|
let arr: u16 = unwrap!((pclk_ticks_per_timer_period / (u32::from(psc) + 1)).try_into());
|
||||||
@ -255,6 +257,7 @@ macro_rules! impl_32bit_timer {
|
|||||||
fn set_frequency(&mut self, frequency: Hertz) {
|
fn set_frequency(&mut self, frequency: Hertz) {
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
let f = frequency.0;
|
let f = frequency.0;
|
||||||
|
assert!(f > 0);
|
||||||
let timer_f = Self::frequency().0;
|
let timer_f = Self::frequency().0;
|
||||||
let pclk_ticks_per_timer_period = (timer_f / f) as u64;
|
let pclk_ticks_per_timer_period = (timer_f / f) as u64;
|
||||||
let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into());
|
let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into());
|
||||||
|
96
embassy-stm32/src/timer/qei.rs
Normal file
96
embassy-stm32/src/timer/qei.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::gpio::sealed::AFType;
|
||||||
|
use crate::gpio::AnyPin;
|
||||||
|
use crate::Peripheral;
|
||||||
|
|
||||||
|
pub enum Direction {
|
||||||
|
Upcounting,
|
||||||
|
Downcounting,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Ch1;
|
||||||
|
pub struct Ch2;
|
||||||
|
|
||||||
|
pub struct QeiPin<'d, Perip, Channel> {
|
||||||
|
_pin: PeripheralRef<'d, AnyPin>,
|
||||||
|
phantom: PhantomData<(Perip, Channel)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! channel_impl {
|
||||||
|
($new_chx:ident, $channel:ident, $pin_trait:ident) => {
|
||||||
|
impl<'d, Perip: CaptureCompare16bitInstance> QeiPin<'d, Perip, $channel> {
|
||||||
|
pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd) -> Self {
|
||||||
|
into_ref!(pin);
|
||||||
|
critical_section::with(|_| {
|
||||||
|
pin.set_low();
|
||||||
|
pin.set_as_af(pin.af_num(), AFType::Input);
|
||||||
|
#[cfg(gpio_v2)]
|
||||||
|
pin.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
|
});
|
||||||
|
QeiPin {
|
||||||
|
_pin: pin.map_into(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
channel_impl!(new_ch1, Ch1, Channel1Pin);
|
||||||
|
channel_impl!(new_ch2, Ch2, Channel2Pin);
|
||||||
|
|
||||||
|
pub struct SimplePwm<'d, T> {
|
||||||
|
_inner: PeripheralRef<'d, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
||||||
|
pub fn new(tim: impl Peripheral<P = T> + 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self {
|
||||||
|
Self::new_inner(tim)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_inner(tim: impl Peripheral<P = T> + 'd) -> Self {
|
||||||
|
into_ref!(tim);
|
||||||
|
|
||||||
|
T::enable();
|
||||||
|
<T as crate::rcc::sealed::RccPeripheral>::reset();
|
||||||
|
|
||||||
|
// Configure TxC1 and TxC2 as captures
|
||||||
|
T::regs_gp16().ccmr_input(0).modify(|w| {
|
||||||
|
w.set_ccs(0, vals::CcmrInputCcs::TI4);
|
||||||
|
w.set_ccs(1, vals::CcmrInputCcs::TI4);
|
||||||
|
});
|
||||||
|
|
||||||
|
// enable and configure to capture on rising edge
|
||||||
|
T::regs_gp16().ccer().modify(|w| {
|
||||||
|
w.set_cce(0, true);
|
||||||
|
w.set_cce(1, true);
|
||||||
|
|
||||||
|
w.set_ccp(0, false);
|
||||||
|
w.set_ccp(1, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
T::regs_gp16().smcr().modify(|w| {
|
||||||
|
w.set_sms(vals::Sms::ENCODER_MODE_3);
|
||||||
|
});
|
||||||
|
|
||||||
|
T::regs_gp16().arr().modify(|w| w.set_arr(u16::MAX));
|
||||||
|
T::regs_gp16().cr1().modify(|w| w.set_cen(true));
|
||||||
|
|
||||||
|
Self { _inner: tim }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_direction(&self) -> Direction {
|
||||||
|
match T::regs_gp16().cr1().read().dir() {
|
||||||
|
vals::Dir::DOWN => Direction::Downcounting,
|
||||||
|
vals::Dir::UP => Direction::Upcounting,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(&self) -> u16 {
|
||||||
|
T::regs_gp16().cnt().read().cnt()
|
||||||
|
}
|
||||||
|
}
|
@ -809,45 +809,57 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx:
|
|||||||
Kind::Uart => (1, 0x10, 0x1_0000),
|
Kind::Uart => (1, 0x10, 0x1_0000),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 {
|
||||||
|
// The calculation to be done to get the BRR is `mul * pclk / presc / baud`
|
||||||
|
// To do this in 32-bit only we can't multiply `mul` and `pclk`
|
||||||
|
let clock = pclk / presc;
|
||||||
|
|
||||||
|
// The mul is applied as the last operation to prevent overflow
|
||||||
|
let brr = clock / baud * mul;
|
||||||
|
|
||||||
|
// The BRR calculation will be a bit off because of integer rounding.
|
||||||
|
// Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul.
|
||||||
|
let rounding = ((clock % baud) * mul + (baud / 2)) / baud;
|
||||||
|
|
||||||
|
brr + rounding
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(usart_v1))]
|
#[cfg(not(usart_v1))]
|
||||||
let mut over8 = false;
|
let mut over8 = false;
|
||||||
let mut found = None;
|
let mut found_brr = None;
|
||||||
for &(presc, _presc_val) in &DIVS {
|
for &(presc, _presc_val) in &DIVS {
|
||||||
let denom = (config.baudrate * presc as u32) as u64;
|
let brr = calculate_brr(config.baudrate, pclk_freq.0, presc as u32, mul);
|
||||||
let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom;
|
|
||||||
trace!(
|
trace!(
|
||||||
"USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})",
|
"USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})",
|
||||||
presc,
|
presc,
|
||||||
div,
|
brr,
|
||||||
div >> 4,
|
brr >> 4,
|
||||||
div & 0x0F
|
brr & 0x0F
|
||||||
);
|
);
|
||||||
|
|
||||||
if div < brr_min {
|
if brr < brr_min {
|
||||||
#[cfg(not(usart_v1))]
|
#[cfg(not(usart_v1))]
|
||||||
if div * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) {
|
if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) {
|
||||||
over8 = true;
|
over8 = true;
|
||||||
let div = div as u32;
|
r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07)));
|
||||||
r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07)));
|
|
||||||
#[cfg(usart_v4)]
|
#[cfg(usart_v4)]
|
||||||
r.presc().write(|w| w.set_prescaler(_presc_val));
|
r.presc().write(|w| w.set_prescaler(_presc_val));
|
||||||
found = Some(div);
|
found_brr = Some(brr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
panic!("USART: baudrate too high");
|
panic!("USART: baudrate too high");
|
||||||
}
|
}
|
||||||
|
|
||||||
if div < brr_max {
|
if brr < brr_max {
|
||||||
let div = div as u32;
|
r.brr().write_value(regs::Brr(brr));
|
||||||
r.brr().write_value(regs::Brr(div));
|
|
||||||
#[cfg(usart_v4)]
|
#[cfg(usart_v4)]
|
||||||
r.presc().write(|w| w.set_prescaler(_presc_val));
|
r.presc().write(|w| w.set_prescaler(_presc_val));
|
||||||
found = Some(div);
|
found_brr = Some(brr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let div = found.expect("USART: baudrate too low");
|
let brr = found_brr.expect("USART: baudrate too low");
|
||||||
|
|
||||||
#[cfg(not(usart_v1))]
|
#[cfg(not(usart_v1))]
|
||||||
let oversampling = if over8 { "8 bit" } else { "16 bit" };
|
let oversampling = if over8 { "8 bit" } else { "16 bit" };
|
||||||
@ -857,7 +869,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx:
|
|||||||
"Using {} oversampling, desired baudrate: {}, actual baudrate: {}",
|
"Using {} oversampling, desired baudrate: {}, actual baudrate: {}",
|
||||||
oversampling,
|
oversampling,
|
||||||
config.baudrate,
|
config.baudrate,
|
||||||
(pclk_freq.0 * mul as u32) / div
|
pclk_freq.0 / brr * mul
|
||||||
);
|
);
|
||||||
|
|
||||||
r.cr2().write(|w| {
|
r.cr2().write(|w| {
|
||||||
|
@ -65,6 +65,13 @@ where
|
|||||||
pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {
|
pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {
|
||||||
self.channel.try_send(message)
|
self.channel.try_send(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allows a poll_fn to poll until the channel is ready to send
|
||||||
|
///
|
||||||
|
/// See [`Channel::poll_ready_to_send()`]
|
||||||
|
pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
|
self.channel.poll_ready_to_send(cx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send-only access to a [`Channel`] without knowing channel size.
|
/// Send-only access to a [`Channel`] without knowing channel size.
|
||||||
@ -106,6 +113,13 @@ impl<'ch, T> DynamicSender<'ch, T> {
|
|||||||
pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {
|
pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {
|
||||||
self.channel.try_send_with_context(message, None)
|
self.channel.try_send_with_context(message, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allows a poll_fn to poll until the channel is ready to send
|
||||||
|
///
|
||||||
|
/// See [`Channel::poll_ready_to_send()`]
|
||||||
|
pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
|
self.channel.poll_ready_to_send(cx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive-only access to a [`Channel`].
|
/// Receive-only access to a [`Channel`].
|
||||||
@ -133,16 +147,30 @@ where
|
|||||||
{
|
{
|
||||||
/// Receive the next value.
|
/// Receive the next value.
|
||||||
///
|
///
|
||||||
/// See [`Channel::recv()`].
|
/// See [`Channel::receive()`].
|
||||||
pub fn recv(&self) -> RecvFuture<'_, M, T, N> {
|
pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> {
|
||||||
self.channel.recv()
|
self.channel.receive()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to immediately receive the next value.
|
/// Attempt to immediately receive the next value.
|
||||||
///
|
///
|
||||||
/// See [`Channel::try_recv()`]
|
/// See [`Channel::try_receive()`]
|
||||||
pub fn try_recv(&self) -> Result<T, TryRecvError> {
|
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
|
||||||
self.channel.try_recv()
|
self.channel.try_receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows a poll_fn to poll until the channel is ready to receive
|
||||||
|
///
|
||||||
|
/// See [`Channel::poll_ready_to_receive()`]
|
||||||
|
pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
|
self.channel.poll_ready_to_receive(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Poll the channel for the next item
|
||||||
|
///
|
||||||
|
/// See [`Channel::poll_receive()`]
|
||||||
|
pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
|
||||||
|
self.channel.poll_receive(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,16 +190,30 @@ impl<'ch, T> Copy for DynamicReceiver<'ch, T> {}
|
|||||||
impl<'ch, T> DynamicReceiver<'ch, T> {
|
impl<'ch, T> DynamicReceiver<'ch, T> {
|
||||||
/// Receive the next value.
|
/// Receive the next value.
|
||||||
///
|
///
|
||||||
/// See [`Channel::recv()`].
|
/// See [`Channel::receive()`].
|
||||||
pub fn recv(&self) -> DynamicRecvFuture<'_, T> {
|
pub fn receive(&self) -> DynamicReceiveFuture<'_, T> {
|
||||||
DynamicRecvFuture { channel: self.channel }
|
DynamicReceiveFuture { channel: self.channel }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to immediately receive the next value.
|
/// Attempt to immediately receive the next value.
|
||||||
///
|
///
|
||||||
/// See [`Channel::try_recv()`]
|
/// See [`Channel::try_receive()`]
|
||||||
pub fn try_recv(&self) -> Result<T, TryRecvError> {
|
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
|
||||||
self.channel.try_recv_with_context(None)
|
self.channel.try_receive_with_context(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows a poll_fn to poll until the channel is ready to receive
|
||||||
|
///
|
||||||
|
/// See [`Channel::poll_ready_to_receive()`]
|
||||||
|
pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
|
self.channel.poll_ready_to_receive(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Poll the channel for the next item
|
||||||
|
///
|
||||||
|
/// See [`Channel::poll_receive()`]
|
||||||
|
pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
|
||||||
|
self.channel.poll_receive(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,42 +226,39 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Future returned by [`Channel::recv`] and [`Receiver::recv`].
|
/// Future returned by [`Channel::receive`] and [`Receiver::receive`].
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||||
pub struct RecvFuture<'ch, M, T, const N: usize>
|
pub struct ReceiveFuture<'ch, M, T, const N: usize>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
{
|
{
|
||||||
channel: &'ch Channel<M, T, N>,
|
channel: &'ch Channel<M, T, N>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ch, M, T, const N: usize> Future for RecvFuture<'ch, M, T, N>
|
impl<'ch, M, T, const N: usize> Future for ReceiveFuture<'ch, M, T, N>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
{
|
{
|
||||||
type Output = T;
|
type Output = T;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
||||||
match self.channel.try_recv_with_context(Some(cx)) {
|
self.channel.poll_receive(cx)
|
||||||
Ok(v) => Poll::Ready(v),
|
|
||||||
Err(TryRecvError::Empty) => Poll::Pending,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Future returned by [`DynamicReceiver::recv`].
|
/// Future returned by [`DynamicReceiver::receive`].
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||||
pub struct DynamicRecvFuture<'ch, T> {
|
pub struct DynamicReceiveFuture<'ch, T> {
|
||||||
channel: &'ch dyn DynamicChannel<T>,
|
channel: &'ch dyn DynamicChannel<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ch, T> Future for DynamicRecvFuture<'ch, T> {
|
impl<'ch, T> Future for DynamicReceiveFuture<'ch, T> {
|
||||||
type Output = T;
|
type Output = T;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
||||||
match self.channel.try_recv_with_context(Some(cx)) {
|
match self.channel.try_receive_with_context(Some(cx)) {
|
||||||
Ok(v) => Poll::Ready(v),
|
Ok(v) => Poll::Ready(v),
|
||||||
Err(TryRecvError::Empty) => Poll::Pending,
|
Err(TryReceiveError::Empty) => Poll::Pending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,13 +324,18 @@ impl<'ch, T> Unpin for DynamicSendFuture<'ch, T> {}
|
|||||||
trait DynamicChannel<T> {
|
trait DynamicChannel<T> {
|
||||||
fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>>;
|
fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>>;
|
||||||
|
|
||||||
fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryRecvError>;
|
fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>;
|
||||||
|
|
||||||
|
fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()>;
|
||||||
|
fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()>;
|
||||||
|
|
||||||
|
fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error returned by [`try_recv`](Channel::try_recv).
|
/// Error returned by [`try_receive`](Channel::try_receive).
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum TryRecvError {
|
pub enum TryReceiveError {
|
||||||
/// A message could not be received because the channel is empty.
|
/// A message could not be received because the channel is empty.
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
@ -320,11 +364,11 @@ impl<T, const N: usize> ChannelState<T, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_recv(&mut self) -> Result<T, TryRecvError> {
|
fn try_receive(&mut self) -> Result<T, TryReceiveError> {
|
||||||
self.try_recv_with_context(None)
|
self.try_receive_with_context(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_recv_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result<T, TryRecvError> {
|
fn try_receive_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
|
||||||
if self.queue.is_full() {
|
if self.queue.is_full() {
|
||||||
self.senders_waker.wake();
|
self.senders_waker.wake();
|
||||||
}
|
}
|
||||||
@ -335,14 +379,31 @@ impl<T, const N: usize> ChannelState<T, N> {
|
|||||||
if let Some(cx) = cx {
|
if let Some(cx) = cx {
|
||||||
self.receiver_waker.register(cx.waker());
|
self.receiver_waker.register(cx.waker());
|
||||||
}
|
}
|
||||||
Err(TryRecvError::Empty)
|
Err(TryReceiveError::Empty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> bool {
|
fn poll_receive(&mut self, cx: &mut Context<'_>) -> Poll<T> {
|
||||||
|
if self.queue.is_full() {
|
||||||
|
self.senders_waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(message) = self.queue.pop_front() {
|
||||||
|
Poll::Ready(message)
|
||||||
|
} else {
|
||||||
|
self.receiver_waker.register(cx.waker());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
self.receiver_waker.register(cx.waker());
|
self.receiver_waker.register(cx.waker());
|
||||||
|
|
||||||
!self.queue.is_empty()
|
if !self.queue.is_empty() {
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_send(&mut self, message: T) -> Result<(), TrySendError<T>> {
|
fn try_send(&mut self, message: T) -> Result<(), TrySendError<T>> {
|
||||||
@ -364,10 +425,14 @@ impl<T, const N: usize> ChannelState<T, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> bool {
|
fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
self.senders_waker.register(cx.waker());
|
self.senders_waker.register(cx.waker());
|
||||||
|
|
||||||
!self.queue.is_full()
|
if !self.queue.is_full() {
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,8 +474,13 @@ where
|
|||||||
self.inner.lock(|rc| f(&mut *rc.borrow_mut()))
|
self.inner.lock(|rc| f(&mut *rc.borrow_mut()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryRecvError> {
|
fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
|
||||||
self.lock(|c| c.try_recv_with_context(cx))
|
self.lock(|c| c.try_receive_with_context(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Poll the channel for the next message
|
||||||
|
pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
|
||||||
|
self.lock(|c| c.poll_receive(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {
|
fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {
|
||||||
@ -418,12 +488,12 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Allows a poll_fn to poll until the channel is ready to receive
|
/// Allows a poll_fn to poll until the channel is ready to receive
|
||||||
pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool {
|
pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
self.lock(|c| c.poll_ready_to_receive(cx))
|
self.lock(|c| c.poll_ready_to_receive(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows a poll_fn to poll until the channel is ready to send
|
/// Allows a poll_fn to poll until the channel is ready to send
|
||||||
pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool {
|
pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
self.lock(|c| c.poll_ready_to_send(cx))
|
self.lock(|c| c.poll_ready_to_send(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,16 +536,16 @@ where
|
|||||||
///
|
///
|
||||||
/// If there are no messages in the channel's buffer, this method will
|
/// If there are no messages in the channel's buffer, this method will
|
||||||
/// wait until a message is sent.
|
/// wait until a message is sent.
|
||||||
pub fn recv(&self) -> RecvFuture<'_, M, T, N> {
|
pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> {
|
||||||
RecvFuture { channel: self }
|
ReceiveFuture { channel: self }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to immediately receive a message.
|
/// Attempt to immediately receive a message.
|
||||||
///
|
///
|
||||||
/// This method will either receive a message from the channel immediately or return an error
|
/// This method will either receive a message from the channel immediately or return an error
|
||||||
/// if the channel is empty.
|
/// if the channel is empty.
|
||||||
pub fn try_recv(&self) -> Result<T, TryRecvError> {
|
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
|
||||||
self.lock(|c| c.try_recv())
|
self.lock(|c| c.try_receive())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,8 +559,20 @@ where
|
|||||||
Channel::try_send_with_context(self, m, cx)
|
Channel::try_send_with_context(self, m, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryRecvError> {
|
fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
|
||||||
Channel::try_recv_with_context(self, cx)
|
Channel::try_receive_with_context(self, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
|
Channel::poll_ready_to_send(self, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
|
||||||
|
Channel::poll_ready_to_receive(self, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
|
||||||
|
Channel::poll_receive(self, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,15 +616,15 @@ mod tests {
|
|||||||
fn receiving_once_with_one_send() {
|
fn receiving_once_with_one_send() {
|
||||||
let mut c = ChannelState::<u32, 3>::new();
|
let mut c = ChannelState::<u32, 3>::new();
|
||||||
assert!(c.try_send(1).is_ok());
|
assert!(c.try_send(1).is_ok());
|
||||||
assert_eq!(c.try_recv().unwrap(), 1);
|
assert_eq!(c.try_receive().unwrap(), 1);
|
||||||
assert_eq!(capacity(&c), 3);
|
assert_eq!(capacity(&c), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn receiving_when_empty() {
|
fn receiving_when_empty() {
|
||||||
let mut c = ChannelState::<u32, 3>::new();
|
let mut c = ChannelState::<u32, 3>::new();
|
||||||
match c.try_recv() {
|
match c.try_receive() {
|
||||||
Err(TryRecvError::Empty) => assert!(true),
|
Err(TryReceiveError::Empty) => assert!(true),
|
||||||
_ => assert!(false),
|
_ => assert!(false),
|
||||||
}
|
}
|
||||||
assert_eq!(capacity(&c), 3);
|
assert_eq!(capacity(&c), 3);
|
||||||
@ -552,7 +634,7 @@ mod tests {
|
|||||||
fn simple_send_and_receive() {
|
fn simple_send_and_receive() {
|
||||||
let c = Channel::<NoopRawMutex, u32, 3>::new();
|
let c = Channel::<NoopRawMutex, u32, 3>::new();
|
||||||
assert!(c.try_send(1).is_ok());
|
assert!(c.try_send(1).is_ok());
|
||||||
assert_eq!(c.try_recv().unwrap(), 1);
|
assert_eq!(c.try_receive().unwrap(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -572,7 +654,7 @@ mod tests {
|
|||||||
let r: DynamicReceiver<'_, u32> = c.receiver().into();
|
let r: DynamicReceiver<'_, u32> = c.receiver().into();
|
||||||
|
|
||||||
assert!(s.try_send(1).is_ok());
|
assert!(s.try_send(1).is_ok());
|
||||||
assert_eq!(r.try_recv().unwrap(), 1);
|
assert_eq!(r.try_receive().unwrap(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[futures_test::test]
|
#[futures_test::test]
|
||||||
@ -587,14 +669,14 @@ mod tests {
|
|||||||
assert!(c2.try_send(1).is_ok());
|
assert!(c2.try_send(1).is_ok());
|
||||||
})
|
})
|
||||||
.is_ok());
|
.is_ok());
|
||||||
assert_eq!(c.recv().await, 1);
|
assert_eq!(c.receive().await, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[futures_test::test]
|
#[futures_test::test]
|
||||||
async fn sender_send_completes_if_capacity() {
|
async fn sender_send_completes_if_capacity() {
|
||||||
let c = Channel::<CriticalSectionRawMutex, u32, 1>::new();
|
let c = Channel::<CriticalSectionRawMutex, u32, 1>::new();
|
||||||
c.send(1).await;
|
c.send(1).await;
|
||||||
assert_eq!(c.recv().await, 1);
|
assert_eq!(c.receive().await, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[futures_test::test]
|
#[futures_test::test]
|
||||||
@ -612,11 +694,11 @@ mod tests {
|
|||||||
// Wish I could think of a means of determining that the async send is waiting instead.
|
// Wish I could think of a means of determining that the async send is waiting instead.
|
||||||
// However, I've used the debugger to observe that the send does indeed wait.
|
// However, I've used the debugger to observe that the send does indeed wait.
|
||||||
Delay::new(Duration::from_millis(500)).await;
|
Delay::new(Duration::from_millis(500)).await;
|
||||||
assert_eq!(c.recv().await, 1);
|
assert_eq!(c.receive().await, 1);
|
||||||
assert!(executor
|
assert!(executor
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
c.recv().await;
|
c.receive().await;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.is_ok());
|
.is_ok());
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
//! Async byte stream pipe.
|
//! Async byte stream pipe.
|
||||||
|
|
||||||
use core::cell::RefCell;
|
use core::cell::{RefCell, UnsafeCell};
|
||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
|
use core::ops::Range;
|
||||||
use core::pin::Pin;
|
use core::pin::Pin;
|
||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
@ -82,17 +83,6 @@ where
|
|||||||
pipe: &'p Pipe<M, N>,
|
pipe: &'p Pipe<M, N>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'p, M, const N: usize> Clone for Reader<'p, M, N>
|
|
||||||
where
|
|
||||||
M: RawMutex,
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Reader { pipe: self.pipe }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'p, M, const N: usize> Copy for Reader<'p, M, N> where M: RawMutex {}
|
|
||||||
|
|
||||||
impl<'p, M, const N: usize> Reader<'p, M, N>
|
impl<'p, M, const N: usize> Reader<'p, M, N>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -110,6 +100,29 @@ where
|
|||||||
pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> {
|
pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> {
|
||||||
self.pipe.try_read(buf)
|
self.pipe.try_read(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
|
||||||
|
///
|
||||||
|
/// If no bytes are currently available to read, this function waits until at least one byte is available.
|
||||||
|
///
|
||||||
|
/// If the reader is at end-of-file (EOF), an empty slice is returned.
|
||||||
|
pub fn fill_buf(&mut self) -> FillBufFuture<'_, M, N> {
|
||||||
|
FillBufFuture { pipe: Some(self.pipe) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try returning contents of the internal buffer.
|
||||||
|
///
|
||||||
|
/// If no bytes are currently available to read, this function returns `Err(TryReadError::Empty)`.
|
||||||
|
///
|
||||||
|
/// If the reader is at end-of-file (EOF), an empty slice is returned.
|
||||||
|
pub fn try_fill_buf(&mut self) -> Result<&[u8], TryReadError> {
|
||||||
|
unsafe { self.pipe.try_fill_buf_with_context(None) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
|
||||||
|
pub fn consume(&mut self, amt: usize) {
|
||||||
|
self.pipe.consume(amt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Future returned by [`Pipe::read`] and [`Reader::read`].
|
/// Future returned by [`Pipe::read`] and [`Reader::read`].
|
||||||
@ -138,6 +151,35 @@ where
|
|||||||
|
|
||||||
impl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {}
|
impl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {}
|
||||||
|
|
||||||
|
/// Future returned by [`Pipe::fill_buf`] and [`Reader::fill_buf`].
|
||||||
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||||
|
pub struct FillBufFuture<'p, M, const N: usize>
|
||||||
|
where
|
||||||
|
M: RawMutex,
|
||||||
|
{
|
||||||
|
pipe: Option<&'p Pipe<M, N>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'p, M, const N: usize> Future for FillBufFuture<'p, M, N>
|
||||||
|
where
|
||||||
|
M: RawMutex,
|
||||||
|
{
|
||||||
|
type Output = &'p [u8];
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let pipe = self.pipe.take().unwrap();
|
||||||
|
match unsafe { pipe.try_fill_buf_with_context(Some(cx)) } {
|
||||||
|
Ok(buf) => Poll::Ready(buf),
|
||||||
|
Err(TryReadError::Empty) => {
|
||||||
|
self.pipe = Some(pipe);
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'p, M, const N: usize> Unpin for FillBufFuture<'p, M, N> where M: RawMutex {}
|
||||||
|
|
||||||
/// Error returned by [`try_read`](Pipe::try_read).
|
/// Error returned by [`try_read`](Pipe::try_read).
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
@ -162,67 +204,24 @@ struct PipeState<const N: usize> {
|
|||||||
write_waker: WakerRegistration,
|
write_waker: WakerRegistration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> PipeState<N> {
|
#[repr(transparent)]
|
||||||
const fn new() -> Self {
|
struct Buffer<const N: usize>(UnsafeCell<[u8; N]>);
|
||||||
PipeState {
|
|
||||||
buffer: RingBuffer::new(),
|
impl<const N: usize> Buffer<N> {
|
||||||
read_waker: WakerRegistration::new(),
|
unsafe fn get<'a>(&self, r: Range<usize>) -> &'a [u8] {
|
||||||
write_waker: WakerRegistration::new(),
|
let p = self.0.get() as *const u8;
|
||||||
}
|
core::slice::from_raw_parts(p.add(r.start), r.end - r.start)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
unsafe fn get_mut<'a>(&self, r: Range<usize>) -> &'a mut [u8] {
|
||||||
self.buffer.clear();
|
let p = self.0.get() as *mut u8;
|
||||||
self.write_waker.wake();
|
core::slice::from_raw_parts_mut(p.add(r.start), r.end - r.start)
|
||||||
}
|
|
||||||
|
|
||||||
fn try_read(&mut self, buf: &mut [u8]) -> Result<usize, TryReadError> {
|
|
||||||
self.try_read_with_context(None, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_read_with_context(&mut self, cx: Option<&mut Context<'_>>, buf: &mut [u8]) -> Result<usize, TryReadError> {
|
|
||||||
if self.buffer.is_full() {
|
|
||||||
self.write_waker.wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
let available = self.buffer.pop_buf();
|
|
||||||
if available.is_empty() {
|
|
||||||
if let Some(cx) = cx {
|
|
||||||
self.read_waker.register(cx.waker());
|
|
||||||
}
|
|
||||||
return Err(TryReadError::Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = available.len().min(buf.len());
|
|
||||||
buf[..n].copy_from_slice(&available[..n]);
|
|
||||||
self.buffer.pop(n);
|
|
||||||
Ok(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_write(&mut self, buf: &[u8]) -> Result<usize, TryWriteError> {
|
|
||||||
self.try_write_with_context(None, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_write_with_context(&mut self, cx: Option<&mut Context<'_>>, buf: &[u8]) -> Result<usize, TryWriteError> {
|
|
||||||
if self.buffer.is_empty() {
|
|
||||||
self.read_waker.wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
let available = self.buffer.push_buf();
|
|
||||||
if available.is_empty() {
|
|
||||||
if let Some(cx) = cx {
|
|
||||||
self.write_waker.register(cx.waker());
|
|
||||||
}
|
|
||||||
return Err(TryWriteError::Full);
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = available.len().min(buf.len());
|
|
||||||
available[..n].copy_from_slice(&buf[..n]);
|
|
||||||
self.buffer.push(n);
|
|
||||||
Ok(n)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl<const N: usize> Send for Buffer<N> {}
|
||||||
|
unsafe impl<const N: usize> Sync for Buffer<N> {}
|
||||||
|
|
||||||
/// A bounded byte-oriented pipe for communicating between asynchronous tasks
|
/// A bounded byte-oriented pipe for communicating between asynchronous tasks
|
||||||
/// with backpressure.
|
/// with backpressure.
|
||||||
///
|
///
|
||||||
@ -234,6 +233,7 @@ pub struct Pipe<M, const N: usize>
|
|||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
{
|
{
|
||||||
|
buf: Buffer<N>,
|
||||||
inner: Mutex<M, RefCell<PipeState<N>>>,
|
inner: Mutex<M, RefCell<PipeState<N>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +252,12 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Mutex::new(RefCell::new(PipeState::new())),
|
buf: Buffer(UnsafeCell::new([0; N])),
|
||||||
|
inner: Mutex::new(RefCell::new(PipeState {
|
||||||
|
buffer: RingBuffer::new(),
|
||||||
|
read_waker: WakerRegistration::new(),
|
||||||
|
write_waker: WakerRegistration::new(),
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,21 +266,91 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn try_read_with_context(&self, cx: Option<&mut Context<'_>>, buf: &mut [u8]) -> Result<usize, TryReadError> {
|
fn try_read_with_context(&self, cx: Option<&mut Context<'_>>, buf: &mut [u8]) -> Result<usize, TryReadError> {
|
||||||
self.lock(|c| c.try_read_with_context(cx, buf))
|
self.inner.lock(|rc: &RefCell<PipeState<N>>| {
|
||||||
|
let s = &mut *rc.borrow_mut();
|
||||||
|
|
||||||
|
if s.buffer.is_full() {
|
||||||
|
s.write_waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
let available = unsafe { self.buf.get(s.buffer.pop_buf()) };
|
||||||
|
if available.is_empty() {
|
||||||
|
if let Some(cx) = cx {
|
||||||
|
s.read_waker.register(cx.waker());
|
||||||
|
}
|
||||||
|
return Err(TryReadError::Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = available.len().min(buf.len());
|
||||||
|
buf[..n].copy_from_slice(&available[..n]);
|
||||||
|
s.buffer.pop(n);
|
||||||
|
Ok(n)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// safety: While the returned slice is alive,
|
||||||
|
// no `read` or `consume` methods in the pipe must be called.
|
||||||
|
unsafe fn try_fill_buf_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<&[u8], TryReadError> {
|
||||||
|
self.inner.lock(|rc: &RefCell<PipeState<N>>| {
|
||||||
|
let s = &mut *rc.borrow_mut();
|
||||||
|
|
||||||
|
if s.buffer.is_full() {
|
||||||
|
s.write_waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
let available = unsafe { self.buf.get(s.buffer.pop_buf()) };
|
||||||
|
if available.is_empty() {
|
||||||
|
if let Some(cx) = cx {
|
||||||
|
s.read_waker.register(cx.waker());
|
||||||
|
}
|
||||||
|
return Err(TryReadError::Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(available)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&self, amt: usize) {
|
||||||
|
self.inner.lock(|rc: &RefCell<PipeState<N>>| {
|
||||||
|
let s = &mut *rc.borrow_mut();
|
||||||
|
let available = s.buffer.pop_buf();
|
||||||
|
assert!(amt <= available.len());
|
||||||
|
s.buffer.pop(amt);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_write_with_context(&self, cx: Option<&mut Context<'_>>, buf: &[u8]) -> Result<usize, TryWriteError> {
|
fn try_write_with_context(&self, cx: Option<&mut Context<'_>>, buf: &[u8]) -> Result<usize, TryWriteError> {
|
||||||
self.lock(|c| c.try_write_with_context(cx, buf))
|
self.inner.lock(|rc: &RefCell<PipeState<N>>| {
|
||||||
|
let s = &mut *rc.borrow_mut();
|
||||||
|
|
||||||
|
if s.buffer.is_empty() {
|
||||||
|
s.read_waker.wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a writer for this pipe.
|
let available = unsafe { self.buf.get_mut(s.buffer.push_buf()) };
|
||||||
pub fn writer(&self) -> Writer<'_, M, N> {
|
if available.is_empty() {
|
||||||
Writer { pipe: self }
|
if let Some(cx) = cx {
|
||||||
|
s.write_waker.register(cx.waker());
|
||||||
|
}
|
||||||
|
return Err(TryWriteError::Full);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reader for this pipe.
|
let n = available.len().min(buf.len());
|
||||||
pub fn reader(&self) -> Reader<'_, M, N> {
|
available[..n].copy_from_slice(&buf[..n]);
|
||||||
Reader { pipe: self }
|
s.buffer.push(n);
|
||||||
|
Ok(n)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split this pipe into a BufRead-capable reader and a writer.
|
||||||
|
///
|
||||||
|
/// The reader and writer borrow the current pipe mutably, so it is not
|
||||||
|
/// possible to use it directly while they exist. This is needed because
|
||||||
|
/// implementing `BufRead` requires there is a single reader.
|
||||||
|
///
|
||||||
|
/// The writer is cloneable, the reader is not.
|
||||||
|
pub fn split(&mut self) -> (Reader<'_, M, N>, Writer<'_, M, N>) {
|
||||||
|
(Reader { pipe: self }, Writer { pipe: self })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write some bytes to the pipe.
|
/// Write some bytes to the pipe.
|
||||||
@ -312,7 +387,7 @@ where
|
|||||||
/// or return an error if the pipe is empty. See [`write`](Self::write) for a variant
|
/// or return an error if the pipe is empty. See [`write`](Self::write) for a variant
|
||||||
/// that waits instead of returning an error.
|
/// that waits instead of returning an error.
|
||||||
pub fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError> {
|
pub fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError> {
|
||||||
self.lock(|c| c.try_write(buf))
|
self.try_write_with_context(None, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read some bytes from the pipe.
|
/// Read some bytes from the pipe.
|
||||||
@ -339,12 +414,17 @@ where
|
|||||||
/// or return an error if the pipe is empty. See [`read`](Self::read) for a variant
|
/// or return an error if the pipe is empty. See [`read`](Self::read) for a variant
|
||||||
/// that waits instead of returning an error.
|
/// that waits instead of returning an error.
|
||||||
pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> {
|
pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> {
|
||||||
self.lock(|c| c.try_read(buf))
|
self.try_read_with_context(None, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the data in the pipe's buffer.
|
/// Clear the data in the pipe's buffer.
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
self.lock(|c| c.clear())
|
self.inner.lock(|rc: &RefCell<PipeState<N>>| {
|
||||||
|
let s = &mut *rc.borrow_mut();
|
||||||
|
|
||||||
|
s.buffer.clear();
|
||||||
|
s.write_waker.wake();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return whether the pipe is full (no free space in the buffer)
|
/// Return whether the pipe is full (no free space in the buffer)
|
||||||
@ -433,6 +513,16 @@ mod io_impls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<M: RawMutex, const N: usize> embedded_io_async::BufRead for Reader<'_, M, N> {
|
||||||
|
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
||||||
|
Ok(Reader::fill_buf(self).await)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&mut self, amt: usize) {
|
||||||
|
Reader::consume(self, amt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<M: RawMutex, const N: usize> embedded_io_async::ErrorType for Writer<'_, M, N> {
|
impl<M: RawMutex, const N: usize> embedded_io_async::ErrorType for Writer<'_, M, N> {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
}
|
}
|
||||||
@ -457,43 +547,39 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
|
use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
|
||||||
|
|
||||||
fn capacity<const N: usize>(c: &PipeState<N>) -> usize {
|
|
||||||
N - c.buffer.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn writing_once() {
|
fn writing_once() {
|
||||||
let mut c = PipeState::<3>::new();
|
let c = Pipe::<NoopRawMutex, 3>::new();
|
||||||
assert!(c.try_write(&[1]).is_ok());
|
assert!(c.try_write(&[1]).is_ok());
|
||||||
assert_eq!(capacity(&c), 2);
|
assert_eq!(c.free_capacity(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn writing_when_full() {
|
fn writing_when_full() {
|
||||||
let mut c = PipeState::<3>::new();
|
let c = Pipe::<NoopRawMutex, 3>::new();
|
||||||
assert_eq!(c.try_write(&[42]), Ok(1));
|
assert_eq!(c.try_write(&[42]), Ok(1));
|
||||||
assert_eq!(c.try_write(&[43]), Ok(1));
|
assert_eq!(c.try_write(&[43]), Ok(1));
|
||||||
assert_eq!(c.try_write(&[44]), Ok(1));
|
assert_eq!(c.try_write(&[44]), Ok(1));
|
||||||
assert_eq!(c.try_write(&[45]), Err(TryWriteError::Full));
|
assert_eq!(c.try_write(&[45]), Err(TryWriteError::Full));
|
||||||
assert_eq!(capacity(&c), 0);
|
assert_eq!(c.free_capacity(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn receiving_once_with_one_send() {
|
fn receiving_once_with_one_send() {
|
||||||
let mut c = PipeState::<3>::new();
|
let c = Pipe::<NoopRawMutex, 3>::new();
|
||||||
assert!(c.try_write(&[42]).is_ok());
|
assert!(c.try_write(&[42]).is_ok());
|
||||||
let mut buf = [0; 16];
|
let mut buf = [0; 16];
|
||||||
assert_eq!(c.try_read(&mut buf), Ok(1));
|
assert_eq!(c.try_read(&mut buf), Ok(1));
|
||||||
assert_eq!(buf[0], 42);
|
assert_eq!(buf[0], 42);
|
||||||
assert_eq!(capacity(&c), 3);
|
assert_eq!(c.free_capacity(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn receiving_when_empty() {
|
fn receiving_when_empty() {
|
||||||
let mut c = PipeState::<3>::new();
|
let c = Pipe::<NoopRawMutex, 3>::new();
|
||||||
let mut buf = [0; 16];
|
let mut buf = [0; 16];
|
||||||
assert_eq!(c.try_read(&mut buf), Err(TryReadError::Empty));
|
assert_eq!(c.try_read(&mut buf), Err(TryReadError::Empty));
|
||||||
assert_eq!(capacity(&c), 3);
|
assert_eq!(c.free_capacity(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -506,13 +592,37 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cloning() {
|
fn read_buf() {
|
||||||
let c = Pipe::<NoopRawMutex, 3>::new();
|
let mut c = Pipe::<NoopRawMutex, 3>::new();
|
||||||
let r1 = c.reader();
|
let (mut r, w) = c.split();
|
||||||
let w1 = c.writer();
|
assert!(w.try_write(&[42, 43]).is_ok());
|
||||||
|
let buf = r.try_fill_buf().unwrap();
|
||||||
|
assert_eq!(buf, &[42, 43]);
|
||||||
|
let buf = r.try_fill_buf().unwrap();
|
||||||
|
assert_eq!(buf, &[42, 43]);
|
||||||
|
r.consume(1);
|
||||||
|
let buf = r.try_fill_buf().unwrap();
|
||||||
|
assert_eq!(buf, &[43]);
|
||||||
|
r.consume(1);
|
||||||
|
assert_eq!(r.try_fill_buf(), Err(TryReadError::Empty));
|
||||||
|
assert_eq!(w.try_write(&[44, 45, 46]), Ok(1));
|
||||||
|
assert_eq!(w.try_write(&[45, 46]), Ok(2));
|
||||||
|
let buf = r.try_fill_buf().unwrap();
|
||||||
|
assert_eq!(buf, &[44]); // only one byte due to wraparound.
|
||||||
|
r.consume(1);
|
||||||
|
let buf = r.try_fill_buf().unwrap();
|
||||||
|
assert_eq!(buf, &[45, 46]);
|
||||||
|
assert!(w.try_write(&[47]).is_ok());
|
||||||
|
let buf = r.try_fill_buf().unwrap();
|
||||||
|
assert_eq!(buf, &[45, 46, 47]);
|
||||||
|
r.consume(3);
|
||||||
|
}
|
||||||
|
|
||||||
let _ = r1.clone();
|
#[test]
|
||||||
let _ = w1.clone();
|
fn writer_is_cloneable() {
|
||||||
|
let mut c = Pipe::<NoopRawMutex, 3>::new();
|
||||||
|
let (_r, w) = c.split();
|
||||||
|
let _ = w.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[futures_test::test]
|
#[futures_test::test]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
use core::ops::Range;
|
||||||
|
|
||||||
pub struct RingBuffer<const N: usize> {
|
pub struct RingBuffer<const N: usize> {
|
||||||
buf: [u8; N],
|
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
empty: bool,
|
empty: bool,
|
||||||
@ -8,27 +9,26 @@ pub struct RingBuffer<const N: usize> {
|
|||||||
impl<const N: usize> RingBuffer<N> {
|
impl<const N: usize> RingBuffer<N> {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
buf: [0; N],
|
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
empty: true,
|
empty: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_buf(&mut self) -> &mut [u8] {
|
pub fn push_buf(&mut self) -> Range<usize> {
|
||||||
if self.start == self.end && !self.empty {
|
if self.start == self.end && !self.empty {
|
||||||
trace!(" ringbuf: push_buf empty");
|
trace!(" ringbuf: push_buf empty");
|
||||||
return &mut self.buf[..0];
|
return 0..0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let n = if self.start <= self.end {
|
let n = if self.start <= self.end {
|
||||||
self.buf.len() - self.end
|
N - self.end
|
||||||
} else {
|
} else {
|
||||||
self.start - self.end
|
self.start - self.end
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n);
|
trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n);
|
||||||
&mut self.buf[self.end..self.end + n]
|
self.end..self.end + n
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, n: usize) {
|
pub fn push(&mut self, n: usize) {
|
||||||
@ -41,20 +41,20 @@ impl<const N: usize> RingBuffer<N> {
|
|||||||
self.empty = false;
|
self.empty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop_buf(&mut self) -> &mut [u8] {
|
pub fn pop_buf(&mut self) -> Range<usize> {
|
||||||
if self.empty {
|
if self.empty {
|
||||||
trace!(" ringbuf: pop_buf empty");
|
trace!(" ringbuf: pop_buf empty");
|
||||||
return &mut self.buf[..0];
|
return 0..0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let n = if self.end <= self.start {
|
let n = if self.end <= self.start {
|
||||||
self.buf.len() - self.start
|
N - self.start
|
||||||
} else {
|
} else {
|
||||||
self.end - self.start
|
self.end - self.start
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n);
|
trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n);
|
||||||
&mut self.buf[self.start..self.start + n]
|
self.start..self.start + n
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(&mut self, n: usize) {
|
pub fn pop(&mut self, n: usize) {
|
||||||
@ -93,8 +93,8 @@ impl<const N: usize> RingBuffer<N> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn wrap(&self, n: usize) -> usize {
|
fn wrap(&self, n: usize) -> usize {
|
||||||
assert!(n <= self.buf.len());
|
assert!(n <= N);
|
||||||
if n == self.buf.len() {
|
if n == N {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
n
|
n
|
||||||
@ -110,37 +110,29 @@ mod tests {
|
|||||||
fn push_pop() {
|
fn push_pop() {
|
||||||
let mut rb: RingBuffer<4> = RingBuffer::new();
|
let mut rb: RingBuffer<4> = RingBuffer::new();
|
||||||
let buf = rb.push_buf();
|
let buf = rb.push_buf();
|
||||||
assert_eq!(4, buf.len());
|
assert_eq!(0..4, buf);
|
||||||
buf[0] = 1;
|
|
||||||
buf[1] = 2;
|
|
||||||
buf[2] = 3;
|
|
||||||
buf[3] = 4;
|
|
||||||
rb.push(4);
|
rb.push(4);
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
let buf = rb.pop_buf();
|
||||||
assert_eq!(4, buf.len());
|
assert_eq!(0..4, buf);
|
||||||
assert_eq!(1, buf[0]);
|
|
||||||
rb.pop(1);
|
rb.pop(1);
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
let buf = rb.pop_buf();
|
||||||
assert_eq!(3, buf.len());
|
assert_eq!(1..4, buf);
|
||||||
assert_eq!(2, buf[0]);
|
|
||||||
rb.pop(1);
|
rb.pop(1);
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
let buf = rb.pop_buf();
|
||||||
assert_eq!(2, buf.len());
|
assert_eq!(2..4, buf);
|
||||||
assert_eq!(3, buf[0]);
|
|
||||||
rb.pop(1);
|
rb.pop(1);
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
let buf = rb.pop_buf();
|
||||||
assert_eq!(1, buf.len());
|
assert_eq!(3..4, buf);
|
||||||
assert_eq!(4, buf[0]);
|
|
||||||
rb.pop(1);
|
rb.pop(1);
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
let buf = rb.pop_buf();
|
||||||
assert_eq!(0, buf.len());
|
assert_eq!(0..0, buf);
|
||||||
|
|
||||||
let buf = rb.push_buf();
|
let buf = rb.push_buf();
|
||||||
assert_eq!(4, buf.len());
|
assert_eq!(0..4, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## 0.1.3 - 2023-08-28
|
||||||
|
|
||||||
|
- Update `embedded-hal-async` to `1.0.0-rc.1`
|
||||||
|
- Update `embedded-hal v1` to `1.0.0-rc.1`
|
||||||
|
|
||||||
## 0.1.2 - 2023-07-05
|
## 0.1.2 - 2023-07-05
|
||||||
|
|
||||||
- Update `embedded-hal-async` to `0.2.0-alpha.2`.
|
- Update `embedded-hal-async` to `0.2.0-alpha.2`.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-time"
|
name = "embassy-time"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Instant and Duration for embedded no-std systems, with async timer support"
|
description = "Instant and Duration for embedded no-std systems, with async timer support"
|
||||||
repository = "https://github.com/embassy-rs/embassy"
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
@ -169,4 +169,4 @@ wasm-timer = { version = "0.2.5", optional = true }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = "0.9"
|
serial_test = "0.9"
|
||||||
critical-section = { version = "1.1", features = ["std"] }
|
critical-section = { version = "1.1", features = ["std"] }
|
||||||
embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["nightly"] }
|
embassy-executor = { version = "0.3.0", path = "../embassy-executor", features = ["nightly"] }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> Debug for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LowerHex for Bytes<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{:#02x?}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl<'a> defmt::Format for Bytes<'a> {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
defmt::write!(fmt, "{:02x}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user