Compare commits
146 Commits
embassy-ti
...
boot-hil
Author | SHA1 | Date | |
---|---|---|---|
9010bfe79b | |||
2a437d31c4 | |||
2d78aa763a | |||
1133cbf90e | |||
77c357e744 | |||
521970535e | |||
93d4cfe7c1 | |||
8413a89752 | |||
db717d9c81 | |||
808fa9dce6 | |||
ceb13def19 | |||
5cf494113f | |||
8edb7bb012 | |||
8900f5f52b | |||
8201979d71 | |||
2d9f50addc | |||
18da91e252 | |||
28e2570533 | |||
26e0823c35 | |||
ceca8b4336 | |||
e308286f3c | |||
268da2fcde | |||
a992d4895d | |||
1bae34d37c | |||
aac42e3209 | |||
08415e001e | |||
d6a1b567ee | |||
a47fb42962 | |||
70a4a193c5 | |||
2132afb48b | |||
0e9131fd14 | |||
40a18b075d | |||
f17f09057d | |||
11a78fb1e4 | |||
48154e18bf | |||
cf2d4eca7c | |||
3e0b752bef | |||
6070d61d8c | |||
a4f8d82ef5 | |||
c27b0296fe | |||
336ae54a56 | |||
d6a1118406 | |||
9de08d56a0 | |||
26740bb3ef | |||
4550452f43 | |||
08410432b5 | |||
3cf3caa3ab | |||
c21ad04c2e | |||
d097c99719 | |||
af7c93abba | |||
c356585a59 | |||
0d3ff34d80 | |||
bb2d6c8542 | |||
a05afc5426 | |||
50116e5b27 | |||
a80acf686e | |||
6770d8e8a6 | |||
7307098780 | |||
f4601af2a4 | |||
fd22f4fac5 | |||
7622d2eb61 | |||
7573160077 | |||
7e18e5e0c1 | |||
9d76a6e933 | |||
f502271940 | |||
e2f8bf19ea | |||
1180e1770d | |||
49ba9c3da2 | |||
ce662766be | |||
a03b6be693 | |||
87d3086533 | |||
d19e1c1dd1 | |||
8a55adbfd8 | |||
6dc56d2b35 | |||
394503ab69 | |||
bfb4cf775a | |||
274f63a879 | |||
615882ebd6 | |||
6e38b07642 | |||
1eb03dc41a | |||
a2656f402b | |||
3466c9cfa9 | |||
3295ec94e5 | |||
c5732ce285 | |||
2e6f4237f2 | |||
ac11635b0b | |||
9dd58660c3 | |||
9baa3bafb0 | |||
360286e67c | |||
0c66636d00 | |||
9d8c527308 | |||
1d87ec9cc3 | |||
b74645e259 | |||
56351cedcb | |||
0823d9dc93 | |||
c10fb7c1c4 | |||
a2ce3aa1a1 | |||
3769447382 | |||
2c6b428ed4 | |||
83a976cf4b | |||
a76ff53fb3 | |||
f69e8459c9 | |||
891f1758bc | |||
ae174fd0e0 | |||
5c936d33d4 | |||
36ec9bcc1d | |||
5adc80f6d9 | |||
98f55fa54d | |||
416ecc73d8 | |||
27dfced285 | |||
21681d8b4e | |||
989c98f316 | |||
fdb2c4946a | |||
5e613d9abb | |||
40b576584e | |||
aa7752020e | |||
6c165f8dc0 | |||
975f2f23c0 | |||
0eeefd3dbf | |||
a4d78a6552 | |||
f503417f4c | |||
6b8b145266 | |||
e07f943562 | |||
70a5221b2e | |||
b315c28d4e | |||
e025693914 | |||
05ee02b593 | |||
4098a61ef0 | |||
1db00f5439 | |||
7fc17bc150 | |||
6e616a6fe6 | |||
d02886786e | |||
2db4d01198 | |||
fd739250ea | |||
71c4e7e4d2 | |||
2c80784fe6 | |||
538001a4bc | |||
e981cd4968 | |||
91bb3aae3f | |||
e08dbcd027 | |||
5c27265a21 | |||
2c36199dea | |||
13a0be6289 | |||
9f928010a8 | |||
7f7256050c | |||
4b6538c8a8 |
4
.github/bors.toml
vendored
4
.github/bors.toml
vendored
@ -1,4 +0,0 @@
|
||||
status = [
|
||||
"all",
|
||||
]
|
||||
delete_merged_branches = true
|
1
.github/ci/build.sh
vendored
1
.github/ci/build.sh
vendored
@ -11,6 +11,7 @@ if [ -f /ci/secrets/teleprobe-token.txt ]; then
|
||||
echo Got teleprobe token!
|
||||
export TELEPROBE_HOST=https://teleprobe.embassy.dev
|
||||
export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt)
|
||||
export TELEPROBE_CACHE=/ci/cache/teleprobe_cache.json
|
||||
fi
|
||||
|
||||
hashtime restore /ci/cache/filetime.json || true
|
||||
|
3
ci.sh
3
ci.sh
@ -155,7 +155,7 @@ cargo batch \
|
||||
--- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l1 \
|
||||
--- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32l4 \
|
||||
--- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --features skip-include --out-dir out/examples/boot/stm32wl \
|
||||
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
||||
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 --out-dir out/examples/bootloader/nrf \
|
||||
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
||||
--- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||
@ -171,6 +171,7 @@ cargo batch \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \
|
||||
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
|
||||
--- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
|
||||
--- build --release --manifest-path tests/boot/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/boot/nrf52840-dk \
|
||||
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
|
||||
$BUILD_EXTRA
|
||||
|
||||
|
@ -96,6 +96,7 @@ pub(crate) const IOCTL_CMD_UP: u32 = 2;
|
||||
pub(crate) const IOCTL_CMD_DOWN: u32 = 3;
|
||||
pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26;
|
||||
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_SET_AP: u32 = 118;
|
||||
pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263;
|
||||
|
@ -124,7 +124,7 @@ impl<'a> Control<'a> {
|
||||
Timer::after(Duration::from_millis(100)).await;
|
||||
|
||||
// set wifi up
|
||||
self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
|
||||
self.up().await;
|
||||
|
||||
Timer::after(Duration::from_millis(100)).await;
|
||||
|
||||
@ -138,6 +138,16 @@ impl<'a> Control<'a> {
|
||||
debug!("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) {
|
||||
// power save mode
|
||||
let mode_num = mode.mode();
|
||||
@ -256,13 +266,13 @@ impl<'a> Control<'a> {
|
||||
}
|
||||
|
||||
// Temporarily set wifi down
|
||||
self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await;
|
||||
self.down().await;
|
||||
|
||||
// Turn off APSTA mode
|
||||
self.set_iovar_u32("apsta", 0).await;
|
||||
|
||||
// Set wifi up again
|
||||
self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
|
||||
self.up().await;
|
||||
|
||||
// Turn on AP mode
|
||||
self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await;
|
||||
@ -423,6 +433,11 @@ impl<'a> Control<'a> {
|
||||
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> {
|
||||
|
@ -83,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
|
@ -27,7 +27,7 @@ use ioctl::IoctlState;
|
||||
|
||||
use crate::bus::Bus;
|
||||
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::structs::BssInfo;
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
name = "embassy-net-adin1110"
|
||||
version = "0.1.0"
|
||||
version = "0.2.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"]
|
||||
@ -12,30 +12,31 @@ edition = "2021"
|
||||
[dependencies]
|
||||
heapless = "0.7.16"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.4", default-features = false, optional = true }
|
||||
log = { version = "0.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-time = { version = "0.1.3" }
|
||||
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"
|
||||
critical-section = { version = "1.1.2", features = ["std"] }
|
||||
futures-test = "0.3.28"
|
||||
|
||||
[features]
|
||||
default = [ ]
|
||||
defmt = [ "dep:defmt" ]
|
||||
defmt = [ "dep:defmt", "embedded-hal-1/defmt-03" ]
|
||||
log = ["dep:log"]
|
||||
|
||||
[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"
|
||||
features = ["defmt"]
|
||||
|
@ -30,8 +30,8 @@ Currently only `Generic` SPI with or without CRC is supported.
|
||||
|
||||
## 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!**
|
||||
- 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) for an example.
|
||||
- [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit (End Of Life)`](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
|
||||
|
||||
@ -44,6 +44,39 @@ ADIN1110 library can tested on the host with a mock SPI driver.
|
||||
|
||||
$ `cargo test --target x86_64-unknown-linux-gnu`
|
||||
|
||||
## Benchmark
|
||||
|
||||
- Benchmarked on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html), with [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) example.
|
||||
|
||||
Basic `ping` benchmark
|
||||
```rust,ignore
|
||||
# ping <IP> -c 60
|
||||
|
||||
60 packets transmitted, 60 received, 0% packet loss, time 59066ms
|
||||
rtt min/avg/max/mdev = 1.089/1.161/1.237/0.018 ms
|
||||
|
||||
# ping <IP> -s 1472 -M do -c 60
|
||||
|
||||
60 packets transmitted, 60 received, 0% packet loss, time 59066ms
|
||||
rtt min/avg/max/mdev = 5.122/5.162/6.177/0.133 ms
|
||||
```
|
||||
|
||||
HTTP load generator benchmark with [`oha`](https://github.com/hatoo/oha)
|
||||
```rust,ignore
|
||||
# oha -c 1 http://<IP> -z 60s
|
||||
Summary:
|
||||
Success rate: 50.00%
|
||||
Total: 60.0005 secs
|
||||
Slowest: 0.0055 secs
|
||||
Fastest: 0.0033 secs
|
||||
Average: 0.0034 secs
|
||||
Requests/sec: 362.1971
|
||||
|
||||
Total data: 2.99 MiB
|
||||
Size/request: 289 B
|
||||
Size/sec: 51.11 KiB
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under either of
|
||||
|
@ -257,29 +257,30 @@ pub const CRC32R_LOOKUP_TABLE: [u32; 256] = [
|
||||
0x2D02_EF8D,
|
||||
];
|
||||
|
||||
/// Generate Ethernet Frame Check Sequence
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Debug)]
|
||||
pub struct ETH_FSC(pub u32);
|
||||
pub struct ETH_FCS(pub u32);
|
||||
|
||||
impl ETH_FSC {
|
||||
impl ETH_FCS {
|
||||
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 fcs = 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)
|
||||
Self(fcs)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn update(self, data: &[u8]) -> Self {
|
||||
let fsc = data.iter().fold(self.0 ^ u32::MAX, |crc, byte| {
|
||||
let fcs = 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)
|
||||
Self(fcs)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -319,24 +320,24 @@ mod tests {
|
||||
];
|
||||
|
||||
// Packet A
|
||||
let own_crc = ETH_FSC::new(&packet_a[0..60]);
|
||||
let own_crc = ETH_FCS::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);
|
||||
let own_crc = ETH_FCS::new(packet_a);
|
||||
println!("{:08x}", own_crc.0);
|
||||
assert_eq!(own_crc.0, ETH_FSC::CRC32_OK);
|
||||
assert_eq!(own_crc.0, ETH_FCS::CRC32_OK);
|
||||
|
||||
// Packet B
|
||||
let own_crc = ETH_FSC::new(&packet_b[0..60]);
|
||||
let own_crc = ETH_FCS::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);
|
||||
let own_crc = ETH_FCS::new(packet_b);
|
||||
println!("{:08x}", own_crc.0);
|
||||
assert_eq!(own_crc.0, ETH_FSC::CRC32_OK);
|
||||
assert_eq!(own_crc.0, ETH_FCS::CRC32_OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -349,9 +350,9 @@ mod tests {
|
||||
];
|
||||
|
||||
let (part_a, part_b) = full_data.split_at(16);
|
||||
let crc_partially = ETH_FSC::new(part_a).update(part_b);
|
||||
let crc_partially = ETH_FCS::new(part_a).update(part_b);
|
||||
|
||||
let crc_full = ETH_FSC::new(full_data);
|
||||
let crc_full = ETH_FCS::new(full_data);
|
||||
|
||||
assert_eq!(crc_full.0, crc_partially.0);
|
||||
}
|
||||
|
254
embassy-net-adin1110/src/fmt.rs
Normal file
254
embassy-net-adin1110/src/fmt.rs
Normal file
@ -0,0 +1,254 @@
|
||||
#![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)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::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 _ignored = ($( & $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 _ignored = ($( & $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 _ignored = ($( & $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 _ignored = ($( & $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 _ignored = ($( & $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
|
||||
}
|
||||
}
|
||||
|
||||
pub 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)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -32,11 +32,12 @@ enum Reg13Op {
|
||||
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>
|
||||
/// Clause 45 methodes are bases on <https://www.ieee802.org/3/efm/public/nov02/oam/pannell_oam_1_1102.pdf>
|
||||
pub trait MdioBus {
|
||||
type Error;
|
||||
|
||||
@ -87,89 +88,89 @@ pub trait MdioBus {
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use core::convert::Infallible;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::convert::Infallible;
|
||||
|
||||
// use super::{MdioBus, PhyAddr, RegC22, RegVal};
|
||||
use super::{MdioBus, PhyAddr, RegC22, RegVal};
|
||||
|
||||
// #[derive(Debug, PartialEq, Eq)]
|
||||
// enum A {
|
||||
// Read(PhyAddr, RegC22),
|
||||
// Write(PhyAddr, RegC22, RegVal),
|
||||
// }
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum A {
|
||||
Read(PhyAddr, RegC22),
|
||||
Write(PhyAddr, RegC22, RegVal),
|
||||
}
|
||||
|
||||
// struct MockMdioBus(Vec<A>);
|
||||
struct MockMdioBus(Vec<A>);
|
||||
|
||||
// impl MockMdioBus {
|
||||
// pub fn clear(&mut self) {
|
||||
// self.0.clear();
|
||||
// }
|
||||
// }
|
||||
impl MockMdioBus {
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// impl MdioBus for MockMdioBus {
|
||||
// type Error = Infallible;
|
||||
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(())
|
||||
// }
|
||||
async 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)
|
||||
// }
|
||||
// }
|
||||
async 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));
|
||||
#[futures_test::test]
|
||||
async 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_cl22(0x01, 0x00).await.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)
|
||||
// ]
|
||||
// );
|
||||
// }
|
||||
mdiobus.clear();
|
||||
mdiobus.read_cl45(0x01, (0xBB, 0x1234)).await.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));
|
||||
#[futures_test::test]
|
||||
async 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_cl22(0x01, 0x00, 0xABCD).await.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)
|
||||
// ]
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
mdiobus.clear();
|
||||
mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).await.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)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +111,7 @@ pub mod RegsC45 {
|
||||
}
|
||||
}
|
||||
|
||||
/// 10-BASE-T1x PHY functions.
|
||||
pub struct Phy10BaseT1x(u8);
|
||||
|
||||
impl Default for Phy10BaseT1x {
|
||||
|
@ -1,3 +1,5 @@
|
||||
use core::fmt::{Debug, Display};
|
||||
|
||||
use bitfield::{bitfield, bitfield_bitrange, bitfield_fields};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
@ -34,6 +36,12 @@ pub enum SpiRegisters {
|
||||
RX = 0x91,
|
||||
}
|
||||
|
||||
impl Display for SpiRegisters {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpiRegisters> for u16 {
|
||||
fn from(val: SpiRegisters) -> Self {
|
||||
val as u16
|
||||
@ -68,7 +76,7 @@ impl From<u16> for SpiRegisters {
|
||||
0x73 => Self::ADDR_MSK_UPR1,
|
||||
0x90 => Self::RX_FSIZE,
|
||||
0x91 => Self::RX,
|
||||
e => panic!("Unknown value {e}"),
|
||||
e => panic!("Unknown value {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,7 +182,7 @@ bitfield! {
|
||||
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
|
||||
/// Enable SPI CRC
|
||||
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;
|
||||
@ -313,7 +321,7 @@ impl From<u8> for LedFunc {
|
||||
26 => LedFunc::Clk25Ref,
|
||||
27 => LedFunc::TxTCLK,
|
||||
28 => LedFunc::Clk120MHz,
|
||||
e => panic!("Invalid value {e}"),
|
||||
e => panic!("Invalid value {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -369,7 +377,7 @@ impl From<u8> for LedPol {
|
||||
0 => LedPol::AutoSense,
|
||||
1 => LedPol::ActiveHigh,
|
||||
2 => LedPol::ActiveLow,
|
||||
e => panic!("Invalid value {e}"),
|
||||
e => panic!("Invalid value {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ use embassy_net_driver::{Capabilities, LinkState, Medium};
|
||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embassy_sync::waitqueue::WakerRegistration;
|
||||
use embassy_sync::zerocopy_channel;
|
||||
|
||||
pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {
|
||||
rx: [PacketBuf<MTU>; N_RX],
|
||||
@ -130,24 +131,24 @@ impl<'d, const MTU: usize> Runner<'d, MTU> {
|
||||
}
|
||||
|
||||
pub async fn tx_buf(&mut self) -> &mut [u8] {
|
||||
let p = self.tx_chan.recv().await;
|
||||
let p = self.tx_chan.receive().await;
|
||||
&mut p.buf[..p.len]
|
||||
}
|
||||
|
||||
pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> {
|
||||
let p = self.tx_chan.try_recv()?;
|
||||
let p = self.tx_chan.try_receive()?;
|
||||
Some(&mut p.buf[..p.len])
|
||||
}
|
||||
|
||||
pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {
|
||||
match self.tx_chan.poll_recv(cx) {
|
||||
match self.tx_chan.poll_receive(cx) {
|
||||
Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]),
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tx_done(&mut self) {
|
||||
self.tx_chan.recv_done();
|
||||
self.tx_chan.receive_done();
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,24 +205,24 @@ impl<'d, const MTU: usize> RxRunner<'d, MTU> {
|
||||
|
||||
impl<'d, const MTU: usize> TxRunner<'d, MTU> {
|
||||
pub async fn tx_buf(&mut self) -> &mut [u8] {
|
||||
let p = self.tx_chan.recv().await;
|
||||
let p = self.tx_chan.receive().await;
|
||||
&mut p.buf[..p.len]
|
||||
}
|
||||
|
||||
pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> {
|
||||
let p = self.tx_chan.try_recv()?;
|
||||
let p = self.tx_chan.try_receive()?;
|
||||
Some(&mut p.buf[..p.len])
|
||||
}
|
||||
|
||||
pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {
|
||||
match self.tx_chan.poll_recv(cx) {
|
||||
match self.tx_chan.poll_receive(cx) {
|
||||
Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]),
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tx_done(&mut self) {
|
||||
self.tx_chan.recv_done();
|
||||
self.tx_chan.receive_done();
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,7 +294,7 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> {
|
||||
type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ;
|
||||
|
||||
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() {
|
||||
if self.rx.poll_receive(cx).is_ready() && self.tx.poll_send(cx).is_ready() {
|
||||
Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() }))
|
||||
} else {
|
||||
None
|
||||
@ -337,9 +338,9 @@ impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> {
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
// NOTE(unwrap): we checked the queue wasn't full when creating the token.
|
||||
let pkt = unwrap!(self.rx.try_recv());
|
||||
let pkt = unwrap!(self.rx.try_receive());
|
||||
let r = f(&mut pkt.buf[..pkt.len]);
|
||||
self.rx.recv_done();
|
||||
self.rx.receive_done();
|
||||
r
|
||||
}
|
||||
}
|
||||
@ -361,215 +362,3 @@ impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> {
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
mod zerocopy_channel {
|
||||
use core::cell::RefCell;
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embassy_sync::waitqueue::WakerRegistration;
|
||||
|
||||
pub struct Channel<'a, M: RawMutex, T> {
|
||||
buf: *mut T,
|
||||
phantom: PhantomData<&'a mut T>,
|
||||
state: Mutex<M, RefCell<State>>,
|
||||
}
|
||||
|
||||
impl<'a, M: RawMutex, T> Channel<'a, M, T> {
|
||||
pub fn new(buf: &'a mut [T]) -> Self {
|
||||
let len = buf.len();
|
||||
assert!(len != 0);
|
||||
|
||||
Self {
|
||||
buf: buf.as_mut_ptr(),
|
||||
phantom: PhantomData,
|
||||
state: Mutex::new(RefCell::new(State {
|
||||
len,
|
||||
front: 0,
|
||||
back: 0,
|
||||
full: false,
|
||||
send_waker: WakerRegistration::new(),
|
||||
recv_waker: WakerRegistration::new(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) {
|
||||
(Sender { channel: self }, Receiver { channel: self })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Sender<'a, M: RawMutex, T> {
|
||||
channel: &'a Channel<'a, M, T>,
|
||||
}
|
||||
|
||||
impl<'a, M: RawMutex, T> Sender<'a, M, T> {
|
||||
pub fn borrow(&mut self) -> Sender<'_, M, T> {
|
||||
Sender { channel: self.channel }
|
||||
}
|
||||
|
||||
pub fn try_send(&mut self) -> Option<&mut T> {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.push_index() {
|
||||
Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
|
||||
None => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.push_index() {
|
||||
Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
|
||||
None => {
|
||||
s.recv_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send(&mut self) -> &mut T {
|
||||
let i = poll_fn(|cx| {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.push_index() {
|
||||
Some(i) => Poll::Ready(i),
|
||||
None => {
|
||||
s.recv_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.await;
|
||||
unsafe { &mut *self.channel.buf.add(i) }
|
||||
}
|
||||
|
||||
pub fn send_done(&mut self) {
|
||||
self.channel.state.lock(|s| s.borrow_mut().push_done())
|
||||
}
|
||||
}
|
||||
pub struct Receiver<'a, M: RawMutex, T> {
|
||||
channel: &'a Channel<'a, M, T>,
|
||||
}
|
||||
|
||||
impl<'a, M: RawMutex, T> Receiver<'a, M, T> {
|
||||
pub fn borrow(&mut self) -> Receiver<'_, M, T> {
|
||||
Receiver { channel: self.channel }
|
||||
}
|
||||
|
||||
pub fn try_recv(&mut self) -> Option<&mut T> {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.pop_index() {
|
||||
Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
|
||||
None => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.pop_index() {
|
||||
Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
|
||||
None => {
|
||||
s.send_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn recv(&mut self) -> &mut T {
|
||||
let i = poll_fn(|cx| {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.pop_index() {
|
||||
Some(i) => Poll::Ready(i),
|
||||
None => {
|
||||
s.send_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.await;
|
||||
unsafe { &mut *self.channel.buf.add(i) }
|
||||
}
|
||||
|
||||
pub fn recv_done(&mut self) {
|
||||
self.channel.state.lock(|s| s.borrow_mut().pop_done())
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
len: usize,
|
||||
|
||||
/// Front index. Always 0..=(N-1)
|
||||
front: usize,
|
||||
/// Back index. Always 0..=(N-1).
|
||||
back: usize,
|
||||
|
||||
/// Used to distinguish "empty" and "full" cases when `front == back`.
|
||||
/// May only be `true` if `front == back`, always `false` otherwise.
|
||||
full: bool,
|
||||
|
||||
send_waker: WakerRegistration,
|
||||
recv_waker: WakerRegistration,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn increment(&self, i: usize) -> usize {
|
||||
if i + 1 == self.len {
|
||||
0
|
||||
} else {
|
||||
i + 1
|
||||
}
|
||||
}
|
||||
|
||||
fn is_full(&self) -> bool {
|
||||
self.full
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.front == self.back && !self.full
|
||||
}
|
||||
|
||||
fn push_index(&mut self) -> Option<usize> {
|
||||
match self.is_full() {
|
||||
true => None,
|
||||
false => Some(self.back),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_done(&mut self) {
|
||||
assert!(!self.is_full());
|
||||
self.back = self.increment(self.back);
|
||||
if self.back == self.front {
|
||||
self.full = true;
|
||||
}
|
||||
self.send_waker.wake();
|
||||
}
|
||||
|
||||
fn pop_index(&mut self) -> Option<usize> {
|
||||
match self.is_empty() {
|
||||
true => None,
|
||||
false => Some(self.front),
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_done(&mut self) {
|
||||
assert!(!self.is_empty());
|
||||
self.front = self.increment(self.front);
|
||||
self.full = false;
|
||||
self.recv_waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ pub struct Control<'a> {
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
enum WifiMode {
|
||||
None = 0,
|
||||
Sta = 1,
|
||||
@ -26,6 +28,18 @@ enum WifiMode {
|
||||
ApSta = 3,
|
||||
}
|
||||
|
||||
pub use proto::CtrlWifiSecProt as Security;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Status {
|
||||
pub ssid: String<32>,
|
||||
pub bssid: [u8; 6],
|
||||
pub rssi: i32,
|
||||
pub channel: u32,
|
||||
pub security: Security,
|
||||
}
|
||||
|
||||
macro_rules! ioctl {
|
||||
($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => {
|
||||
let mut msg = proto::CtrlMsg {
|
||||
@ -34,7 +48,9 @@ macro_rules! ioctl {
|
||||
payload: Some(proto::CtrlMsgPayload::$req_variant($req)),
|
||||
};
|
||||
$self.ioctl(&mut msg).await?;
|
||||
let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else {
|
||||
#[allow(unused_mut)]
|
||||
let Some(proto::CtrlMsgPayload::$resp_variant(mut $resp)) = msg.payload
|
||||
else {
|
||||
warn!("unexpected response variant");
|
||||
return Err(Error::Internal);
|
||||
};
|
||||
@ -66,6 +82,19 @@ impl<'a> Control<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_status(&mut self) -> Result<Status, Error> {
|
||||
let req = proto::CtrlMsgReqGetApConfig {};
|
||||
ioctl!(self, ReqGetApConfig, RespGetApConfig, req, resp);
|
||||
trim_nulls(&mut resp.ssid);
|
||||
Ok(Status {
|
||||
ssid: resp.ssid,
|
||||
bssid: parse_mac(&resp.bssid)?,
|
||||
rssi: resp.rssi as _,
|
||||
channel: resp.chnl,
|
||||
security: resp.sec_prot,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
|
||||
let req = proto::CtrlMsgReqConnectAp {
|
||||
ssid: String::from(ssid),
|
||||
@ -98,27 +127,7 @@ impl<'a> Control<'a> {
|
||||
mode: WifiMode::Sta as _,
|
||||
};
|
||||
ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp);
|
||||
|
||||
// WHY IS THIS A STRING? WHYYYY
|
||||
fn nibble_from_hex(b: u8) -> u8 {
|
||||
match b {
|
||||
b'0'..=b'9' => b - b'0',
|
||||
b'a'..=b'f' => b + 0xa - b'a',
|
||||
b'A'..=b'F' => b + 0xa - b'A',
|
||||
_ => panic!("invalid hex digit {}", b),
|
||||
}
|
||||
}
|
||||
|
||||
let mac = resp.mac.as_bytes();
|
||||
let mut res = [0; 6];
|
||||
if mac.len() != 17 {
|
||||
warn!("unexpected MAC respnse length");
|
||||
return Err(Error::Internal);
|
||||
}
|
||||
for (i, b) in res.iter_mut().enumerate() {
|
||||
*b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1])
|
||||
}
|
||||
Ok(res)
|
||||
parse_mac(&resp.mac)
|
||||
}
|
||||
|
||||
async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> {
|
||||
@ -167,3 +176,35 @@ impl<'a> Control<'a> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// WHY IS THIS A STRING? WHYYYY
|
||||
fn parse_mac(mac: &str) -> Result<[u8; 6], Error> {
|
||||
fn nibble_from_hex(b: u8) -> Result<u8, Error> {
|
||||
match b {
|
||||
b'0'..=b'9' => Ok(b - b'0'),
|
||||
b'a'..=b'f' => Ok(b + 0xa - b'a'),
|
||||
b'A'..=b'F' => Ok(b + 0xa - b'A'),
|
||||
_ => {
|
||||
warn!("invalid hex digit {}", b);
|
||||
Err(Error::Internal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mac = mac.as_bytes();
|
||||
let mut res = [0; 6];
|
||||
if mac.len() != 17 {
|
||||
warn!("unexpected MAC length");
|
||||
return Err(Error::Internal);
|
||||
}
|
||||
for (i, b) in res.iter_mut().enumerate() {
|
||||
*b = (nibble_from_hex(mac[i * 3])? << 4) | nibble_from_hex(mac[i * 3 + 1])?
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn trim_nulls<const N: usize>(s: &mut String<N>) {
|
||||
while s.chars().rev().next() == Some(0 as char) {
|
||||
s.pop();
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ macro_rules! unreachable {
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unreachable {
|
||||
($($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> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
|
@ -18,7 +18,7 @@ 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.1"}
|
||||
ppproto = { version = "0.1.2"}
|
||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
|
@ -93,7 +93,7 @@ macro_rules! unreachable {
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unreachable {
|
||||
($($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> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
|
@ -53,6 +53,8 @@ pub enum RunError<E> {
|
||||
WriteZero,
|
||||
/// Writing to the serial got EOF.
|
||||
Eof,
|
||||
/// PPP protocol was terminated by the peer
|
||||
Terminated,
|
||||
}
|
||||
|
||||
impl<E> From<WriteAllError<E>> for RunError<E> {
|
||||
@ -128,6 +130,9 @@ impl<'d> Runner<'d> {
|
||||
|
||||
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());
|
||||
|
@ -22,13 +22,13 @@ where
|
||||
|
||||
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
self.inner
|
||||
.receive(self.cx.as_deref_mut().unwrap())
|
||||
.receive(unwrap!(self.cx.as_deref_mut()))
|
||||
.map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx)))
|
||||
}
|
||||
|
||||
/// Construct a transmit token.
|
||||
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
|
||||
self.inner.transmit(self.cx.as_deref_mut().unwrap()).map(TxTokenAdapter)
|
||||
self.inner.transmit(unwrap!(self.cx.as_deref_mut())).map(TxTokenAdapter)
|
||||
}
|
||||
|
||||
/// Get a description of device capabilities.
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +172,7 @@ impl Config {
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// # use embassy_net::Config;
|
||||
/// let _cfg = Config::dhcpv4(Default::default());
|
||||
/// ```
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
@ -226,6 +227,7 @@ struct Inner<D: Driver> {
|
||||
static_v6: Option<StaticConfigV6>,
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
dhcp_socket: Option<SocketHandle>,
|
||||
config_waker: WakerRegistration,
|
||||
#[cfg(feature = "dns")]
|
||||
dns_socket: SocketHandle,
|
||||
#[cfg(feature = "dns")]
|
||||
@ -297,6 +299,7 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
static_v6: None,
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
dhcp_socket: None,
|
||||
config_waker: WakerRegistration::new(),
|
||||
#[cfg(feature = "dns")]
|
||||
dns_socket: socket.sockets.add(dns::Socket::new(
|
||||
&[],
|
||||
@ -363,6 +366,55 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
v4_up || v6_up
|
||||
}
|
||||
|
||||
/// Wait for the network stack to obtain a valid IP configuration.
|
||||
///
|
||||
/// ## Notes:
|
||||
/// - Ensure [`Stack::run`] has been called before using this function.
|
||||
///
|
||||
/// - This function may never return (e.g. if no configuration is obtained through DHCP).
|
||||
/// The caller is supposed to handle a timeout for this case.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```ignore
|
||||
/// let config = embassy_net::Config::dhcpv4(Default::default());
|
||||
///// Init network stack
|
||||
/// let stack = &*make_static!(embassy_net::Stack::new(
|
||||
/// device,
|
||||
/// config,
|
||||
/// make_static!(embassy_net::StackResources::<2>::new()),
|
||||
/// seed
|
||||
/// ));
|
||||
/// // Launch network task that runs `stack.run().await`
|
||||
/// spawner.spawn(net_task(stack)).unwrap();
|
||||
/// // Wait for DHCP config
|
||||
/// stack.wait_config_up().await;
|
||||
/// // use the network stack
|
||||
/// // ...
|
||||
/// ```
|
||||
pub async fn wait_config_up(&self) {
|
||||
// If the config is up already, we can return immediately.
|
||||
if self.is_config_up() {
|
||||
return;
|
||||
}
|
||||
|
||||
poll_fn(|cx| {
|
||||
if self.is_config_up() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
// If the config is not up, we register a waker that is woken up
|
||||
// when a config is applied (static or DHCP).
|
||||
trace!("Waiting for config up");
|
||||
|
||||
self.with_mut(|_, i| {
|
||||
i.config_waker.register(cx.waker());
|
||||
});
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Get the current IPv4 configuration.
|
||||
///
|
||||
/// If using DHCP, this will be None if DHCP hasn't been able to
|
||||
@ -616,7 +668,7 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
}
|
||||
|
||||
// Configure it
|
||||
let socket = _s.sockets.get_mut::<dhcpv4::Socket>(self.dhcp_socket.unwrap());
|
||||
let socket = _s.sockets.get_mut::<dhcpv4::Socket>(unwrap!(self.dhcp_socket));
|
||||
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);
|
||||
@ -656,12 +708,12 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
debug!(" IP address: {:?}", config.address);
|
||||
debug!(" Default gateway: {:?}", config.gateway);
|
||||
|
||||
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
||||
unwrap!(addrs.push(IpCidr::Ipv4(config.address)).ok());
|
||||
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();
|
||||
unwrap!(dns_servers.push(s.clone().into()).ok());
|
||||
}
|
||||
} else {
|
||||
info!("IPv4: DOWN");
|
||||
@ -673,12 +725,12 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
debug!(" IP address: {:?}", config.address);
|
||||
debug!(" Default gateway: {:?}", config.gateway);
|
||||
|
||||
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
||||
unwrap!(addrs.push(IpCidr::Ipv6(config.address)).ok());
|
||||
gateway_v6 = config.gateway.into();
|
||||
#[cfg(feature = "dns")]
|
||||
for s in &config.dns_servers {
|
||||
debug!(" DNS server: {:?}", s);
|
||||
dns_servers.push(s.clone().into()).unwrap();
|
||||
unwrap!(dns_servers.push(s.clone().into()).ok());
|
||||
}
|
||||
} else {
|
||||
info!("IPv6: DOWN");
|
||||
@ -690,13 +742,13 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
// Apply gateways
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
if let Some(gateway) = gateway_v4 {
|
||||
s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
|
||||
unwrap!(s.iface.routes_mut().add_default_ipv4_route(gateway));
|
||||
} else {
|
||||
s.iface.routes_mut().remove_default_ipv4_route();
|
||||
}
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
if let Some(gateway) = gateway_v6 {
|
||||
s.iface.routes_mut().add_default_ipv6_route(gateway).unwrap();
|
||||
unwrap!(s.iface.routes_mut().add_default_ipv6_route(gateway));
|
||||
} else {
|
||||
s.iface.routes_mut().remove_default_ipv6_route();
|
||||
}
|
||||
@ -706,6 +758,8 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
s.sockets
|
||||
.get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket)
|
||||
.update_servers(&dns_servers[..]);
|
||||
|
||||
self.config_waker.wake();
|
||||
}
|
||||
|
||||
fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
|
||||
|
@ -440,7 +440,7 @@ impl<'d> TcpIo<'d> {
|
||||
Poll::Ready(Err(Error::ConnectionReset))
|
||||
}
|
||||
} else {
|
||||
Poll::Ready(match s.send(f.take().unwrap()) {
|
||||
Poll::Ready(match s.send(unwrap!(f.take())) {
|
||||
// Connection reset. TODO: this can also be timeouts etc, investigate.
|
||||
Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset),
|
||||
Ok(r) => Ok(r),
|
||||
@ -468,7 +468,7 @@ impl<'d> TcpIo<'d> {
|
||||
Poll::Ready(Err(Error::ConnectionReset))
|
||||
}
|
||||
} else {
|
||||
Poll::Ready(match s.recv(f.take().unwrap()) {
|
||||
Poll::Ready(match s.recv(unwrap!(f.take())) {
|
||||
// Connection reset. TODO: this can also be timeouts etc, investigate.
|
||||
Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => {
|
||||
Err(Error::ConnectionReset)
|
||||
|
@ -29,6 +29,8 @@ pub enum BindError {
|
||||
pub enum Error {
|
||||
/// No route to host.
|
||||
NoRoute,
|
||||
/// Socket not bound to an outgoing port.
|
||||
SocketNotBound,
|
||||
}
|
||||
|
||||
/// An UDP socket.
|
||||
@ -155,7 +157,14 @@ impl<'a> UdpSocket<'a> {
|
||||
s.register_send_waker(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)),
|
||||
Err(udp::SendError::Unaddressable) => {
|
||||
// If no sender/outgoing port is specified, there is not really "no route"
|
||||
if s.endpoint().port == 0 {
|
||||
Poll::Ready(Err(Error::SocketNotBound))
|
||||
} else {
|
||||
Poll::Ready(Err(Error::NoRoute))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,28 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
let r = T::regs();
|
||||
let s = T::state();
|
||||
|
||||
#[cfg(feature = "nrf52832")]
|
||||
// NRF32 Anomaly 109 workaround... NRF52832
|
||||
if r.intenset.read().started().is_enabled() && r.events_started.read().bits() != 0 {
|
||||
// Handle the first "fake" transmission
|
||||
r.events_started.reset();
|
||||
r.events_end.reset();
|
||||
|
||||
// Update DMA registers with correct rx/tx buffer sizes
|
||||
r.rxd
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.maxcnt().bits(s.rx.load(Ordering::Relaxed)) });
|
||||
r.txd
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.maxcnt().bits(s.tx.load(Ordering::Relaxed)) });
|
||||
|
||||
// Disable interrupt for STARTED event...
|
||||
r.intenclr.write(|w| w.started().clear());
|
||||
// ... and start actual, hopefully glitch-free transmission
|
||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||
return;
|
||||
}
|
||||
|
||||
if r.events_end.read().bits() != 0 {
|
||||
s.end_waker.wake();
|
||||
r.intenclr.write(|w| w.end().clear());
|
||||
@ -167,42 +189,10 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
// Enable SPIM instance.
|
||||
r.enable.write(|w| w.enable().enabled());
|
||||
|
||||
// Configure mode.
|
||||
let mode = config.mode;
|
||||
r.config.write(|w| {
|
||||
match mode {
|
||||
MODE_0 => {
|
||||
w.order().msb_first();
|
||||
w.cpol().active_high();
|
||||
w.cpha().leading();
|
||||
}
|
||||
MODE_1 => {
|
||||
w.order().msb_first();
|
||||
w.cpol().active_high();
|
||||
w.cpha().trailing();
|
||||
}
|
||||
MODE_2 => {
|
||||
w.order().msb_first();
|
||||
w.cpol().active_low();
|
||||
w.cpha().leading();
|
||||
}
|
||||
MODE_3 => {
|
||||
w.order().msb_first();
|
||||
w.cpol().active_low();
|
||||
w.cpha().trailing();
|
||||
}
|
||||
}
|
||||
let mut spim = Self { _p: spim };
|
||||
|
||||
w
|
||||
});
|
||||
|
||||
// Configure frequency.
|
||||
let frequency = config.frequency;
|
||||
r.frequency.write(|w| w.frequency().variant(frequency));
|
||||
|
||||
// Set over-read character
|
||||
let orc = config.orc;
|
||||
r.orc.write(|w| unsafe { w.orc().bits(orc) });
|
||||
// Apply runtime peripheral configuration
|
||||
Self::set_config(&mut spim, &config);
|
||||
|
||||
// Disable all events interrupts
|
||||
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
@ -210,7 +200,7 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
Self { _p: spim }
|
||||
spim
|
||||
}
|
||||
|
||||
fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
@ -223,14 +213,33 @@ impl<'d, T: Instance> Spim<'d, T> {
|
||||
let r = T::regs();
|
||||
|
||||
// Set up the DMA write.
|
||||
let (ptr, len) = slice_ptr_parts(tx);
|
||||
let (ptr, tx_len) = slice_ptr_parts(tx);
|
||||
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx_len as _) });
|
||||
|
||||
// Set up the DMA read.
|
||||
let (ptr, len) = slice_ptr_parts_mut(rx);
|
||||
let (ptr, rx_len) = slice_ptr_parts_mut(rx);
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx_len as _) });
|
||||
|
||||
// ANOMALY 109 workaround
|
||||
#[cfg(feature = "nrf52832")]
|
||||
{
|
||||
let s = T::state();
|
||||
|
||||
r.events_started.reset();
|
||||
|
||||
// Set rx/tx buffer lengths to 0...
|
||||
r.txd.maxcnt.reset();
|
||||
r.rxd.maxcnt.reset();
|
||||
|
||||
// ...and keep track of original buffer lengths...
|
||||
s.tx.store(tx_len as _, Ordering::Relaxed);
|
||||
s.rx.store(rx_len as _, Ordering::Relaxed);
|
||||
|
||||
// ...signalling the start of the fake transfer.
|
||||
r.intenset.write(|w| w.started().bit(true));
|
||||
}
|
||||
|
||||
// Reset and enable the event
|
||||
r.events_end.reset();
|
||||
@ -386,18 +395,29 @@ impl<'d, T: Instance> Drop for Spim<'d, T> {
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
#[cfg(feature = "nrf52832")]
|
||||
use core::sync::atomic::AtomicU8;
|
||||
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct State {
|
||||
pub end_waker: AtomicWaker,
|
||||
#[cfg(feature = "nrf52832")]
|
||||
pub rx: AtomicU8,
|
||||
#[cfg(feature = "nrf52832")]
|
||||
pub tx: AtomicU8,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
end_waker: AtomicWaker::new(),
|
||||
#[cfg(feature = "nrf52832")]
|
||||
rx: AtomicU8::new(0),
|
||||
#[cfg(feature = "nrf52832")]
|
||||
tx: AtomicU8::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,47 +169,10 @@ impl<'d, T: Instance> Spis<'d, T> {
|
||||
// Enable SPIS instance.
|
||||
r.enable.write(|w| w.enable().enabled());
|
||||
|
||||
// Configure mode.
|
||||
let mode = config.mode;
|
||||
r.config.write(|w| {
|
||||
match mode {
|
||||
MODE_0 => {
|
||||
w.order().msb_first();
|
||||
w.cpol().active_high();
|
||||
w.cpha().leading();
|
||||
}
|
||||
MODE_1 => {
|
||||
w.order().msb_first();
|
||||
w.cpol().active_high();
|
||||
w.cpha().trailing();
|
||||
}
|
||||
MODE_2 => {
|
||||
w.order().msb_first();
|
||||
w.cpol().active_low();
|
||||
w.cpha().leading();
|
||||
}
|
||||
MODE_3 => {
|
||||
w.order().msb_first();
|
||||
w.cpol().active_low();
|
||||
w.cpha().trailing();
|
||||
}
|
||||
}
|
||||
let mut spis = Self { _p: spis };
|
||||
|
||||
w
|
||||
});
|
||||
|
||||
// Set over-read character.
|
||||
let orc = config.orc;
|
||||
r.orc.write(|w| unsafe { w.orc().bits(orc) });
|
||||
|
||||
// Set default character.
|
||||
let def = config.def;
|
||||
r.def.write(|w| unsafe { w.def().bits(def) });
|
||||
|
||||
// Configure auto-acquire on 'transfer end' event.
|
||||
if config.auto_acquire {
|
||||
r.shorts.write(|w| w.end_acquire().bit(true));
|
||||
}
|
||||
// Apply runtime peripheral configuration
|
||||
Self::set_config(&mut spis, &config);
|
||||
|
||||
// Disable all events interrupts.
|
||||
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
@ -217,7 +180,7 @@ impl<'d, T: Instance> Spis<'d, T> {
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
Self { _p: spis }
|
||||
spis
|
||||
}
|
||||
|
||||
fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {
|
||||
|
@ -57,7 +57,6 @@ impl<'d> Temp<'d> {
|
||||
/// ```no_run
|
||||
/// use embassy_nrf::{bind_interrupts, temp};
|
||||
/// use embassy_nrf::temp::Temp;
|
||||
/// use embassy_time::{Duration, Timer};
|
||||
///
|
||||
/// bind_interrupts!(struct Irqs {
|
||||
/// TEMP => temp::InterruptHandler;
|
||||
|
@ -167,9 +167,10 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
// Enable TWIM instance.
|
||||
r.enable.write(|w| w.enable().enabled());
|
||||
|
||||
// Configure frequency.
|
||||
r.frequency
|
||||
.write(|w| unsafe { w.frequency().bits(config.frequency as u32) });
|
||||
let mut twim = Self { _p: twim };
|
||||
|
||||
// Apply runtime peripheral configuration
|
||||
Self::set_config(&mut twim, &config);
|
||||
|
||||
// Disable all events interrupts
|
||||
r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) });
|
||||
@ -177,7 +178,7 @@ impl<'d, T: Instance> Twim<'d, T> {
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
Self { _p: twim }
|
||||
twim
|
||||
}
|
||||
|
||||
/// Set TX buffer, checking that it is in RAM and has suitable length.
|
||||
|
@ -94,6 +94,7 @@ impl ClockConfig {
|
||||
post_div1: 6,
|
||||
post_div2: 5,
|
||||
}),
|
||||
delay_multiplier: 128,
|
||||
}),
|
||||
ref_clk: RefClkConfig {
|
||||
src: RefClkSrc::Xosc,
|
||||
@ -203,6 +204,7 @@ pub struct XoscConfig {
|
||||
pub hz: u32,
|
||||
pub sys_pll: Option<PllConfig>,
|
||||
pub usb_pll: Option<PllConfig>,
|
||||
pub delay_multiplier: u32,
|
||||
}
|
||||
|
||||
pub struct PllConfig {
|
||||
@ -363,7 +365,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
// start XOSC
|
||||
// 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.
|
||||
start_xosc(config.hz);
|
||||
start_xosc(config.hz, config.delay_multiplier);
|
||||
|
||||
let pll_sys_freq = match config.sys_pll {
|
||||
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)
|
||||
}
|
||||
|
||||
fn start_xosc(crystal_hz: u32) {
|
||||
fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
|
||||
pac::XOSC
|
||||
.ctrl()
|
||||
.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.ctrl().write(|w| {
|
||||
w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ);
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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,6 +21,8 @@ pub enum AbortReason {
|
||||
NoAcknowledge,
|
||||
/// The arbitration was lost, e.g. electrical problems with the clock signal
|
||||
ArbitrationLoss,
|
||||
/// Transmit ended with data still in fifo
|
||||
TxNotEmpty(u16),
|
||||
Other(u32),
|
||||
}
|
||||
|
||||
@ -52,7 +54,7 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
const FIFO_SIZE: u8 = 16;
|
||||
pub const FIFO_SIZE: u8 = 16;
|
||||
|
||||
pub struct I2c<'d, T: Instance, M: Mode> {
|
||||
phantom: PhantomData<(&'d mut T, M)>,
|
||||
@ -636,6 +638,7 @@ mod eh1 {
|
||||
Self::Abort(AbortReason::NoAcknowledge) => {
|
||||
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
|
||||
}
|
||||
Self::Abort(AbortReason::TxNotEmpty(_)) => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
@ -738,8 +741,8 @@ mod nightly {
|
||||
}
|
||||
}
|
||||
|
||||
fn i2c_reserved_addr(addr: u16) -> bool {
|
||||
(addr & 0x78) == 0 || (addr & 0x78) == 0x78
|
||||
pub fn i2c_reserved_addr(addr: u16) -> bool {
|
||||
((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
|
338
embassy-rp/src/i2c_slave.rs
Normal file
338
embassy-rp/src/i2c_slave.rs
Normal file
@ -0,0 +1,338 @@
|
||||
use core::future;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
use pac::i2c;
|
||||
|
||||
use crate::i2c::{i2c_reserved_addr, AbortReason, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE};
|
||||
use crate::interrupt::typelevel::{Binding, Interrupt};
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
/// I2C error
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// I2C abort with error
|
||||
Abort(AbortReason),
|
||||
/// User passed in a response buffer that was 0 length
|
||||
InvalidResponseBufferLength,
|
||||
}
|
||||
|
||||
/// Received command
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Command {
|
||||
/// General Call
|
||||
GeneralCall(usize),
|
||||
/// Read
|
||||
Read,
|
||||
/// Write+read
|
||||
WriteRead(usize),
|
||||
/// Write
|
||||
Write(usize),
|
||||
}
|
||||
|
||||
/// Possible responses to responding to a read
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ReadStatus {
|
||||
/// Transaction Complete, controller naked our last byte
|
||||
Done,
|
||||
/// Transaction Incomplete, controller trying to read more bytes than were provided
|
||||
NeedMoreBytes,
|
||||
/// Transaction Complere, but controller stopped reading bytes before we ran out
|
||||
LeftoverBytes(u16),
|
||||
}
|
||||
|
||||
/// Slave Configuration
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Config {
|
||||
/// Target Address
|
||||
pub addr: u16,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self { addr: 0x55 }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct I2cSlave<'d, T: Instance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
||||
_irq: impl Binding<T::Interrupt, InterruptHandler<T>>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(_peri, scl, sda);
|
||||
|
||||
assert!(!i2c_reserved_addr(config.addr));
|
||||
assert!(config.addr != 0);
|
||||
|
||||
let p = T::regs();
|
||||
|
||||
let reset = T::reset();
|
||||
crate::reset::reset(reset);
|
||||
crate::reset::unreset_wait(reset);
|
||||
|
||||
p.ic_enable().write(|w| w.set_enable(false));
|
||||
|
||||
p.ic_sar().write(|w| w.set_ic_sar(config.addr));
|
||||
p.ic_con().modify(|w| {
|
||||
w.set_master_mode(false);
|
||||
w.set_ic_slave_disable(false);
|
||||
w.set_tx_empty_ctrl(true);
|
||||
});
|
||||
|
||||
// Set FIFO watermarks to 1 to make things simpler. This is encoded
|
||||
// by a register value of 0. Rx watermark should never change, but Tx watermark will be
|
||||
// adjusted in operation.
|
||||
p.ic_tx_tl().write(|w| w.set_tx_tl(0));
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
|
||||
|
||||
// Configure SCL & SDA pins
|
||||
scl.gpio().ctrl().write(|w| w.set_funcsel(3));
|
||||
sda.gpio().ctrl().write(|w| w.set_funcsel(3));
|
||||
|
||||
scl.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
sda.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
|
||||
// Clear interrupts
|
||||
p.ic_clr_intr().read();
|
||||
|
||||
// Enable I2C block
|
||||
p.ic_enable().write(|w| w.set_enable(true));
|
||||
|
||||
// mask everything initially
|
||||
p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
}
|
||||
|
||||
/// Calls `f` to check if we are ready or not.
|
||||
/// If not, `g` is called once the waker is set (to eg enable the required interrupts).
|
||||
#[inline(always)]
|
||||
async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U
|
||||
where
|
||||
F: FnMut(&mut Self) -> Poll<U>,
|
||||
G: FnMut(&mut Self),
|
||||
{
|
||||
future::poll_fn(|cx| {
|
||||
let r = f(self);
|
||||
|
||||
trace!("intr p: {:013b}", T::regs().ic_raw_intr_stat().read().0);
|
||||
|
||||
if r.is_pending() {
|
||||
T::waker().register(cx.waker());
|
||||
g(self);
|
||||
}
|
||||
|
||||
r
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn drain_fifo(&mut self, buffer: &mut [u8], offset: usize) -> usize {
|
||||
let p = T::regs();
|
||||
let len = p.ic_rxflr().read().rxflr() as usize;
|
||||
let end = offset + len;
|
||||
for i in offset..end {
|
||||
buffer[i] = p.ic_data_cmd().read().dat();
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_to_fifo(&mut self, buffer: &[u8]) {
|
||||
let p = T::regs();
|
||||
for byte in buffer {
|
||||
p.ic_data_cmd().write(|w| w.set_dat(*byte));
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait asynchronously for commands from an I2C master.
|
||||
/// `buffer` is provided in case master does a 'write' and is unused for 'read'.
|
||||
pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
|
||||
let p = T::regs();
|
||||
|
||||
p.ic_clr_intr().read();
|
||||
// set rx fifo watermark to 1 byte
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
|
||||
|
||||
let mut len = 0;
|
||||
let ret = self
|
||||
.wait_on(
|
||||
|me| {
|
||||
let stat = p.ic_raw_intr_stat().read();
|
||||
if p.ic_rxflr().read().rxflr() > 0 {
|
||||
len = me.drain_fifo(buffer, len);
|
||||
// we're recieving data, set rx fifo watermark to 12 bytes to reduce interrupt noise
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(11));
|
||||
}
|
||||
|
||||
if stat.restart_det() && stat.rd_req() {
|
||||
Poll::Ready(Ok(Command::WriteRead(len)))
|
||||
} else if stat.gen_call() && stat.stop_det() && len > 0 {
|
||||
Poll::Ready(Ok(Command::GeneralCall(len)))
|
||||
} else if stat.stop_det() {
|
||||
Poll::Ready(Ok(Command::Write(len)))
|
||||
} else if stat.rd_req() {
|
||||
Poll::Ready(Ok(Command::Read))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
},
|
||||
|_me| {
|
||||
p.ic_intr_mask().modify(|w| {
|
||||
w.set_m_stop_det(true);
|
||||
w.set_m_restart_det(true);
|
||||
w.set_m_gen_call(true);
|
||||
w.set_m_rd_req(true);
|
||||
w.set_m_rx_full(true);
|
||||
});
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
p.ic_clr_intr().read();
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
/// Respond to an I2C master READ command, asynchronously.
|
||||
pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<ReadStatus, Error> {
|
||||
let p = T::regs();
|
||||
|
||||
if buffer.len() == 0 {
|
||||
return Err(Error::InvalidResponseBufferLength);
|
||||
}
|
||||
|
||||
let mut chunks = buffer.chunks(FIFO_SIZE as usize);
|
||||
|
||||
let ret = self
|
||||
.wait_on(
|
||||
|me| {
|
||||
if let Err(abort_reason) = me.read_and_clear_abort_reason() {
|
||||
if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason {
|
||||
return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes)));
|
||||
} else {
|
||||
return Poll::Ready(Err(abort_reason));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(chunk) = chunks.next() {
|
||||
me.write_to_fifo(chunk);
|
||||
|
||||
Poll::Pending
|
||||
} else {
|
||||
let stat = p.ic_raw_intr_stat().read();
|
||||
|
||||
if stat.rx_done() && stat.stop_det() {
|
||||
Poll::Ready(Ok(ReadStatus::Done))
|
||||
} else if stat.rd_req() {
|
||||
Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
},
|
||||
|_me| {
|
||||
p.ic_intr_mask().modify(|w| {
|
||||
w.set_m_stop_det(true);
|
||||
w.set_m_rx_done(true);
|
||||
w.set_m_tx_empty(true);
|
||||
w.set_m_tx_abrt(true);
|
||||
})
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
p.ic_clr_intr().read();
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
/// Respond to reads with the fill byte until the controller stops asking
|
||||
pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> {
|
||||
loop {
|
||||
match self.respond_to_read(&[fill]).await {
|
||||
Ok(ReadStatus::NeedMoreBytes) => (),
|
||||
Ok(_) => break Ok(()),
|
||||
Err(e) => break Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Respond to a master read, then fill any remaining read bytes with `fill`
|
||||
pub async fn respond_and_fill(&mut self, buffer: &[u8], fill: u8) -> Result<ReadStatus, Error> {
|
||||
let resp_stat = self.respond_to_read(buffer).await?;
|
||||
|
||||
if resp_stat == ReadStatus::NeedMoreBytes {
|
||||
self.respond_till_stop(fill).await?;
|
||||
Ok(ReadStatus::Done)
|
||||
} else {
|
||||
Ok(resp_stat)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
|
||||
let p = T::regs();
|
||||
let mut abort_reason = p.ic_tx_abrt_source().read();
|
||||
|
||||
// Mask off fifo flush count
|
||||
let tx_flush_cnt = abort_reason.tx_flush_cnt();
|
||||
abort_reason.set_tx_flush_cnt(0);
|
||||
|
||||
// Mask off master_dis
|
||||
abort_reason.set_abrt_master_dis(false);
|
||||
|
||||
if abort_reason.0 != 0 {
|
||||
// Note clearing the abort flag also clears the reason, and this
|
||||
// instance of flag is clear-on-read! Note also the
|
||||
// IC_CLR_TX_ABRT register always reads as 0.
|
||||
p.ic_clr_tx_abrt().read();
|
||||
|
||||
let reason = if abort_reason.abrt_7b_addr_noack()
|
||||
| abort_reason.abrt_10addr1_noack()
|
||||
| abort_reason.abrt_10addr2_noack()
|
||||
{
|
||||
AbortReason::NoAcknowledge
|
||||
} else if abort_reason.arb_lost() {
|
||||
AbortReason::ArbitrationLoss
|
||||
} else if abort_reason.abrt_slvflush_txfifo() {
|
||||
AbortReason::TxNotEmpty(tx_flush_cnt)
|
||||
} else {
|
||||
AbortReason::Other(abort_reason.0)
|
||||
};
|
||||
|
||||
Err(Error::Abort(reason))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ pub mod flash;
|
||||
mod float;
|
||||
pub mod gpio;
|
||||
pub mod i2c;
|
||||
pub mod i2c_slave;
|
||||
pub mod multicore;
|
||||
pub mod pwm;
|
||||
mod reset;
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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,13 +8,13 @@ license = "MIT OR Apache-2.0"
|
||||
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/"
|
||||
|
||||
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time", "low-power"]
|
||||
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"]
|
||||
flavors = [
|
||||
{ regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" },
|
||||
{ regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" },
|
||||
{ regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" },
|
||||
{ regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi", features = ["low-power"] },
|
||||
{ regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" },
|
||||
{ regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" },
|
||||
{ regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" },
|
||||
@ -58,7 +58,7 @@ sdio-host = "0.5.0"
|
||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||
critical-section = "1.1"
|
||||
atomic-polyfill = "1.0.1"
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9a61a1f090462df8bd1751f89951f04934fdceb3" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e667107cf81934383ec5744f49b2cda0599ec749" }
|
||||
vcell = "0.1.3"
|
||||
bxcan = "0.7.0"
|
||||
nb = "1.0.0"
|
||||
@ -77,7 +77,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
||||
[build-dependencies]
|
||||
proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9a61a1f090462df8bd1751f89951f04934fdceb3", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e667107cf81934383ec5744f49b2cda0599ec749", default-features = false, features = ["metadata"]}
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
|
@ -308,13 +308,11 @@ fn main() {
|
||||
// ========
|
||||
// Generate RccPeripheral impls
|
||||
|
||||
let refcounted_peripherals = HashSet::from(["usart", "adc"]);
|
||||
let mut refcount_statics = HashSet::new();
|
||||
|
||||
for p in METADATA.peripherals {
|
||||
// generating RccPeripheral impl for H7 ADC3 would result in bad frequency
|
||||
if !singletons.contains(&p.name.to_string())
|
||||
|| (p.name == "ADC3" && METADATA.line.starts_with("STM32H7"))
|
||||
|| (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "f3"))
|
||||
|| (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "v4"))
|
||||
{
|
||||
if !singletons.contains(&p.name.to_string()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -344,11 +342,36 @@ fn main() {
|
||||
TokenStream::new()
|
||||
};
|
||||
|
||||
let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" };
|
||||
let pname = format_ident!("{}", p.name);
|
||||
let clk = format_ident!("{}", rcc.clock.to_ascii_lowercase());
|
||||
let en_reg = format_ident!("{}", en.register.to_ascii_lowercase());
|
||||
let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase());
|
||||
|
||||
let (before_enable, before_disable) = if refcounted_peripherals.contains(ptype) {
|
||||
let refcount_static =
|
||||
format_ident!("{}_{}", en.register.to_ascii_uppercase(), en.field.to_ascii_uppercase());
|
||||
|
||||
refcount_statics.insert(refcount_static.clone());
|
||||
|
||||
(
|
||||
quote! {
|
||||
unsafe { refcount_statics::#refcount_static += 1 };
|
||||
if unsafe { refcount_statics::#refcount_static } > 1 {
|
||||
return;
|
||||
}
|
||||
},
|
||||
quote! {
|
||||
unsafe { refcount_statics::#refcount_static -= 1 };
|
||||
if unsafe { refcount_statics::#refcount_static } > 0 {
|
||||
return;
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(TokenStream::new(), TokenStream::new())
|
||||
};
|
||||
|
||||
g.extend(quote! {
|
||||
impl crate::rcc::sealed::RccPeripheral for peripherals::#pname {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
@ -356,6 +379,7 @@ fn main() {
|
||||
}
|
||||
fn enable() {
|
||||
critical_section::with(|_| {
|
||||
#before_enable
|
||||
#[cfg(feature = "low-power")]
|
||||
crate::rcc::clock_refcount_add();
|
||||
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
|
||||
@ -364,6 +388,7 @@ fn main() {
|
||||
}
|
||||
fn disable() {
|
||||
critical_section::with(|_| {
|
||||
#before_disable
|
||||
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
|
||||
#[cfg(feature = "low-power")]
|
||||
crate::rcc::clock_refcount_sub();
|
||||
@ -379,6 +404,19 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
let mut refcount_mod = TokenStream::new();
|
||||
for refcount_static in refcount_statics {
|
||||
refcount_mod.extend(quote! {
|
||||
pub(crate) static mut #refcount_static: u8 = 0;
|
||||
});
|
||||
}
|
||||
|
||||
g.extend(quote! {
|
||||
mod refcount_statics {
|
||||
#refcount_mod
|
||||
}
|
||||
});
|
||||
|
||||
// ========
|
||||
// Generate fns to enable GPIO, DMA in RCC
|
||||
|
||||
@ -664,6 +702,10 @@ fn main() {
|
||||
|
||||
// ADC is special
|
||||
if regs.kind == "adc" {
|
||||
if p.rcc.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let peri = format_ident!("{}", p.name);
|
||||
let pin_name = format_ident!("{}", pin.pin);
|
||||
|
||||
|
@ -60,7 +60,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
|
||||
fn freq() -> Hertz {
|
||||
unsafe { get_freqs() }.adc
|
||||
unsafe { get_freqs() }.adc.unwrap()
|
||||
}
|
||||
|
||||
pub fn sample_time_for_us(&self, us: u32) -> SampleTime {
|
||||
|
123
embassy-stm32/src/adc/f3.rs
Normal file
123
embassy-stm32/src/adc/f3.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use embassy_hal_internal::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use crate::adc::{Adc, AdcPin, Instance, SampleTime};
|
||||
use crate::time::Hertz;
|
||||
use crate::Peripheral;
|
||||
|
||||
pub const VDDA_CALIB_MV: u32 = 3300;
|
||||
pub const ADC_MAX: u32 = (1 << 12) - 1;
|
||||
pub const VREF_INT: u32 = 1230;
|
||||
|
||||
pub struct Vref;
|
||||
impl<T: Instance> AdcPin<T> for Vref {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Vref {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
16
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
use crate::pac::adc::vals;
|
||||
|
||||
into_ref!(adc);
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
// Enable the adc regulator
|
||||
T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::INTERMEDIATE));
|
||||
T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::ENABLED));
|
||||
|
||||
// Wait for the regulator to stabilize
|
||||
delay.delay_us(10);
|
||||
|
||||
assert!(!T::regs().cr().read().aden());
|
||||
|
||||
// Begin calibration
|
||||
T::regs().cr().modify(|w| w.set_adcaldif(false));
|
||||
T::regs().cr().modify(|w| w.set_adcal(true));
|
||||
|
||||
while T::regs().cr().read().adcal() {}
|
||||
|
||||
// Enable the adc
|
||||
T::regs().cr().modify(|w| w.set_aden(true));
|
||||
|
||||
// Wait until the adc is ready
|
||||
while !T::regs().isr().read().adrdy() {}
|
||||
|
||||
Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn freq() -> Hertz {
|
||||
<T as crate::adc::sealed::Instance>::frequency()
|
||||
}
|
||||
|
||||
pub fn sample_time_for_us(&self, us: u32) -> SampleTime {
|
||||
match us * Self::freq().0 / 1_000_000 {
|
||||
0..=1 => SampleTime::Cycles1_5,
|
||||
2..=4 => SampleTime::Cycles4_5,
|
||||
5..=7 => SampleTime::Cycles7_5,
|
||||
8..=19 => SampleTime::Cycles19_5,
|
||||
20..=61 => SampleTime::Cycles61_5,
|
||||
62..=181 => SampleTime::Cycles181_5,
|
||||
_ => SampleTime::Cycles601_5,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_vref(&self, _delay: &mut impl DelayUs<u32>) -> Vref {
|
||||
T::common_regs().ccr().modify(|w| w.set_vrefen(true));
|
||||
|
||||
Vref {}
|
||||
}
|
||||
|
||||
pub fn enable_temperature(&self) -> Temperature {
|
||||
T::common_regs().ccr().modify(|w| w.set_tsen(true));
|
||||
|
||||
Temperature {}
|
||||
}
|
||||
|
||||
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
|
||||
self.sample_time = sample_time;
|
||||
}
|
||||
|
||||
/// Perform a single conversion.
|
||||
fn convert(&mut self) -> u16 {
|
||||
T::regs().isr().write(|_| {});
|
||||
T::regs().cr().modify(|w| w.set_adstart(true));
|
||||
|
||||
while !T::regs().isr().read().eoc() && !T::regs().isr().read().eos() {}
|
||||
T::regs().isr().write(|_| {});
|
||||
|
||||
T::regs().dr().read().rdata()
|
||||
}
|
||||
|
||||
pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
|
||||
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
||||
|
||||
// Configure the channel to sample
|
||||
T::regs().sqr1().write(|w| w.set_sq(0, pin.channel()));
|
||||
self.convert()
|
||||
}
|
||||
|
||||
fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
if ch <= 9 {
|
||||
T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
|
||||
} else {
|
||||
T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +1,24 @@
|
||||
#![macro_use]
|
||||
|
||||
#[cfg(not(any(adc_f3, adc_f3_v2)))]
|
||||
#[cfg(not(adc_f3_v2))]
|
||||
#[cfg_attr(adc_f1, path = "f1.rs")]
|
||||
#[cfg_attr(adc_f3, path = "f3.rs")]
|
||||
#[cfg_attr(adc_v1, path = "v1.rs")]
|
||||
#[cfg_attr(adc_v2, path = "v2.rs")]
|
||||
#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")]
|
||||
#[cfg_attr(adc_v4, path = "v4.rs")]
|
||||
mod _version;
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))]
|
||||
#[cfg(not(any(adc_f1, adc_f3_v2)))]
|
||||
mod resolution;
|
||||
mod sample_time;
|
||||
|
||||
#[cfg(not(any(adc_f3, adc_f3_v2)))]
|
||||
#[allow(unused)]
|
||||
#[cfg(not(adc_f3_v2))]
|
||||
pub use _version::*;
|
||||
#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))]
|
||||
pub use resolution::Resolution;
|
||||
#[cfg(not(any(adc_f3, adc_f3_v2)))]
|
||||
#[cfg(not(adc_f3_v2))]
|
||||
pub use sample_time::SampleTime;
|
||||
|
||||
use crate::peripherals;
|
||||
@ -25,15 +26,17 @@ use crate::peripherals;
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
#[allow(unused)]
|
||||
adc: crate::PeripheralRef<'d, T>,
|
||||
#[cfg(not(any(adc_f3, adc_f3_v2)))]
|
||||
#[cfg(not(adc_f3_v2))]
|
||||
sample_time: SampleTime,
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs() -> crate::pac::adc::Adc;
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))]
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
||||
#[cfg(adc_f3)]
|
||||
fn frequency() -> crate::time::Hertz;
|
||||
}
|
||||
|
||||
pub trait AdcPin<T: Instance> {
|
||||
@ -45,22 +48,22 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4)))]
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4, adc_f3)))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
|
||||
#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4))]
|
||||
#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4, adc_f3))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
|
||||
|
||||
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
|
||||
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
||||
|
||||
#[cfg(not(stm32h7))]
|
||||
#[cfg(not(any(stm32h7, adc_f3, adc_v4)))]
|
||||
foreach_peripheral!(
|
||||
(adc, $inst:ident) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::$inst
|
||||
}
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))]
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, $common_inst:ident) => {
|
||||
@ -74,9 +77,10 @@ foreach_peripheral!(
|
||||
};
|
||||
);
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
#[cfg(any(stm32h7, adc_f3, adc_v4))]
|
||||
foreach_peripheral!(
|
||||
(adc, ADC3) => {
|
||||
#[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))]
|
||||
impl crate::adc::sealed::Instance for peripherals::ADC3 {
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::ADC3
|
||||
@ -89,16 +93,49 @@ foreach_peripheral!(
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(adc_f3)]
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
unsafe { crate::rcc::get_freqs() }.adc34.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))]
|
||||
impl crate::adc::Instance for peripherals::ADC3 {}
|
||||
};
|
||||
(adc, ADC4) => {
|
||||
#[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))]
|
||||
impl crate::adc::sealed::Instance for peripherals::ADC4 {
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::ADC4
|
||||
}
|
||||
#[cfg(not(any(adc_f1, adc_v1)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, ADC3_COMMON) => {
|
||||
return crate::pac::ADC3_COMMON
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(adc_f3)]
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
unsafe { crate::rcc::get_freqs() }.adc34.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))]
|
||||
impl crate::adc::Instance for peripherals::ADC4 {}
|
||||
};
|
||||
(adc, ADC5) => {
|
||||
|
||||
};
|
||||
(adc, $inst:ident) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::$inst {
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::$inst
|
||||
}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
#[cfg(not(any(adc_f1, adc_v1)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||
foreach_peripheral!{
|
||||
(adccommon, ADC_COMMON) => {
|
||||
@ -106,6 +143,11 @@ foreach_peripheral!(
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(adc_f3)]
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
unsafe { crate::rcc::get_freqs() }.adc.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::adc::Instance for peripherals::$inst {}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Resolution {
|
||||
TwelveBit,
|
||||
@ -19,7 +19,7 @@ pub enum Resolution {
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
|
||||
{
|
||||
Self::TwelveBit
|
||||
}
|
||||
@ -40,7 +40,7 @@ impl From<Resolution> for crate::pac::adc::vals::Res {
|
||||
Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT,
|
||||
Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT,
|
||||
Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT,
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
|
||||
Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT,
|
||||
}
|
||||
}
|
||||
@ -56,7 +56,7 @@ impl Resolution {
|
||||
Resolution::TwelveBit => (1 << 12) - 1,
|
||||
Resolution::TenBit => (1 << 10) - 1,
|
||||
Resolution::EightBit => (1 << 8) - 1,
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))]
|
||||
#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))]
|
||||
Resolution::SixBit => (1 << 6) - 1,
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#[cfg(not(any(adc_f3, adc_f3_v2)))]
|
||||
#[cfg(not(adc_f3_v2))]
|
||||
macro_rules! impl_sample_time {
|
||||
($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => {
|
||||
#[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")]
|
||||
@ -105,3 +105,19 @@ impl_sample_time!(
|
||||
("810.5", Cycles810_5, CYCLES810_5)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(adc_f3)]
|
||||
impl_sample_time!(
|
||||
"1.5",
|
||||
Cycles1_5,
|
||||
(
|
||||
("1.5", Cycles1_5, CYCLES1_5),
|
||||
("2.5", Cycles2_5, CYCLES2_5),
|
||||
("4.5", Cycles4_5, CYCLES4_5),
|
||||
("7.5", Cycles7_5, CYCLES7_5),
|
||||
("19.5", Cycles19_5, CYCLES19_5),
|
||||
("61.5", Cycles61_5, CYCLES61_5),
|
||||
("181.5", Cycles181_5, CYCLES181_5),
|
||||
("601.5", Cycles601_5, CYCLES601_5)
|
||||
)
|
||||
);
|
||||
|
@ -13,7 +13,7 @@ pub const VREF_CALIB_MV: u32 = 3000;
|
||||
/// configuration.
|
||||
fn enable() {
|
||||
critical_section::with(|_| {
|
||||
#[cfg(stm32h7)]
|
||||
#[cfg(any(stm32h7, stm32wl))]
|
||||
crate::pac::RCC.apb2enr().modify(|w| w.set_adcen(true));
|
||||
#[cfg(stm32g0)]
|
||||
crate::pac::RCC.apbenr2().modify(|w| w.set_adcen(true));
|
||||
@ -26,9 +26,9 @@ pub struct VrefInt;
|
||||
impl<T: Instance> AdcPin<T> for VrefInt {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
#[cfg(not(stm32g0))]
|
||||
#[cfg(not(adc_g0))]
|
||||
let val = 0;
|
||||
#[cfg(stm32g0)]
|
||||
#[cfg(adc_g0)]
|
||||
let val = 13;
|
||||
val
|
||||
}
|
||||
@ -38,9 +38,9 @@ pub struct Temperature;
|
||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
#[cfg(not(stm32g0))]
|
||||
#[cfg(not(adc_g0))]
|
||||
let val = 17;
|
||||
#[cfg(stm32g0)]
|
||||
#[cfg(adc_g0)]
|
||||
let val = 12;
|
||||
val
|
||||
}
|
||||
@ -50,9 +50,9 @@ pub struct Vbat;
|
||||
impl<T: Instance> AdcPin<T> for Vbat {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
#[cfg(not(stm32g0))]
|
||||
#[cfg(not(adc_g0))]
|
||||
let val = 18;
|
||||
#[cfg(stm32g0)]
|
||||
#[cfg(adc_g0)]
|
||||
let val = 14;
|
||||
val
|
||||
}
|
||||
@ -92,9 +92,14 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
|
||||
pub fn enable_vrefint(&self, delay: &mut impl DelayUs<u32>) -> VrefInt {
|
||||
#[cfg(not(adc_g0))]
|
||||
T::common_regs().ccr().modify(|reg| {
|
||||
reg.set_vrefen(true);
|
||||
});
|
||||
#[cfg(adc_g0)]
|
||||
T::regs().ccr().modify(|reg| {
|
||||
reg.set_vrefen(true);
|
||||
});
|
||||
|
||||
// "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us
|
||||
// to stabilize the internal voltage reference, we wait a little more.
|
||||
@ -106,17 +111,27 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
|
||||
pub fn enable_temperature(&self) -> Temperature {
|
||||
#[cfg(not(adc_g0))]
|
||||
T::common_regs().ccr().modify(|reg| {
|
||||
reg.set_ch17sel(true);
|
||||
});
|
||||
#[cfg(adc_g0)]
|
||||
T::regs().ccr().modify(|reg| {
|
||||
reg.set_tsen(true);
|
||||
});
|
||||
|
||||
Temperature {}
|
||||
}
|
||||
|
||||
pub fn enable_vbat(&self) -> Vbat {
|
||||
#[cfg(not(adc_g0))]
|
||||
T::common_regs().ccr().modify(|reg| {
|
||||
reg.set_ch18sel(true);
|
||||
});
|
||||
#[cfg(adc_g0)]
|
||||
T::regs().ccr().modify(|reg| {
|
||||
reg.set_vbaten(true);
|
||||
});
|
||||
|
||||
Vbat {}
|
||||
}
|
||||
@ -126,9 +141,9 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
|
||||
pub fn set_resolution(&mut self, resolution: Resolution) {
|
||||
#[cfg(not(stm32g0))]
|
||||
#[cfg(not(adc_g0))]
|
||||
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
|
||||
#[cfg(stm32g0)]
|
||||
#[cfg(adc_g0)]
|
||||
T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
|
||||
}
|
||||
|
||||
@ -182,9 +197,9 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
||||
|
||||
// Select channel
|
||||
#[cfg(not(stm32g0))]
|
||||
#[cfg(not(adc_g0))]
|
||||
T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
|
||||
#[cfg(stm32g0)]
|
||||
#[cfg(adc_g0)]
|
||||
T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel()));
|
||||
|
||||
// Some models are affected by an erratum:
|
||||
@ -203,12 +218,12 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
val
|
||||
}
|
||||
|
||||
#[cfg(stm32g0)]
|
||||
#[cfg(adc_g0)]
|
||||
fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
|
||||
T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into()));
|
||||
}
|
||||
|
||||
#[cfg(not(stm32g0))]
|
||||
#[cfg(not(adc_g0))]
|
||||
fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
let sample_time = sample_time.into();
|
||||
T::regs()
|
||||
|
@ -1,6 +1,5 @@
|
||||
use core::sync::atomic::{AtomicU8, Ordering};
|
||||
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
#[allow(unused)]
|
||||
use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
|
||||
use pac::adccommon::vals::Presc;
|
||||
|
||||
@ -13,12 +12,31 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
|
||||
/// VREF voltage used for factory calibration of VREFINTCAL register.
|
||||
pub const VREF_CALIB_MV: u32 = 3300;
|
||||
|
||||
// NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
|
||||
/// Max single ADC operation clock frequency
|
||||
#[cfg(stm32g4)]
|
||||
const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);
|
||||
#[cfg(stm32h7)]
|
||||
const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50);
|
||||
|
||||
#[cfg(stm32g4)]
|
||||
const VREF_CHANNEL: u8 = 18;
|
||||
#[cfg(stm32g4)]
|
||||
const TEMP_CHANNEL: u8 = 16;
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
const VREF_CHANNEL: u8 = 19;
|
||||
#[cfg(stm32h7)]
|
||||
const TEMP_CHANNEL: u8 = 18;
|
||||
|
||||
// TODO this should be 14 for H7a/b/35
|
||||
const VBAT_CHANNEL: u8 = 17;
|
||||
|
||||
// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
|
||||
pub struct VrefInt;
|
||||
impl<T: Instance> InternalChannel<T> for VrefInt {}
|
||||
impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
19
|
||||
VREF_CHANNEL
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +44,7 @@ pub struct Temperature;
|
||||
impl<T: Instance> InternalChannel<T> for Temperature {}
|
||||
impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
TEMP_CHANNEL
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,128 +52,10 @@ pub struct Vbat;
|
||||
impl<T: Instance> InternalChannel<T> for Vbat {}
|
||||
impl<T: Instance> super::sealed::InternalChannel<T> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
// TODO this should be 14 for H7a/b/35
|
||||
17
|
||||
VBAT_CHANNEL
|
||||
}
|
||||
}
|
||||
|
||||
static ADC12_ENABLE_COUNTER: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
foreach_peripheral!(
|
||||
(adc, ADC1) => {
|
||||
impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC1 {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
critical_section::with(|_| {
|
||||
match unsafe { crate::rcc::get_freqs() }.adc {
|
||||
Some(ck) => ck,
|
||||
None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn enable() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true))
|
||||
});
|
||||
ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn disable() {
|
||||
if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false));
|
||||
})
|
||||
}
|
||||
ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn reset() {
|
||||
if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true));
|
||||
crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::rcc::RccPeripheral for crate::peripherals::ADC1 {}
|
||||
};
|
||||
(adc, ADC2) => {
|
||||
impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC2 {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
critical_section::with(|_| {
|
||||
match unsafe { crate::rcc::get_freqs() }.adc {
|
||||
Some(ck) => ck,
|
||||
None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn enable() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true))
|
||||
});
|
||||
ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn disable() {
|
||||
if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false));
|
||||
})
|
||||
}
|
||||
ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn reset() {
|
||||
if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true));
|
||||
crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::rcc::RccPeripheral for crate::peripherals::ADC2 {}
|
||||
};
|
||||
(adc, ADC3) => {
|
||||
impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC3 {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
critical_section::with(|_| {
|
||||
match unsafe { crate::rcc::get_freqs() }.adc {
|
||||
Some(ck) => ck,
|
||||
None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn enable() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(true))
|
||||
});
|
||||
}
|
||||
|
||||
fn disable() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false));
|
||||
})
|
||||
}
|
||||
|
||||
fn reset() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(true));
|
||||
crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(false));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::rcc::RccPeripheral for crate::peripherals::ADC3 {}
|
||||
};
|
||||
);
|
||||
|
||||
// NOTE (unused): The prescaler enum closely copies the hardware capabilities,
|
||||
// but high prescaling doesn't make a lot of sense in the current implementation and is ommited.
|
||||
#[allow(unused)]
|
||||
@ -176,7 +76,7 @@ enum Prescaler {
|
||||
|
||||
impl Prescaler {
|
||||
fn from_ker_ck(frequency: Hertz) -> Self {
|
||||
let raw_prescaler = frequency.0 / 50_000_000;
|
||||
let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0;
|
||||
match raw_prescaler {
|
||||
0 => Self::NotDivided,
|
||||
1 => Self::DividedBy2,
|
||||
@ -237,20 +137,23 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
let frequency = Hertz(T::frequency().0 / prescaler.divisor());
|
||||
info!("ADC frequency set to {} Hz", frequency.0);
|
||||
|
||||
if frequency > Hertz::mhz(50) {
|
||||
panic!("Maximal allowed frequency for the ADC is 50 MHz and it varies with different packages, refer to ST docs for more information.");
|
||||
if frequency > MAX_ADC_CLK_FREQ {
|
||||
panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 );
|
||||
}
|
||||
let boost = if frequency < Hertz::khz(6_250) {
|
||||
Boost::LT6_25
|
||||
} else if frequency < Hertz::khz(12_500) {
|
||||
Boost::LT12_5
|
||||
} else if frequency < Hertz::mhz(25) {
|
||||
Boost::LT25
|
||||
} else {
|
||||
Boost::LT50
|
||||
};
|
||||
T::regs().cr().modify(|w| w.set_boost(boost));
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
{
|
||||
let boost = if frequency < Hertz::khz(6_250) {
|
||||
Boost::LT6_25
|
||||
} else if frequency < Hertz::khz(12_500) {
|
||||
Boost::LT12_5
|
||||
} else if frequency < Hertz::mhz(25) {
|
||||
Boost::LT25
|
||||
} else {
|
||||
Boost::LT50
|
||||
};
|
||||
T::regs().cr().modify(|w| w.set_boost(boost));
|
||||
}
|
||||
let mut s = Self {
|
||||
adc,
|
||||
sample_time: Default::default(),
|
||||
@ -379,10 +282,14 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
// Configure channel
|
||||
Self::set_channel_sample_time(channel, self.sample_time);
|
||||
|
||||
T::regs().cfgr2().modify(|w| w.set_lshift(0));
|
||||
T::regs()
|
||||
.pcsel()
|
||||
.write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
|
||||
#[cfg(stm32h7)]
|
||||
{
|
||||
T::regs().cfgr2().modify(|w| w.set_lshift(0));
|
||||
T::regs()
|
||||
.pcsel()
|
||||
.write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
|
||||
}
|
||||
|
||||
T::regs().sqr1().write(|reg| {
|
||||
reg.set_sq(0, channel);
|
||||
reg.set_l(0);
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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,8 @@ pub use traits::Instance;
|
||||
#[allow(unused_imports)]
|
||||
use crate::gpio::sealed::{AFType, Pin};
|
||||
use crate::gpio::AnyPin;
|
||||
#[cfg(stm32f334)]
|
||||
use crate::rcc::get_freqs;
|
||||
use crate::time::Hertz;
|
||||
use crate::Peripheral;
|
||||
|
||||
@ -158,17 +160,29 @@ impl<'d, T: Instance> AdvancedPwm<'d, T> {
|
||||
T::enable();
|
||||
<T as crate::rcc::sealed::RccPeripheral>::reset();
|
||||
|
||||
// // Enable and and stabilize the DLL
|
||||
// T::regs().dllcr().modify(|w| {
|
||||
// // w.set_calen(true);
|
||||
// // w.set_calrte(11);
|
||||
// w.set_cal(true);
|
||||
// });
|
||||
//
|
||||
// debug!("wait for dll calibration");
|
||||
// while !T::regs().isr().read().dllrdy() {}
|
||||
//
|
||||
// debug!("dll calibration complete");
|
||||
#[cfg(stm32f334)]
|
||||
if unsafe { get_freqs() }.hrtim.is_some() {
|
||||
// Enable and and stabilize the DLL
|
||||
T::regs().dllcr().modify(|w| {
|
||||
w.set_cal(true);
|
||||
});
|
||||
|
||||
trace!("hrtim: wait for dll calibration");
|
||||
while !T::regs().isr().read().dllrdy() {}
|
||||
|
||||
trace!("hrtim: dll calibration complete");
|
||||
|
||||
// Enable periodic calibration
|
||||
// Cal must be disabled before we can enable it
|
||||
T::regs().dllcr().modify(|w| {
|
||||
w.set_cal(false);
|
||||
});
|
||||
|
||||
T::regs().dllcr().modify(|w| {
|
||||
w.set_calen(true);
|
||||
w.set_calrte(11);
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
_inner: tim,
|
||||
|
@ -1,31 +1,17 @@
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
use crate::time::Hertz;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum Prescaler {
|
||||
Div1,
|
||||
Div2,
|
||||
Div4,
|
||||
Div8,
|
||||
Div16,
|
||||
Div32,
|
||||
Div64,
|
||||
Div128,
|
||||
}
|
||||
|
||||
impl From<Prescaler> for u32 {
|
||||
fn from(val: Prescaler) -> Self {
|
||||
match val {
|
||||
Prescaler::Div1 => 1,
|
||||
Prescaler::Div2 => 2,
|
||||
Prescaler::Div4 => 4,
|
||||
Prescaler::Div8 => 8,
|
||||
Prescaler::Div16 => 16,
|
||||
Prescaler::Div32 => 32,
|
||||
Prescaler::Div64 => 64,
|
||||
Prescaler::Div128 => 128,
|
||||
}
|
||||
}
|
||||
Div1 = 1,
|
||||
Div2 = 2,
|
||||
Div4 = 4,
|
||||
Div8 = 8,
|
||||
Div16 = 16,
|
||||
Div32 = 32,
|
||||
Div64 = 64,
|
||||
Div128 = 128,
|
||||
}
|
||||
|
||||
impl From<Prescaler> for u8 {
|
||||
@ -72,7 +58,7 @@ impl Prescaler {
|
||||
Prescaler::Div128,
|
||||
]
|
||||
.iter()
|
||||
.skip_while(|psc| <Prescaler as Into<u32>>::into(**psc) <= val)
|
||||
.skip_while(|psc| **psc as u32 <= val)
|
||||
.next()
|
||||
.unwrap()
|
||||
}
|
||||
@ -80,7 +66,7 @@ impl Prescaler {
|
||||
pub fn compute_min_low_res(val: u32) -> Self {
|
||||
*[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128]
|
||||
.iter()
|
||||
.skip_while(|psc| <Prescaler as Into<u32>>::into(**psc) <= val)
|
||||
.skip_while(|psc| **psc as u32 <= val)
|
||||
.next()
|
||||
.unwrap()
|
||||
}
|
||||
@ -118,7 +104,13 @@ foreach_interrupt! {
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
|
||||
let f = frequency.0;
|
||||
#[cfg(not(stm32f334))]
|
||||
let timer_f = Self::frequency().0;
|
||||
#[cfg(stm32f334)]
|
||||
let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(
|
||||
Self::frequency()
|
||||
).0;
|
||||
|
||||
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
|
||||
let psc = if Self::regs().isr().read().dllrdy() {
|
||||
Prescaler::compute_min_high_res(psc_min)
|
||||
@ -126,8 +118,7 @@ foreach_interrupt! {
|
||||
Prescaler::compute_min_low_res(psc_min)
|
||||
};
|
||||
|
||||
let psc_val: u32 = psc.into();
|
||||
let timer_f = 32 * (timer_f / psc_val);
|
||||
let timer_f = 32 * (timer_f / psc as u32);
|
||||
let per: u16 = (timer_f / f) as u16;
|
||||
|
||||
let regs = Self::regs();
|
||||
@ -140,7 +131,13 @@ foreach_interrupt! {
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
|
||||
let f = frequency.0;
|
||||
#[cfg(not(stm32f334))]
|
||||
let timer_f = Self::frequency().0;
|
||||
#[cfg(stm32f334)]
|
||||
let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(
|
||||
Self::frequency()
|
||||
).0;
|
||||
|
||||
let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);
|
||||
let psc = if Self::regs().isr().read().dllrdy() {
|
||||
Prescaler::compute_min_high_res(psc_min)
|
||||
@ -148,8 +145,7 @@ foreach_interrupt! {
|
||||
Prescaler::compute_min_low_res(psc_min)
|
||||
};
|
||||
|
||||
let psc_val: u32 = psc.into();
|
||||
let timer_f = 32 * (timer_f / psc_val);
|
||||
let timer_f = 32 * (timer_f / psc as u32);
|
||||
let per: u16 = (timer_f / f) as u16;
|
||||
|
||||
let regs = Self::regs();
|
||||
@ -163,20 +159,17 @@ foreach_interrupt! {
|
||||
let regs = Self::regs();
|
||||
|
||||
let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into();
|
||||
let psc_val: u32 = channel_psc.into();
|
||||
|
||||
|
||||
// The dead-time base clock runs 4 times slower than the hrtim base clock
|
||||
// u9::MAX = 511
|
||||
let psc_min = (psc_val * dead_time as u32) / (4 * 511);
|
||||
let psc_min = (channel_psc as u32 * dead_time as u32) / (4 * 511);
|
||||
let psc = if Self::regs().isr().read().dllrdy() {
|
||||
Prescaler::compute_min_high_res(psc_min)
|
||||
} else {
|
||||
Prescaler::compute_min_low_res(psc_min)
|
||||
};
|
||||
|
||||
let dt_psc_val: u32 = psc.into();
|
||||
let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val);
|
||||
let dt_val = (psc as u32 * dead_time as u32) / (4 * channel_psc as u32);
|
||||
|
||||
regs.tim(channel).dt().modify(|w| {
|
||||
w.set_dtprsc(psc.into());
|
||||
|
@ -339,6 +339,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
|
||||
fn drop(&mut self) {
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
|
@ -838,6 +838,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> {
|
||||
fn drop(&mut self) {
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
mod eh02 {
|
||||
use super::*;
|
||||
|
||||
|
@ -6,7 +6,6 @@ use embassy_executor::*;
|
||||
|
||||
use crate::interrupt;
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::pac::EXTI;
|
||||
use crate::rcc::low_power_ready;
|
||||
use crate::time_driver::{get_driver, RtcDriver};
|
||||
|
||||
@ -99,8 +98,7 @@ impl Executor {
|
||||
crate::interrupt::typelevel::RTC_WKUP::unpend();
|
||||
unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() };
|
||||
|
||||
EXTI.rtsr(0).modify(|w| w.set_line(22, true));
|
||||
EXTI.imr(0).modify(|w| w.set_line(22, true));
|
||||
rtc.enable_wakeup_line();
|
||||
}
|
||||
|
||||
fn configure_pwr(&mut self) {
|
||||
|
@ -1,6 +1,34 @@
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default)]
|
||||
pub enum LseDrive {
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
Low = 0,
|
||||
MediumLow = 0x01,
|
||||
#[default]
|
||||
MediumHigh = 0x02,
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
High = 0x03,
|
||||
}
|
||||
|
||||
#[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))]
|
||||
impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv {
|
||||
fn from(value: LseDrive) -> Self {
|
||||
use crate::pac::rcc::vals::Lsedrv;
|
||||
|
||||
match value {
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
LseDrive::Low => Lsedrv::LOW,
|
||||
LseDrive::MediumLow => Lsedrv::MEDIUMLOW,
|
||||
LseDrive::MediumHigh => Lsedrv::MEDIUMHIGH,
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
LseDrive::High => Lsedrv::HIGH,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
pub enum RtcClockSource {
|
||||
/// 00: No clock
|
||||
NoClock = 0b00,
|
||||
@ -71,10 +99,55 @@ impl BackupDomain {
|
||||
rtc_v3u5
|
||||
))]
|
||||
#[allow(dead_code, unused_variables)]
|
||||
pub fn set_rtc_clock_source(clock_source: RtcClockSource) {
|
||||
pub fn configure_ls(clock_source: RtcClockSource, lse_drive: Option<LseDrive>) {
|
||||
match clock_source {
|
||||
RtcClockSource::LSI => {
|
||||
#[cfg(rtc_v3u5)]
|
||||
let csr = crate::pac::RCC.bdcr();
|
||||
|
||||
#[cfg(not(rtc_v3u5))]
|
||||
let csr = crate::pac::RCC.csr();
|
||||
|
||||
Self::modify(|_| {
|
||||
#[cfg(not(rtc_v2wb))]
|
||||
csr.modify(|w| w.set_lsion(true));
|
||||
|
||||
#[cfg(rtc_v2wb)]
|
||||
csr.modify(|w| w.set_lsi1on(true));
|
||||
});
|
||||
|
||||
#[cfg(not(rtc_v2wb))]
|
||||
while !csr.read().lsirdy() {}
|
||||
|
||||
#[cfg(rtc_v2wb)]
|
||||
while !csr.read().lsi1rdy() {}
|
||||
}
|
||||
RtcClockSource::LSE => {
|
||||
let lse_drive = lse_drive.unwrap_or_default();
|
||||
|
||||
Self::modify(|w| {
|
||||
#[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))]
|
||||
w.set_lsedrv(lse_drive.into());
|
||||
w.set_lseon(true);
|
||||
});
|
||||
|
||||
while !Self::read().lserdy() {}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
Self::configure_rtc(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, unused_variables)]
|
||||
pub fn configure_rtc(clock_source: RtcClockSource) {
|
||||
let clock_source = clock_source as u8;
|
||||
#[cfg(any(
|
||||
all(not(any(rtc_v3, rtc_v3u5)), not(rtc_v2wb)),
|
||||
not(any(rtc_v3, rtc_v3u5, 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);
|
||||
@ -87,13 +160,14 @@ 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_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))]
|
||||
#[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() {
|
||||
@ -102,47 +176,21 @@ impl BackupDomain {
|
||||
|
||||
Self::modify(|w| {
|
||||
// Reset
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
||||
#[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))]
|
||||
#[cfg(any(rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
w.set_lscosel(reg.lscosel());
|
||||
#[cfg(any(rtc_v2l4, rtc_v2wb))]
|
||||
#[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))]
|
||||
w.set_lsedrv(reg.lsedrv());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(rtc_v3, rtc_v3u5))]
|
||||
#[allow(dead_code)]
|
||||
pub fn enable_rtc() {
|
||||
let reg = Self::read();
|
||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||
|
||||
if !reg.rtcen() {
|
||||
Self::modify(|w| w.set_bdrst(true));
|
||||
|
||||
Self::modify(|w| {
|
||||
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());
|
||||
#[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());
|
||||
});
|
||||
|
@ -184,6 +184,6 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
ahb1: Hertz(hclk),
|
||||
adc: Hertz(adcclk),
|
||||
adc: Some(Hertz(adcclk)),
|
||||
});
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ use core::ops::{Div, Mul};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::bd::BackupDomain;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::rtc::RtcClockSource;
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
@ -288,6 +290,7 @@ pub struct Config {
|
||||
pub pll_mux: PLLSrc,
|
||||
pub pll: PLLConfig,
|
||||
pub mux: ClockSrc,
|
||||
pub rtc: Option<RtcClockSource>,
|
||||
pub voltage: VoltageScale,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
@ -304,6 +307,7 @@ impl Default for Config {
|
||||
pll: PLLConfig::default(),
|
||||
voltage: VoltageScale::Scale3,
|
||||
mux: ClockSrc::HSI,
|
||||
rtc: None,
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
@ -414,6 +418,13 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().modify(|w| w.set_hsion(false));
|
||||
}
|
||||
|
||||
RCC.apb1enr().modify(|w| w.set_pwren(true));
|
||||
PWR.cr().read();
|
||||
|
||||
config
|
||||
.rtc
|
||||
.map(|clock_source| BackupDomain::configure_ls(clock_source, None));
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk,
|
||||
ahb1: ahb_freq,
|
||||
|
@ -1,5 +1,7 @@
|
||||
#[cfg(rcc_f3)]
|
||||
use crate::pac::adccommon::vals::Ckmode;
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre};
|
||||
use crate::pac::rcc::vals::{Adcpres, Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
@ -10,6 +12,82 @@ pub const HSI_FREQ: Hertz = Hertz(8_000_000);
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(40_000);
|
||||
|
||||
impl From<AdcClockSource> for Adcpres {
|
||||
fn from(value: AdcClockSource) -> Self {
|
||||
match value {
|
||||
AdcClockSource::PllDiv1 => Adcpres::DIV1,
|
||||
AdcClockSource::PllDiv2 => Adcpres::DIV2,
|
||||
AdcClockSource::PllDiv4 => Adcpres::DIV4,
|
||||
AdcClockSource::PllDiv6 => Adcpres::DIV6,
|
||||
AdcClockSource::PllDiv8 => Adcpres::DIV8,
|
||||
AdcClockSource::PllDiv12 => Adcpres::DIV12,
|
||||
AdcClockSource::PllDiv16 => Adcpres::DIV16,
|
||||
AdcClockSource::PllDiv32 => Adcpres::DIV32,
|
||||
AdcClockSource::PllDiv64 => Adcpres::DIV64,
|
||||
AdcClockSource::PllDiv128 => Adcpres::DIV128,
|
||||
AdcClockSource::PllDiv256 => Adcpres::DIV256,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(rcc_f3)]
|
||||
impl From<AdcClockSource> for Ckmode {
|
||||
fn from(value: AdcClockSource) -> Self {
|
||||
match value {
|
||||
AdcClockSource::BusDiv1 => Ckmode::SYNCDIV1,
|
||||
AdcClockSource::BusDiv2 => Ckmode::SYNCDIV2,
|
||||
AdcClockSource::BusDiv4 => Ckmode::SYNCDIV4,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
PllDiv1 = 1,
|
||||
PllDiv2 = 2,
|
||||
PllDiv4 = 4,
|
||||
PllDiv6 = 6,
|
||||
PllDiv8 = 8,
|
||||
PllDiv12 = 12,
|
||||
PllDiv16 = 16,
|
||||
PllDiv32 = 32,
|
||||
PllDiv64 = 64,
|
||||
PllDiv128 = 128,
|
||||
PllDiv256 = 256,
|
||||
BusDiv1,
|
||||
BusDiv2,
|
||||
BusDiv4,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn is_bus(&self) -> bool {
|
||||
match self {
|
||||
Self::BusDiv1 => true,
|
||||
Self::BusDiv2 => true,
|
||||
Self::BusDiv4 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bus_div(&self) -> u32 {
|
||||
match self {
|
||||
Self::BusDiv1 => 1,
|
||||
Self::BusDiv2 => 2,
|
||||
Self::BusDiv4 => 4,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum HrtimClockSource {
|
||||
#[default]
|
||||
BusClk,
|
||||
PllClk,
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
#[non_exhaustive]
|
||||
#[derive(Default)]
|
||||
@ -36,9 +114,20 @@ pub struct Config {
|
||||
/// - The System clock frequency is either 48MHz or 72MHz
|
||||
/// - APB1 clock has a minimum frequency of 10MHz
|
||||
pub pll48: bool,
|
||||
#[cfg(rcc_f3)]
|
||||
/// ADC clock setup
|
||||
/// - For AHB, a psc of 4 or less must be used
|
||||
pub adc: Option<AdcClockSource>,
|
||||
#[cfg(rcc_f3)]
|
||||
/// ADC clock setup
|
||||
/// - For AHB, a psc of 4 or less must be used
|
||||
pub adc34: Option<AdcClockSource>,
|
||||
#[cfg(stm32f334)]
|
||||
pub hrtim: HrtimClockSource,
|
||||
}
|
||||
|
||||
// Information required to setup the PLL clock
|
||||
#[derive(Clone, Copy)]
|
||||
struct PllConfig {
|
||||
pll_src: Pllsrc,
|
||||
pll_mul: Pllmul,
|
||||
@ -170,6 +259,61 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
})
|
||||
});
|
||||
|
||||
#[cfg(rcc_f3)]
|
||||
let adc = config.adc.map(|adc| {
|
||||
if !adc.is_bus() {
|
||||
RCC.cfgr2().modify(|w| {
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
w.set_adc12pres(adc.into());
|
||||
|
||||
Hertz(sysclk / adc as u32)
|
||||
})
|
||||
} else {
|
||||
crate::pac::ADC_COMMON.ccr().modify(|w| {
|
||||
assert!(!(adc.bus_div() == 1 && hpre_bits != Hpre::DIV1));
|
||||
|
||||
w.set_ckmode(adc.into());
|
||||
|
||||
Hertz(sysclk / adc.bus_div() as u32)
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(rcc_f3)]
|
||||
let adc34 = config.adc.map(|adc| {
|
||||
if !adc.is_bus() {
|
||||
RCC.cfgr2().modify(|w| {
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
w.set_adc12pres(adc.into());
|
||||
|
||||
Hertz(sysclk / adc as u32)
|
||||
})
|
||||
} else {
|
||||
// TODO: need to use only if adc32_common is present
|
||||
|
||||
todo!()
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(stm32f334)]
|
||||
let hrtim = match config.hrtim {
|
||||
// Must be configured after the bus is ready, otherwise it won't work
|
||||
HrtimClockSource::BusClk => None,
|
||||
HrtimClockSource::PllClk => {
|
||||
use crate::pac::rcc::vals::Timsw;
|
||||
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
assert!((pclk2 == sysclk) || (pclk2 * 2 == sysclk));
|
||||
|
||||
RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL));
|
||||
|
||||
Some(Hertz(sysclk * 2))
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
@ -177,6 +321,12 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
ahb1: Hertz(hclk),
|
||||
#[cfg(rcc_f3)]
|
||||
adc: adc,
|
||||
#[cfg(rcc_f3)]
|
||||
adc34: adc34,
|
||||
#[cfg(stm32f334)]
|
||||
hrtim: hrtim,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -461,17 +461,9 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
})
|
||||
});
|
||||
|
||||
match config.rtc {
|
||||
Some(RtcClockSource::LSI) => {
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
config.rtc.map(|clock_source| {
|
||||
BackupDomain::set_rtc_clock_source(clock_source);
|
||||
});
|
||||
config
|
||||
.rtc
|
||||
.map(|clock_source| BackupDomain::configure_ls(clock_source, None));
|
||||
|
||||
let rtc = match config.rtc {
|
||||
Some(RtcClockSource::LSI) => Some(LSI_FREQ),
|
||||
@ -499,6 +491,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
pllsai: None,
|
||||
|
||||
rtc: rtc,
|
||||
rtc_hse: None,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use stm32_metapac::flash::vals::Latency;
|
||||
use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw};
|
||||
use stm32_metapac::rcc::vals::{Adcsel, Hpre, Pllsrc, Ppre, Sw};
|
||||
use stm32_metapac::FLASH;
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
@ -14,6 +14,29 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
NoClk,
|
||||
SysClk,
|
||||
PllP,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn adcsel(&self) -> Adcsel {
|
||||
match self {
|
||||
AdcClockSource::NoClk => Adcsel::NOCLK,
|
||||
AdcClockSource::SysClk => Adcsel::SYSCLK,
|
||||
AdcClockSource::PllP => Adcsel::PLLP,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AdcClockSource {
|
||||
fn default() -> Self {
|
||||
Self::NoClk
|
||||
}
|
||||
}
|
||||
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
@ -327,6 +350,8 @@ pub struct Config {
|
||||
pub pll: Option<Pll>,
|
||||
/// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals.
|
||||
pub clock_48mhz_src: Option<Clock48MhzSrc>,
|
||||
pub adc12_clock_source: AdcClockSource,
|
||||
pub adc345_clock_source: AdcClockSource,
|
||||
}
|
||||
|
||||
/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
|
||||
@ -346,6 +371,8 @@ impl Default for Config {
|
||||
low_power_run: false,
|
||||
pll: None,
|
||||
clock_48mhz_src: None,
|
||||
adc12_clock_source: Default::default(),
|
||||
adc345_clock_source: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -549,6 +576,29 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.ccipr().modify(|w| w.set_clk48sel(source));
|
||||
}
|
||||
|
||||
RCC.ccipr()
|
||||
.modify(|w| w.set_adc12sel(config.adc12_clock_source.adcsel()));
|
||||
RCC.ccipr()
|
||||
.modify(|w| w.set_adc345sel(config.adc345_clock_source.adcsel()));
|
||||
|
||||
let adc12_ck = match config.adc12_clock_source {
|
||||
AdcClockSource::NoClk => None,
|
||||
AdcClockSource::PllP => match &pll_freq {
|
||||
Some(pll) => pll.pll_p,
|
||||
None => None,
|
||||
},
|
||||
AdcClockSource::SysClk => Some(Hertz(sys_clk)),
|
||||
};
|
||||
|
||||
let adc345_ck = match config.adc345_clock_source {
|
||||
AdcClockSource::NoClk => None,
|
||||
AdcClockSource::PllP => match &pll_freq {
|
||||
Some(pll) => pll.pll_p,
|
||||
None => None,
|
||||
},
|
||||
AdcClockSource::SysClk => Some(Hertz(sys_clk)),
|
||||
};
|
||||
|
||||
if config.low_power_run {
|
||||
assert!(sys_clk <= 2_000_000);
|
||||
PWR.cr1().modify(|w| w.set_lpr(true));
|
||||
@ -562,5 +612,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
apb1_tim: Hertz(apb1_tim_freq),
|
||||
apb2: Hertz(apb2_freq),
|
||||
apb2_tim: Hertz(apb2_tim_freq),
|
||||
adc: adc12_ck,
|
||||
adc34: adc345_ck,
|
||||
});
|
||||
}
|
||||
|
@ -2,13 +2,13 @@ use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
use stm32_metapac::rcc::regs::Cfgr;
|
||||
use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel};
|
||||
use stm32_metapac::rcc::vals::{Mcopre, Mcosel};
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
@ -407,36 +407,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
RCC.apb1enr1().modify(|w| w.set_pwren(true));
|
||||
|
||||
match config.rtc_mux {
|
||||
RtcClockSource::LSE => {
|
||||
// 1. Unlock the backup domain
|
||||
PWR.cr1().modify(|w| w.set_dbp(true));
|
||||
|
||||
// 2. Setup the LSE
|
||||
RCC.bdcr().modify(|w| {
|
||||
// Enable LSE
|
||||
w.set_lseon(true);
|
||||
// Max drive strength
|
||||
// TODO: should probably be settable
|
||||
w.set_lsedrv(Lsedrv::HIGH);
|
||||
});
|
||||
|
||||
// Wait until LSE is running
|
||||
while !RCC.bdcr().read().lserdy() {}
|
||||
|
||||
BackupDomain::set_rtc_clock_source(RtcClockSource::LSE);
|
||||
}
|
||||
RtcClockSource::LSI => {
|
||||
// Turn on the internal 32 kHz LSI oscillator
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
|
||||
// Wait until LSI is running
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
|
||||
BackupDomain::set_rtc_clock_source(RtcClockSource::LSI);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
BackupDomain::configure_ls(config.rtc_mux, None);
|
||||
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
|
@ -71,15 +71,22 @@ pub struct Clocks {
|
||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||
pub pllsai: Option<Hertz>,
|
||||
|
||||
#[cfg(stm32f1)]
|
||||
pub adc: Hertz,
|
||||
|
||||
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))]
|
||||
#[cfg(any(rcc_f1, rcc_f100, rcc_f1cl, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3, rcc_g4))]
|
||||
pub adc: Option<Hertz>,
|
||||
|
||||
#[cfg(any(rcc_f3, rcc_g4))]
|
||||
pub adc34: Option<Hertz>,
|
||||
|
||||
#[cfg(stm32f334)]
|
||||
pub hrtim: Option<Hertz>,
|
||||
|
||||
#[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>,
|
||||
|
||||
#[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")]
|
||||
|
@ -271,11 +271,11 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks {
|
||||
apb1_tim: apb1_tim_clk,
|
||||
apb2_tim: apb2_tim_clk,
|
||||
rtc: rtc_clk,
|
||||
rtc_hse: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn configure_clocks(config: &Config) {
|
||||
let pwr = crate::pac::PWR;
|
||||
let rcc = crate::pac::RCC;
|
||||
|
||||
let needs_hsi = if let Some(pll_mux) = &config.mux {
|
||||
@ -292,29 +292,11 @@ pub(crate) fn configure_clocks(config: &Config) {
|
||||
while !rcc.cr().read().hsirdy() {}
|
||||
}
|
||||
|
||||
let needs_lsi = if let Some(rtc_mux) = &config.rtc {
|
||||
*rtc_mux == RtcClockSource::LSI
|
||||
} else {
|
||||
false
|
||||
};
|
||||
rcc.cfgr().modify(|w| w.set_stopwuck(true));
|
||||
|
||||
if needs_lsi {
|
||||
rcc.csr().modify(|w| w.set_lsi1on(true));
|
||||
|
||||
while !rcc.csr().read().lsi1rdy() {}
|
||||
}
|
||||
|
||||
match &config.lse {
|
||||
Some(_) => {
|
||||
rcc.cfgr().modify(|w| w.set_stopwuck(true));
|
||||
|
||||
pwr.cr1().modify(|w| w.set_dbp(true));
|
||||
pwr.cr1().modify(|w| w.set_dbp(true));
|
||||
|
||||
rcc.bdcr().modify(|w| w.set_lseon(true));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
config
|
||||
.rtc
|
||||
.map(|clock_source| BackupDomain::configure_ls(clock_source, None));
|
||||
|
||||
match &config.hse {
|
||||
Some(hse) => {
|
||||
@ -374,8 +356,4 @@ pub(crate) fn configure_clocks(config: &Config) {
|
||||
w.set_c2hpre(config.ahb2_pre.into());
|
||||
w.set_shdhpre(config.ahb3_pre.into());
|
||||
});
|
||||
|
||||
config
|
||||
.rtc
|
||||
.map(|clock_source| BackupDomain::set_rtc_clock_source(clock_source));
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler, VoltageScale};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::pac::rcc::vals::Adcsel;
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
@ -106,6 +107,29 @@ impl Into<u8> for MSIRange {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
HSI16,
|
||||
PLLPCLK,
|
||||
SYSCLK,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn adcsel(&self) -> Adcsel {
|
||||
match self {
|
||||
AdcClockSource::HSI16 => Adcsel::HSI16,
|
||||
AdcClockSource::PLLPCLK => Adcsel::PLLPCLK,
|
||||
AdcClockSource::SYSCLK => Adcsel::SYSCLK,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AdcClockSource {
|
||||
fn default() -> Self {
|
||||
Self::HSI16
|
||||
}
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
@ -113,9 +137,8 @@ pub struct Config {
|
||||
pub shd_ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub enable_lsi: bool,
|
||||
pub enable_rtc_apb: bool,
|
||||
pub rtc_mux: RtcClockSource,
|
||||
pub adc_clock_source: AdcClockSource,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -127,9 +150,8 @@ impl Default for Config {
|
||||
shd_ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
enable_lsi: false,
|
||||
enable_rtc_apb: false,
|
||||
rtc_mux: RtcClockSource::LSI,
|
||||
adc_clock_source: AdcClockSource::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,36 +230,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
while FLASH.acr().read().latency() != ws {}
|
||||
|
||||
match config.rtc_mux {
|
||||
RtcClockSource::LSE => {
|
||||
// 1. Unlock the backup domain
|
||||
PWR.cr1().modify(|w| w.set_dbp(true));
|
||||
|
||||
// 2. Setup the LSE
|
||||
RCC.bdcr().modify(|w| {
|
||||
// Enable LSE
|
||||
w.set_lseon(true);
|
||||
// Max drive strength
|
||||
// TODO: should probably be settable
|
||||
w.set_lsedrv(Lsedrv::High as u8); //---// PAM - should not be commented
|
||||
});
|
||||
|
||||
// Wait until LSE is running
|
||||
while !RCC.bdcr().read().lserdy() {}
|
||||
|
||||
BackupDomain::set_rtc_clock_source(RtcClockSource::LSE);
|
||||
}
|
||||
RtcClockSource::LSI => {
|
||||
// Turn on the internal 32 kHz LSI oscillator
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
|
||||
// Wait until LSI is running
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
|
||||
BackupDomain::set_rtc_clock_source(RtcClockSource::LSI);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// Enables the LSI if configured
|
||||
BackupDomain::configure_ls(config.rtc_mux, None);
|
||||
|
||||
match config.mux {
|
||||
ClockSrc::HSI16 => {
|
||||
@ -272,14 +266,6 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
}
|
||||
|
||||
if config.enable_rtc_apb {
|
||||
// enable peripheral clock for communication
|
||||
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
|
||||
|
||||
// read to allow the pwr clock to enable
|
||||
crate::pac::PWR.cr1().read();
|
||||
}
|
||||
|
||||
RCC.extcfgr().modify(|w| {
|
||||
if config.shd_ahb_pre == AHBPrescaler::NotDivided {
|
||||
w.set_shdhpre(0);
|
||||
@ -299,15 +285,10 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
// TODO: switch voltage range
|
||||
// ADC clock MUX
|
||||
RCC.ccipr().modify(|w| w.set_adcsel(config.adc_clock_source.adcsel()));
|
||||
|
||||
if config.enable_lsi {
|
||||
let csr = RCC.csr().read();
|
||||
if !csr.lsion() {
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
}
|
||||
}
|
||||
// TODO: switch voltage range
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
|
@ -119,7 +119,31 @@ impl<'d, T: Instance> Rng<'d, T> {
|
||||
|
||||
pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||
for chunk in dest.chunks_mut(4) {
|
||||
let bits = T::regs().sr().read();
|
||||
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();
|
||||
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() {
|
||||
// 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
|
||||
@ -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()) {
|
||||
*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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ use embassy_sync::blocking_mutex::Mutex;
|
||||
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
|
||||
#[cfg_attr(any(rtc_v1), path = "v1.rs")]
|
||||
@ -47,28 +48,15 @@ struct RtcInstant {
|
||||
subsecond: u16,
|
||||
}
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
impl RtcInstant {
|
||||
pub fn now() -> Self {
|
||||
let tr = RTC::regs().tr().read();
|
||||
let tr2 = RTC::regs().tr().read();
|
||||
let ssr = RTC::regs().ssr().read().ss();
|
||||
let ssr2 = RTC::regs().ssr().read().ss();
|
||||
|
||||
let st = bcd2_to_byte((tr.st(), tr.su()));
|
||||
let st2 = bcd2_to_byte((tr2.st(), tr2.su()));
|
||||
|
||||
assert!(st == st2);
|
||||
assert!(ssr == ssr2);
|
||||
|
||||
let _ = RTC::regs().dr().read();
|
||||
|
||||
let subsecond = ssr;
|
||||
let second = st;
|
||||
|
||||
// trace!("rtc: instant now: st, ssr: {}, {}", st, ssr);
|
||||
|
||||
Self { second, subsecond }
|
||||
#[cfg(all(feature = "low-power", feature = "defmt"))]
|
||||
impl defmt::Format for RtcInstant {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(
|
||||
fmt,
|
||||
"{}:{}",
|
||||
self.second,
|
||||
RTC::regs().prer().read().prediv_s() - self.subsecond,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,66 +73,35 @@ impl core::ops::Sub for RtcInstant {
|
||||
self.second
|
||||
};
|
||||
|
||||
// TODO: read prescaler
|
||||
let psc = RTC::regs().prer().read().prediv_s() as u32;
|
||||
|
||||
let self_ticks = second as u32 * 256 + (255 - self.subsecond as u32);
|
||||
let other_ticks = rhs.second as u32 * 256 + (255 - rhs.subsecond 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;
|
||||
|
||||
// trace!(
|
||||
// "rtc: instant sub: self, other, rtc ticks: {}, {}, {}",
|
||||
// self_ticks,
|
||||
// other_ticks,
|
||||
// rtc_ticks
|
||||
// );
|
||||
|
||||
Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / 256u32) as u64)
|
||||
Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64)
|
||||
}
|
||||
}
|
||||
|
||||
/// RTC Abstraction
|
||||
pub struct Rtc {
|
||||
rtc_config: RtcConfig,
|
||||
#[cfg(feature = "low-power")]
|
||||
stop_time: Mutex<CriticalSectionRawMutex, Cell<Option<RtcInstant>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct RtcConfig {
|
||||
/// Asynchronous prescaler factor
|
||||
/// This is the asynchronous division factor:
|
||||
/// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1)
|
||||
/// ck_apre drives the subsecond register
|
||||
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,
|
||||
/// The subsecond counter frequency; default is 256
|
||||
///
|
||||
/// A high counter frequency may impact stop power consumption
|
||||
pub frequency: Hertz,
|
||||
}
|
||||
|
||||
impl Default for RtcConfig {
|
||||
/// LSI with prescalers assuming 32.768 kHz.
|
||||
/// Raw sub-seconds in 1/256.
|
||||
fn default() -> Self {
|
||||
RtcConfig {
|
||||
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
|
||||
RtcConfig { frequency: Hertz(256) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,23 +124,37 @@ impl Default for RtcCalibrationCyclePeriod {
|
||||
|
||||
impl Rtc {
|
||||
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();
|
||||
BackupDomain::enable_rtc();
|
||||
|
||||
#[cfg(not(feature = "low-power"))]
|
||||
let mut rtc_struct = Self { rtc_config };
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
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)),
|
||||
};
|
||||
|
||||
BackupDomain::enable_rtc();
|
||||
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
|
||||
let freqs = unsafe { get_freqs() };
|
||||
|
||||
rtc_struct.configure(rtc_config);
|
||||
rtc_struct.rtc_config = rtc_config;
|
||||
// Load the clock frequency from the rcc mod, if supported
|
||||
#[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.
|
||||
@ -198,6 +169,20 @@ impl Rtc {
|
||||
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.
|
||||
///
|
||||
/// # Errors
|
||||
@ -234,10 +219,6 @@ impl Rtc {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_config(&self) -> RtcConfig {
|
||||
self.rtc_config
|
||||
}
|
||||
|
||||
pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT;
|
||||
|
||||
/// Read content of the backup register.
|
||||
@ -287,7 +268,7 @@ pub(crate) mod sealed {
|
||||
crate::pac::RTC
|
||||
}
|
||||
|
||||
fn enable_peripheral_clk() {}
|
||||
fn enable_peripheral_clk();
|
||||
|
||||
/// Read content of the backup register.
|
||||
///
|
||||
|
@ -1,19 +1,18 @@
|
||||
use stm32_metapac::rtc::vals::{Init, Osel, Pol};
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
use super::RtcInstant;
|
||||
use super::{sealed, RtcConfig};
|
||||
use super::sealed;
|
||||
use crate::pac::rtc::Rtc;
|
||||
use crate::peripherals::RTC;
|
||||
use crate::rtc::sealed::Instance;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) enum WakeupPrescaler {
|
||||
Div2,
|
||||
Div4,
|
||||
Div8,
|
||||
Div16,
|
||||
Div2 = 2,
|
||||
Div4 = 4,
|
||||
Div8 = 8,
|
||||
Div16 = 16,
|
||||
}
|
||||
|
||||
#[cfg(any(stm32wb, stm32f4))]
|
||||
@ -45,17 +44,6 @@ impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -66,7 +54,7 @@ impl WakeupPrescaler {
|
||||
WakeupPrescaler::Div16,
|
||||
]
|
||||
.iter()
|
||||
.skip_while(|psc| <WakeupPrescaler as Into<u32>>::into(**psc) <= val)
|
||||
.skip_while(|psc| **psc as u32 <= val)
|
||||
.next()
|
||||
.unwrap_or(&WakeupPrescaler::Div16)
|
||||
}
|
||||
@ -86,17 +74,14 @@ impl super::Rtc {
|
||||
let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ;
|
||||
let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32);
|
||||
|
||||
// adjust the rtc ticks to the prescaler
|
||||
let rtc_ticks = rtc_ticks / (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64);
|
||||
// adjust the rtc ticks to the prescaler and subtract one rtc tick
|
||||
let rtc_ticks = rtc_ticks / prescaler as u64;
|
||||
let rtc_ticks = if rtc_ticks >= u16::MAX as u64 {
|
||||
u16::MAX - 1
|
||||
} else {
|
||||
rtc_ticks as u16
|
||||
};
|
||||
|
||||
let duration = Duration::from_ticks(
|
||||
rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz,
|
||||
);
|
||||
}
|
||||
.saturating_sub(1);
|
||||
|
||||
self.write(false, |regs| {
|
||||
regs.cr().modify(|w| w.set_wute(false));
|
||||
@ -104,13 +89,28 @@ impl super::Rtc {
|
||||
while !regs.isr().read().wutwf() {}
|
||||
|
||||
regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
|
||||
regs.wutr().write(|w| w.set_wut(rtc_ticks));
|
||||
regs.cr().modify(|w| w.set_wute(true));
|
||||
regs.cr().modify(|w| w.set_wutie(true));
|
||||
});
|
||||
|
||||
trace!("rtc: start wakeup alarm for {} ms", duration.as_millis());
|
||||
trace!(
|
||||
"rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
|
||||
Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
|
||||
prescaler as u32,
|
||||
rtc_ticks,
|
||||
self.instant(),
|
||||
);
|
||||
|
||||
critical_section::with(|cs| assert!(self.stop_time.borrow(cs).replace(Some(RtcInstant::now())).is_none()))
|
||||
critical_section::with(|cs| assert!(self.stop_time.borrow(cs).replace(Some(self.instant())).is_none()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
pub(crate) fn enable_wakeup_line(&self) {
|
||||
use crate::pac::EXTI;
|
||||
|
||||
EXTI.rtsr(0).modify(|w| w.set_line(22, true));
|
||||
EXTI.imr(0).modify(|w| w.set_line(22, true));
|
||||
}
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
@ -119,7 +119,7 @@ impl super::Rtc {
|
||||
pub(crate) fn stop_wakeup_alarm(&self) -> Option<embassy_time::Duration> {
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
|
||||
trace!("rtc: stop wakeup alarm...");
|
||||
trace!("rtc: stop wakeup alarm at {}", self.instant());
|
||||
|
||||
self.write(false, |regs| {
|
||||
regs.cr().modify(|w| w.set_wutie(false));
|
||||
@ -132,7 +132,7 @@ impl super::Rtc {
|
||||
|
||||
critical_section::with(|cs| {
|
||||
if let Some(stop_time) = self.stop_time.borrow(cs).take() {
|
||||
Some(RtcInstant::now() - stop_time)
|
||||
Some(self.instant() - stop_time)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -141,7 +141,7 @@ impl super::Rtc {
|
||||
|
||||
/// Applies the RTC config
|
||||
/// 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| {
|
||||
rtc.cr().modify(|w| {
|
||||
#[cfg(rtc_v2f2)]
|
||||
@ -153,8 +153,8 @@ impl super::Rtc {
|
||||
});
|
||||
|
||||
rtc.prer().modify(|w| {
|
||||
w.set_prediv_s(rtc_config.sync_prescaler);
|
||||
w.set_prediv_a(rtc_config.async_prescaler);
|
||||
w.set_prediv_s(sync_psc);
|
||||
w.set_prediv_a(async_psc);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -270,9 +270,18 @@ impl sealed::Instance for crate::peripherals::RTC {
|
||||
}
|
||||
#[cfg(any(rtc_v2f2))]
|
||||
{
|
||||
// enable peripheral clock for communication
|
||||
crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true));
|
||||
|
||||
// read to allow the pwr clock to enable
|
||||
crate::pac::PWR.cr().read();
|
||||
}
|
||||
|
||||
#[cfg(any(rtc_v2f0))]
|
||||
{
|
||||
// enable peripheral clock for communication
|
||||
crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true));
|
||||
}
|
||||
}
|
||||
|
||||
fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType};
|
||||
|
||||
use super::{sealed, RtcCalibrationCyclePeriod, RtcConfig};
|
||||
use super::{sealed, RtcCalibrationCyclePeriod};
|
||||
use crate::pac::rtc::Rtc;
|
||||
use crate::peripherals::RTC;
|
||||
use crate::rtc::sealed::Instance;
|
||||
@ -8,7 +8,7 @@ use crate::rtc::sealed::Instance;
|
||||
impl super::Rtc {
|
||||
/// Applies the RTC config
|
||||
/// 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| {
|
||||
rtc.cr().modify(|w| {
|
||||
w.set_fmt(Fmt::TWENTYFOURHOUR);
|
||||
@ -17,8 +17,8 @@ impl super::Rtc {
|
||||
});
|
||||
|
||||
rtc.prer().modify(|w| {
|
||||
w.set_prediv_s(rtc_config.sync_prescaler);
|
||||
w.set_prediv_a(rtc_config.async_prescaler);
|
||||
w.set_prediv_s(sync_psc);
|
||||
w.set_prediv_a(async_psc);
|
||||
});
|
||||
|
||||
// TODO: configuration for output pins
|
||||
@ -128,6 +128,23 @@ impl super::Rtc {
|
||||
impl sealed::Instance for crate::peripherals::RTC {
|
||||
const BACKUP_REGISTER_COUNT: usize = 32;
|
||||
|
||||
fn enable_peripheral_clk() {
|
||||
#[cfg(any(rcc_wle, rcc_wl5, rcc_g4))]
|
||||
{
|
||||
// enable peripheral clock for communication
|
||||
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
|
||||
}
|
||||
|
||||
#[cfg(rcc_g0)]
|
||||
{
|
||||
// enable peripheral clock for communication
|
||||
crate::pac::RCC.apbenr1().modify(|w| w.set_rtcapben(true));
|
||||
}
|
||||
|
||||
// read to allow the pwr clock to enable
|
||||
crate::pac::PWR.cr1().read();
|
||||
}
|
||||
|
||||
fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
|
||||
#[allow(clippy::if_same_then_else)]
|
||||
if register < Self::BACKUP_REGISTER_COUNT {
|
||||
|
@ -33,6 +33,8 @@ impl<T: Instance> InterruptHandler<T> {
|
||||
w.set_dtimeoutie(enable);
|
||||
w.set_dataendie(enable);
|
||||
|
||||
#[cfg(sdmmc_v1)]
|
||||
w.set_stbiterre(enable);
|
||||
#[cfg(sdmmc_v2)]
|
||||
w.set_dabortie(enable);
|
||||
});
|
||||
@ -102,6 +104,8 @@ pub enum Error {
|
||||
BadClock,
|
||||
SignalingSwitchFailed,
|
||||
PeripheralBusy,
|
||||
#[cfg(sdmmc_v1)]
|
||||
StBitErr,
|
||||
}
|
||||
|
||||
/// A SD command
|
||||
@ -707,9 +711,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
||||
|
||||
if status.dcrcfail() {
|
||||
return Poll::Ready(Err(Error::Crc));
|
||||
} else if status.dtimeout() {
|
||||
}
|
||||
if status.dtimeout() {
|
||||
return Poll::Ready(Err(Error::Timeout));
|
||||
} else if status.dataend() {
|
||||
}
|
||||
#[cfg(sdmmc_v1)]
|
||||
if status.stbiterr() {
|
||||
return Poll::Ready(Err(Error::StBitErr));
|
||||
}
|
||||
if status.dataend() {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
Poll::Pending
|
||||
@ -782,9 +792,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
||||
|
||||
if status.dcrcfail() {
|
||||
return Poll::Ready(Err(Error::Crc));
|
||||
} else if status.dtimeout() {
|
||||
}
|
||||
if status.dtimeout() {
|
||||
return Poll::Ready(Err(Error::Timeout));
|
||||
} else if status.dataend() {
|
||||
}
|
||||
#[cfg(sdmmc_v1)]
|
||||
if status.stbiterr() {
|
||||
return Poll::Ready(Err(Error::StBitErr));
|
||||
}
|
||||
if status.dataend() {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
Poll::Pending
|
||||
@ -836,6 +852,8 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
||||
w.set_dataendc(true);
|
||||
w.set_dbckendc(true);
|
||||
w.set_sdioitc(true);
|
||||
#[cfg(sdmmc_v1)]
|
||||
w.set_stbiterrc(true);
|
||||
|
||||
#[cfg(sdmmc_v2)]
|
||||
{
|
||||
@ -873,9 +891,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
||||
|
||||
if status.dcrcfail() {
|
||||
return Poll::Ready(Err(Error::Crc));
|
||||
} else if status.dtimeout() {
|
||||
}
|
||||
if status.dtimeout() {
|
||||
return Poll::Ready(Err(Error::Timeout));
|
||||
} else if status.dataend() {
|
||||
}
|
||||
#[cfg(sdmmc_v1)]
|
||||
if status.stbiterr() {
|
||||
return Poll::Ready(Err(Error::StBitErr));
|
||||
}
|
||||
if status.dataend() {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
Poll::Pending
|
||||
@ -1156,9 +1180,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
||||
|
||||
if status.dcrcfail() {
|
||||
return Poll::Ready(Err(Error::Crc));
|
||||
} else if status.dtimeout() {
|
||||
}
|
||||
if status.dtimeout() {
|
||||
return Poll::Ready(Err(Error::Timeout));
|
||||
} else if status.dataend() {
|
||||
}
|
||||
#[cfg(sdmmc_v1)]
|
||||
if status.stbiterr() {
|
||||
return Poll::Ready(Err(Error::StBitErr));
|
||||
}
|
||||
if status.dataend() {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
Poll::Pending
|
||||
@ -1207,9 +1237,15 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
|
||||
|
||||
if status.dcrcfail() {
|
||||
return Poll::Ready(Err(Error::Crc));
|
||||
} else if status.dtimeout() {
|
||||
}
|
||||
if status.dtimeout() {
|
||||
return Poll::Ready(Err(Error::Timeout));
|
||||
} else if status.dataend() {
|
||||
}
|
||||
#[cfg(sdmmc_v1)]
|
||||
if status.stbiterr() {
|
||||
return Poll::Ready(Err(Error::StBitErr));
|
||||
}
|
||||
if status.dataend() {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
Poll::Pending
|
||||
|
@ -646,6 +646,8 @@ impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> {
|
||||
self.sck.as_ref().map(|x| x.set_as_disconnected());
|
||||
self.mosi.as_ref().map(|x| x.set_as_disconnected());
|
||||
self.miso.as_ref().map(|x| x.set_as_disconnected());
|
||||
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod complementary_pwm;
|
||||
pub mod qei;
|
||||
pub mod simple_pwm;
|
||||
|
||||
use stm32_metapac::timer::vals;
|
||||
@ -211,6 +212,7 @@ macro_rules! impl_basic_16bit_timer {
|
||||
use core::convert::TryInto;
|
||||
let f = frequency.0;
|
||||
let timer_f = Self::frequency().0;
|
||||
assert!(f > 0);
|
||||
let pclk_ticks_per_timer_period = timer_f / f;
|
||||
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());
|
||||
@ -255,6 +257,7 @@ macro_rules! impl_32bit_timer {
|
||||
fn set_frequency(&mut self, frequency: Hertz) {
|
||||
use core::convert::TryInto;
|
||||
let f = frequency.0;
|
||||
assert!(f > 0);
|
||||
let timer_f = Self::frequency().0;
|
||||
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());
|
||||
|
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 Qei<'d, T> {
|
||||
_inner: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: CaptureCompare16bitInstance> Qei<'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()
|
||||
}
|
||||
}
|
@ -124,6 +124,8 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
||||
rx_buffer: &'d mut [u8],
|
||||
config: Config,
|
||||
) -> BufferedUart<'d, T> {
|
||||
// UartRx and UartTx have one refcount ea.
|
||||
T::enable();
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
@ -143,6 +145,8 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
||||
) -> BufferedUart<'d, T> {
|
||||
into_ref!(cts, rts);
|
||||
|
||||
// UartRx and UartTx have one refcount ea.
|
||||
T::enable();
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
@ -169,6 +173,8 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
|
||||
) -> BufferedUart<'d, T> {
|
||||
into_ref!(de);
|
||||
|
||||
// UartRx and UartTx have one refcount ea.
|
||||
T::enable();
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
@ -382,6 +388,8 @@ impl<'d, T: BasicInstance> Drop for BufferedUartRx<'d, T> {
|
||||
T::Interrupt::disable();
|
||||
}
|
||||
}
|
||||
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
@ -397,6 +405,8 @@ impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> {
|
||||
T::Interrupt::disable();
|
||||
}
|
||||
}
|
||||
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -618,6 +618,18 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance, TxDma> Drop for UartTx<'d, T, TxDma> {
|
||||
fn drop(&mut self) {
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance, TxDma> Drop for UartRx<'d, T, TxDma> {
|
||||
fn drop(&mut self) {
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
@ -628,6 +640,8 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||
rx_dma: impl Peripheral<P = RxDma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
// UartRx and UartTx have one refcount ea.
|
||||
T::enable();
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
@ -647,6 +661,8 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||
) -> Self {
|
||||
into_ref!(cts, rts);
|
||||
|
||||
// UartRx and UartTx have one refcount ea.
|
||||
T::enable();
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
@ -672,6 +688,8 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
||||
) -> Self {
|
||||
into_ref!(de);
|
||||
|
||||
// UartRx and UartTx have one refcount ea.
|
||||
T::enable();
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use core::future::poll_fn;
|
||||
use core::mem;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
@ -24,12 +25,16 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> UartRx<'d, T, RxDma> {
|
||||
let request = self.rx_dma.request();
|
||||
let opts = Default::default();
|
||||
|
||||
let ring_buf = unsafe { ReadableRingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) };
|
||||
// Safety: we forget the struct before this function returns.
|
||||
let rx_dma = unsafe { self.rx_dma.clone_unchecked() };
|
||||
let _peri = unsafe { self._peri.clone_unchecked() };
|
||||
|
||||
RingBufferedUartRx {
|
||||
_peri: self._peri,
|
||||
ring_buf,
|
||||
}
|
||||
let ring_buf = unsafe { ReadableRingBuffer::new_read(rx_dma, request, rdr(T::regs()), dma_buf, opts) };
|
||||
|
||||
// Don't disable the clock
|
||||
mem::forget(self);
|
||||
|
||||
RingBufferedUartRx { _peri, ring_buf }
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,6 +191,8 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
|
||||
impl<T: BasicInstance, RxDma: super::RxDma<T>> Drop for RingBufferedUartRx<'_, T, RxDma> {
|
||||
fn drop(&mut self) {
|
||||
self.teardown_uart();
|
||||
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
/// Return an error result if the Sr register has errors
|
||||
|
@ -471,7 +471,7 @@ where
|
||||
}
|
||||
|
||||
fn lock<R>(&self, f: impl FnOnce(&mut ChannelState<T, N>) -> R) -> R {
|
||||
self.inner.lock(|rc| f(&mut *rc.borrow_mut()))
|
||||
self.inner.lock(|rc| f(&mut *unwrap!(rc.try_borrow_mut())))
|
||||
}
|
||||
|
||||
fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
@ -17,3 +17,4 @@ pub mod pipe;
|
||||
pub mod pubsub;
|
||||
pub mod signal;
|
||||
pub mod waitqueue;
|
||||
pub mod zerocopy_channel;
|
||||
|
@ -149,7 +149,7 @@ where
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.mutex.state.lock(|s| {
|
||||
let mut s = s.borrow_mut();
|
||||
let mut s = unwrap!(s.try_borrow_mut());
|
||||
s.locked = false;
|
||||
s.waker.wake();
|
||||
})
|
||||
|
@ -1,7 +1,8 @@
|
||||
//! Async byte stream pipe.
|
||||
|
||||
use core::cell::RefCell;
|
||||
use core::cell::{RefCell, UnsafeCell};
|
||||
use core::future::Future;
|
||||
use core::ops::Range;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
@ -82,17 +83,6 @@ where
|
||||
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>
|
||||
where
|
||||
M: RawMutex,
|
||||
@ -110,6 +100,29 @@ where
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> {
|
||||
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`].
|
||||
@ -138,6 +151,35 @@ where
|
||||
|
||||
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).
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
@ -162,67 +204,24 @@ struct PipeState<const N: usize> {
|
||||
write_waker: WakerRegistration,
|
||||
}
|
||||
|
||||
impl<const N: usize> PipeState<N> {
|
||||
const fn new() -> Self {
|
||||
PipeState {
|
||||
buffer: RingBuffer::new(),
|
||||
read_waker: WakerRegistration::new(),
|
||||
write_waker: WakerRegistration::new(),
|
||||
}
|
||||
#[repr(transparent)]
|
||||
struct Buffer<const N: usize>(UnsafeCell<[u8; N]>);
|
||||
|
||||
impl<const N: usize> Buffer<N> {
|
||||
unsafe fn get<'a>(&self, r: Range<usize>) -> &'a [u8] {
|
||||
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) {
|
||||
self.buffer.clear();
|
||||
self.write_waker.wake();
|
||||
}
|
||||
|
||||
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 fn get_mut<'a>(&self, r: Range<usize>) -> &'a mut [u8] {
|
||||
let p = self.0.get() as *mut u8;
|
||||
core::slice::from_raw_parts_mut(p.add(r.start), r.end - r.start)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
/// with backpressure.
|
||||
///
|
||||
@ -234,6 +233,7 @@ pub struct Pipe<M, const N: usize>
|
||||
where
|
||||
M: RawMutex,
|
||||
{
|
||||
buf: Buffer<N>,
|
||||
inner: Mutex<M, RefCell<PipeState<N>>>,
|
||||
}
|
||||
|
||||
@ -252,7 +252,12 @@ where
|
||||
/// ```
|
||||
pub const fn new() -> 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> {
|
||||
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> {
|
||||
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();
|
||||
}
|
||||
|
||||
let available = unsafe { self.buf.get_mut(s.buffer.push_buf()) };
|
||||
if available.is_empty() {
|
||||
if let Some(cx) = cx {
|
||||
s.write_waker.register(cx.waker());
|
||||
}
|
||||
return Err(TryWriteError::Full);
|
||||
}
|
||||
|
||||
let n = available.len().min(buf.len());
|
||||
available[..n].copy_from_slice(&buf[..n]);
|
||||
s.buffer.push(n);
|
||||
Ok(n)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a writer for this pipe.
|
||||
pub fn writer(&self) -> Writer<'_, M, N> {
|
||||
Writer { pipe: self }
|
||||
}
|
||||
|
||||
/// Get a reader for this pipe.
|
||||
pub fn reader(&self) -> Reader<'_, M, N> {
|
||||
Reader { pipe: self }
|
||||
/// 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.
|
||||
@ -312,7 +387,7 @@ where
|
||||
/// or return an error if the pipe is empty. See [`write`](Self::write) for a variant
|
||||
/// that waits instead of returning an error.
|
||||
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.
|
||||
@ -339,12 +414,17 @@ where
|
||||
/// or return an error if the pipe is empty. See [`read`](Self::read) for a variant
|
||||
/// that waits instead of returning an error.
|
||||
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.
|
||||
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)
|
||||
@ -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> {
|
||||
type Error = Infallible;
|
||||
}
|
||||
@ -457,43 +547,39 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
|
||||
|
||||
fn capacity<const N: usize>(c: &PipeState<N>) -> usize {
|
||||
N - c.buffer.len()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn writing_once() {
|
||||
let mut c = PipeState::<3>::new();
|
||||
let c = Pipe::<NoopRawMutex, 3>::new();
|
||||
assert!(c.try_write(&[1]).is_ok());
|
||||
assert_eq!(capacity(&c), 2);
|
||||
assert_eq!(c.free_capacity(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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(&[43]), Ok(1));
|
||||
assert_eq!(c.try_write(&[44]), Ok(1));
|
||||
assert_eq!(c.try_write(&[45]), Err(TryWriteError::Full));
|
||||
assert_eq!(capacity(&c), 0);
|
||||
assert_eq!(c.free_capacity(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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());
|
||||
let mut buf = [0; 16];
|
||||
assert_eq!(c.try_read(&mut buf), Ok(1));
|
||||
assert_eq!(buf[0], 42);
|
||||
assert_eq!(capacity(&c), 3);
|
||||
assert_eq!(c.free_capacity(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receiving_when_empty() {
|
||||
let mut c = PipeState::<3>::new();
|
||||
let c = Pipe::<NoopRawMutex, 3>::new();
|
||||
let mut buf = [0; 16];
|
||||
assert_eq!(c.try_read(&mut buf), Err(TryReadError::Empty));
|
||||
assert_eq!(capacity(&c), 3);
|
||||
assert_eq!(c.free_capacity(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -506,13 +592,37 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cloning() {
|
||||
let c = Pipe::<NoopRawMutex, 3>::new();
|
||||
let r1 = c.reader();
|
||||
let w1 = c.writer();
|
||||
fn read_buf() {
|
||||
let mut c = Pipe::<NoopRawMutex, 3>::new();
|
||||
let (mut r, w) = c.split();
|
||||
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();
|
||||
let _ = w1.clone();
|
||||
#[test]
|
||||
fn writer_is_cloneable() {
|
||||
let mut c = Pipe::<NoopRawMutex, 3>::new();
|
||||
let (_r, w) = c.split();
|
||||
let _ = w.clone();
|
||||
}
|
||||
|
||||
#[futures_test::test]
|
||||
|
@ -1,5 +1,6 @@
|
||||
use core::ops::Range;
|
||||
|
||||
pub struct RingBuffer<const N: usize> {
|
||||
buf: [u8; N],
|
||||
start: usize,
|
||||
end: usize,
|
||||
empty: bool,
|
||||
@ -8,27 +9,26 @@ pub struct RingBuffer<const N: usize> {
|
||||
impl<const N: usize> RingBuffer<N> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
buf: [0; N],
|
||||
start: 0,
|
||||
end: 0,
|
||||
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 {
|
||||
trace!(" ringbuf: push_buf empty");
|
||||
return &mut self.buf[..0];
|
||||
return 0..0;
|
||||
}
|
||||
|
||||
let n = if self.start <= self.end {
|
||||
self.buf.len() - self.end
|
||||
N - self.end
|
||||
} else {
|
||||
self.start - self.end
|
||||
};
|
||||
|
||||
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) {
|
||||
@ -41,20 +41,20 @@ impl<const N: usize> RingBuffer<N> {
|
||||
self.empty = false;
|
||||
}
|
||||
|
||||
pub fn pop_buf(&mut self) -> &mut [u8] {
|
||||
pub fn pop_buf(&mut self) -> Range<usize> {
|
||||
if self.empty {
|
||||
trace!(" ringbuf: pop_buf empty");
|
||||
return &mut self.buf[..0];
|
||||
return 0..0;
|
||||
}
|
||||
|
||||
let n = if self.end <= self.start {
|
||||
self.buf.len() - self.start
|
||||
N - self.start
|
||||
} else {
|
||||
self.end - self.start
|
||||
};
|
||||
|
||||
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) {
|
||||
@ -93,8 +93,8 @@ impl<const N: usize> RingBuffer<N> {
|
||||
}
|
||||
|
||||
fn wrap(&self, n: usize) -> usize {
|
||||
assert!(n <= self.buf.len());
|
||||
if n == self.buf.len() {
|
||||
assert!(n <= N);
|
||||
if n == N {
|
||||
0
|
||||
} else {
|
||||
n
|
||||
@ -110,37 +110,29 @@ mod tests {
|
||||
fn push_pop() {
|
||||
let mut rb: RingBuffer<4> = RingBuffer::new();
|
||||
let buf = rb.push_buf();
|
||||
assert_eq!(4, buf.len());
|
||||
buf[0] = 1;
|
||||
buf[1] = 2;
|
||||
buf[2] = 3;
|
||||
buf[3] = 4;
|
||||
assert_eq!(0..4, buf);
|
||||
rb.push(4);
|
||||
|
||||
let buf = rb.pop_buf();
|
||||
assert_eq!(4, buf.len());
|
||||
assert_eq!(1, buf[0]);
|
||||
assert_eq!(0..4, buf);
|
||||
rb.pop(1);
|
||||
|
||||
let buf = rb.pop_buf();
|
||||
assert_eq!(3, buf.len());
|
||||
assert_eq!(2, buf[0]);
|
||||
assert_eq!(1..4, buf);
|
||||
rb.pop(1);
|
||||
|
||||
let buf = rb.pop_buf();
|
||||
assert_eq!(2, buf.len());
|
||||
assert_eq!(3, buf[0]);
|
||||
assert_eq!(2..4, buf);
|
||||
rb.pop(1);
|
||||
|
||||
let buf = rb.pop_buf();
|
||||
assert_eq!(1, buf.len());
|
||||
assert_eq!(4, buf[0]);
|
||||
assert_eq!(3..4, buf);
|
||||
rb.pop(1);
|
||||
|
||||
let buf = rb.pop_buf();
|
||||
assert_eq!(0, buf.len());
|
||||
assert_eq!(0..0, buf);
|
||||
|
||||
let buf = rb.push_buf();
|
||||
assert_eq!(4, buf.len());
|
||||
assert_eq!(0..4, buf);
|
||||
}
|
||||
}
|
||||
|
260
embassy-sync/src/zerocopy_channel.rs
Normal file
260
embassy-sync/src/zerocopy_channel.rs
Normal file
@ -0,0 +1,260 @@
|
||||
//! A zero-copy queue for sending values between asynchronous tasks.
|
||||
//!
|
||||
//! It can be used concurrently by multiple producers (senders) and multiple
|
||||
//! consumers (receivers), i.e. it is an "MPMC channel".
|
||||
//!
|
||||
//! Receivers are competing for messages. So a message that is received by
|
||||
//! one receiver is not received by any other.
|
||||
//!
|
||||
//! This queue takes a Mutex type so that various
|
||||
//! targets can be attained. For example, a ThreadModeMutex can be used
|
||||
//! for single-core Cortex-M targets where messages are only passed
|
||||
//! between tasks running in thread mode. Similarly, a CriticalSectionMutex
|
||||
//! can also be used for single-core targets where messages are to be
|
||||
//! passed from exception mode e.g. out of an interrupt handler.
|
||||
//!
|
||||
//! This module provides a bounded channel that has a limit on the number of
|
||||
//! messages that it can store, and if this limit is reached, trying to send
|
||||
//! another message will result in an error being returned.
|
||||
|
||||
use core::cell::RefCell;
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use crate::blocking_mutex::raw::RawMutex;
|
||||
use crate::blocking_mutex::Mutex;
|
||||
use crate::waitqueue::WakerRegistration;
|
||||
|
||||
/// A bounded zero-copy channel for communicating between asynchronous tasks
|
||||
/// with backpressure.
|
||||
///
|
||||
/// The channel will buffer up to the provided number of messages. Once the
|
||||
/// buffer is full, attempts to `send` new messages will wait until a message is
|
||||
/// received from the channel.
|
||||
///
|
||||
/// All data sent will become available in the same order as it was sent.
|
||||
///
|
||||
/// The channel requires a buffer of recyclable elements. Writing to the channel is done through
|
||||
/// an `&mut T`.
|
||||
pub struct Channel<'a, M: RawMutex, T> {
|
||||
buf: *mut T,
|
||||
phantom: PhantomData<&'a mut T>,
|
||||
state: Mutex<M, RefCell<State>>,
|
||||
}
|
||||
|
||||
impl<'a, M: RawMutex, T> Channel<'a, M, T> {
|
||||
/// Initialize a new [`Channel`].
|
||||
///
|
||||
/// The provided buffer will be used and reused by the channel's logic, and thus dictates the
|
||||
/// channel's capacity.
|
||||
pub fn new(buf: &'a mut [T]) -> Self {
|
||||
let len = buf.len();
|
||||
assert!(len != 0);
|
||||
|
||||
Self {
|
||||
buf: buf.as_mut_ptr(),
|
||||
phantom: PhantomData,
|
||||
state: Mutex::new(RefCell::new(State {
|
||||
len,
|
||||
front: 0,
|
||||
back: 0,
|
||||
full: false,
|
||||
send_waker: WakerRegistration::new(),
|
||||
receive_waker: WakerRegistration::new(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [`Sender`] and [`Receiver`] from an existing channel.
|
||||
///
|
||||
/// Further Senders and Receivers can be created through [`Sender::borrow`] and
|
||||
/// [`Receiver::borrow`] respectively.
|
||||
pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) {
|
||||
(Sender { channel: self }, Receiver { channel: self })
|
||||
}
|
||||
}
|
||||
|
||||
/// Send-only access to a [`Channel`].
|
||||
pub struct Sender<'a, M: RawMutex, T> {
|
||||
channel: &'a Channel<'a, M, T>,
|
||||
}
|
||||
|
||||
impl<'a, M: RawMutex, T> Sender<'a, M, T> {
|
||||
/// Creates one further [`Sender`] over the same channel.
|
||||
pub fn borrow(&mut self) -> Sender<'_, M, T> {
|
||||
Sender { channel: self.channel }
|
||||
}
|
||||
|
||||
/// Attempts to send a value over the channel.
|
||||
pub fn try_send(&mut self) -> Option<&mut T> {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.push_index() {
|
||||
Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
|
||||
None => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Attempts to send a value over the channel.
|
||||
pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.push_index() {
|
||||
Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
|
||||
None => {
|
||||
s.receive_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Asynchronously send a value over the channel.
|
||||
pub async fn send(&mut self) -> &mut T {
|
||||
let i = poll_fn(|cx| {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.push_index() {
|
||||
Some(i) => Poll::Ready(i),
|
||||
None => {
|
||||
s.receive_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.await;
|
||||
unsafe { &mut *self.channel.buf.add(i) }
|
||||
}
|
||||
|
||||
/// Notify the channel that the sending of the value has been finalized.
|
||||
pub fn send_done(&mut self) {
|
||||
self.channel.state.lock(|s| s.borrow_mut().push_done())
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive-only access to a [`Channel`].
|
||||
pub struct Receiver<'a, M: RawMutex, T> {
|
||||
channel: &'a Channel<'a, M, T>,
|
||||
}
|
||||
|
||||
impl<'a, M: RawMutex, T> Receiver<'a, M, T> {
|
||||
/// Creates one further [`Sender`] over the same channel.
|
||||
pub fn borrow(&mut self) -> Receiver<'_, M, T> {
|
||||
Receiver { channel: self.channel }
|
||||
}
|
||||
|
||||
/// Attempts to receive a value over the channel.
|
||||
pub fn try_receive(&mut self) -> Option<&mut T> {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.pop_index() {
|
||||
Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
|
||||
None => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Attempts to asynchronously receive a value over the channel.
|
||||
pub fn poll_receive(&mut self, cx: &mut Context) -> Poll<&mut T> {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.pop_index() {
|
||||
Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
|
||||
None => {
|
||||
s.send_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Asynchronously receive a value over the channel.
|
||||
pub async fn receive(&mut self) -> &mut T {
|
||||
let i = poll_fn(|cx| {
|
||||
self.channel.state.lock(|s| {
|
||||
let s = &mut *s.borrow_mut();
|
||||
match s.pop_index() {
|
||||
Some(i) => Poll::Ready(i),
|
||||
None => {
|
||||
s.send_waker.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.await;
|
||||
unsafe { &mut *self.channel.buf.add(i) }
|
||||
}
|
||||
|
||||
/// Notify the channel that the receiving of the value has been finalized.
|
||||
pub fn receive_done(&mut self) {
|
||||
self.channel.state.lock(|s| s.borrow_mut().pop_done())
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
len: usize,
|
||||
|
||||
/// Front index. Always 0..=(N-1)
|
||||
front: usize,
|
||||
/// Back index. Always 0..=(N-1).
|
||||
back: usize,
|
||||
|
||||
/// Used to distinguish "empty" and "full" cases when `front == back`.
|
||||
/// May only be `true` if `front == back`, always `false` otherwise.
|
||||
full: bool,
|
||||
|
||||
send_waker: WakerRegistration,
|
||||
receive_waker: WakerRegistration,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn increment(&self, i: usize) -> usize {
|
||||
if i + 1 == self.len {
|
||||
0
|
||||
} else {
|
||||
i + 1
|
||||
}
|
||||
}
|
||||
|
||||
fn is_full(&self) -> bool {
|
||||
self.full
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.front == self.back && !self.full
|
||||
}
|
||||
|
||||
fn push_index(&mut self) -> Option<usize> {
|
||||
match self.is_full() {
|
||||
true => None,
|
||||
false => Some(self.back),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_done(&mut self) {
|
||||
assert!(!self.is_full());
|
||||
self.back = self.increment(self.back);
|
||||
if self.back == self.front {
|
||||
self.full = true;
|
||||
}
|
||||
self.send_waker.wake();
|
||||
}
|
||||
|
||||
fn pop_index(&mut self) -> Option<usize> {
|
||||
match self.is_empty() {
|
||||
true => None,
|
||||
false => Some(self.front),
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_done(&mut self) {
|
||||
assert!(!self.is_empty());
|
||||
self.front = self.increment(self.front);
|
||||
self.full = false;
|
||||
self.receive_waker.wake();
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ impl Instant {
|
||||
/// Panics on over/underflow.
|
||||
pub fn duration_since(&self, earlier: Instant) -> Duration {
|
||||
Duration {
|
||||
ticks: self.ticks.checked_sub(earlier.ticks).unwrap(),
|
||||
ticks: unwrap!(self.ticks.checked_sub(earlier.ticks)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![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.");
|
||||
|
||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::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
|
||||
}
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ default = ["panic-reset"]
|
||||
debug = [
|
||||
"embassy-rp/defmt",
|
||||
"embassy-boot-rp/defmt",
|
||||
"embassy-sync/defmt",
|
||||
"panic-probe"
|
||||
]
|
||||
skip-include = []
|
||||
|
@ -38,7 +38,7 @@ async fn main(_s: Spawner) {
|
||||
let flash = Mutex::new(RefCell::new(flash));
|
||||
|
||||
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
|
||||
let mut aligned = AlignedBuffer([0; 4]);
|
||||
let mut aligned = AlignedBuffer([0; 1]);
|
||||
let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0);
|
||||
|
||||
Timer::after(Duration::from_secs(5)).await;
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] }
|
||||
embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] }
|
||||
@ -25,5 +25,6 @@ defmt = [
|
||||
"dep:defmt",
|
||||
"embassy-stm32/defmt",
|
||||
"embassy-boot-stm32/defmt",
|
||||
"embassy-sync/defmt",
|
||||
]
|
||||
skip-include = []
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] }
|
||||
embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] }
|
||||
@ -26,5 +26,6 @@ defmt = [
|
||||
"dep:defmt",
|
||||
"embassy-stm32/defmt",
|
||||
"embassy-boot-stm32/defmt",
|
||||
"embassy-sync/defmt",
|
||||
]
|
||||
skip-include = []
|
||||
|
@ -26,5 +26,6 @@ defmt = [
|
||||
"dep:defmt",
|
||||
"embassy-stm32/defmt",
|
||||
"embassy-boot-stm32/defmt",
|
||||
"embassy-sync/defmt",
|
||||
]
|
||||
skip-include = []
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] }
|
||||
embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] }
|
||||
@ -25,5 +25,6 @@ defmt = [
|
||||
"dep:defmt",
|
||||
"embassy-stm32/defmt",
|
||||
"embassy-boot-stm32/defmt",
|
||||
"embassy-sync/defmt",
|
||||
]
|
||||
skip-include = []
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] }
|
||||
embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.3", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] }
|
||||
@ -25,5 +25,6 @@ defmt = [
|
||||
"dep:defmt",
|
||||
"embassy-stm32/defmt",
|
||||
"embassy-boot-stm32/defmt",
|
||||
"embassy-sync/defmt",
|
||||
]
|
||||
skip-include = []
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user