Merge branch 'main' of https://github.com/embassy-rs/embassy into rtc-lp

This commit is contained in:
xoviat 2023-08-22 16:58:43 -05:00
commit 7d6edd7b15
145 changed files with 3933 additions and 1276 deletions

12
.github/ci/doc.sh vendored
View File

@ -15,7 +15,6 @@ export BUILDER_COMPRESS=true
# which makes rustup very sad # which makes rustup very sad
rustc --version > /dev/null rustc --version > /dev/null
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup
docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup
docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup
@ -36,7 +35,8 @@ docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/g
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup
docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup
docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
@ -44,3 +44,11 @@ export KUBECONFIG=/ci/secrets/kubeconfig.yml
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
kubectl cp webroot/crates $POD:/data kubectl cp webroot/crates $POD:/data
kubectl cp webroot/static $POD:/data kubectl cp webroot/static $POD:/data
# build and upload stm32 last
# so that it doesn't prevent other crates from getting docs updates when it breaks.
rm -rf webroot
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
kubectl cp webroot/crates $POD:/data

17
.vscode/settings.json vendored
View File

@ -6,16 +6,21 @@
"rust-analyzer.check.allTargets": false, "rust-analyzer.check.allTargets": false,
"rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.check.noDefaultFeatures": true,
"rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true,
"rust-analyzer.cargo.target": "thumbv7m-none-eabi", "rust-analyzer.showUnlinkedFileNotification": false,
// uncomment the target of your chip.
//"rust-analyzer.cargo.target": "thumbv6m-none-eabi",
//"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
"rust-analyzer.cargo.features": [ "rust-analyzer.cargo.features": [
///"nightly", // Uncomment if the example has a "nightly" feature.
"nightly",
], ],
"rust-analyzer.linkedProjects": [ "rust-analyzer.linkedProjects": [
// Declare for the target you wish to develop // Uncomment ONE line for the chip you want to work on.
// "embassy-executor/Cargo.toml", // This makes rust-analyzer work on the example crate and all its dependencies.
// "embassy-sync/Cargo.toml", "examples/nrf52840/Cargo.toml",
"examples/stm32wl/Cargo.toml", // "examples/nrf52840-rtic/Cargo.toml",
// "examples/nrf5340/Cargo.toml", // "examples/nrf5340/Cargo.toml",
// "examples/nrf-rtos-trace/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml",
// "examples/rp/Cargo.toml", // "examples/rp/Cargo.toml",

3
ci.sh
View File

@ -3,7 +3,7 @@
set -euo pipefail set -euo pipefail
export RUSTFLAGS=-Dwarnings export RUSTFLAGS=-Dwarnings
export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
TARGET=$(rustc -vV | sed -n 's|host: ||p') TARGET=$(rustc -vV | sed -n 's|host: ||p')
@ -81,6 +81,7 @@ cargo batch \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f378cc,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \

View File

@ -24,7 +24,7 @@ cortex-m = "0.7.6"
cortex-m-rt = "0.7.0" cortex-m-rt = "0.7.0"
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.11" } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.1" }
num_enum = { version = "0.5.7", default-features = false } num_enum = { version = "0.5.7", default-features = false }
[package.metadata.embassy_docs] [package.metadata.embassy_docs]

View File

@ -102,7 +102,7 @@ where
cmd_buf[0] = cmd; cmd_buf[0] = cmd;
cmd_buf[1..][..buf.len()].copy_from_slice(buf); cmd_buf[1..][..buf.len()].copy_from_slice(buf);
self.status = self.spi.cmd_write(&cmd_buf).await; self.status = self.spi.cmd_write(&cmd_buf[..buf.len() + 1]).await;
} }
#[allow(unused)] #[allow(unused)]

View File

@ -14,28 +14,17 @@ use embassy_nrf::wdt;
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
/// A bootloader for nRF devices. /// A bootloader for nRF devices.
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = PAGE_SIZE> { pub struct BootLoader<const BUFFER_SIZE: usize = PAGE_SIZE>;
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
}
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE> /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware.
{ pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
/// Create a new bootloader instance using the supplied partitions for active, dfu and state. config: BootLoaderConfig<ACTIVE, DFU, STATE>,
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self { ) -> Self {
Self { let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
boot: embassy_boot::BootLoader::new(config), let mut boot = embassy_boot::BootLoader::new(config);
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), boot.prepare_boot(&mut aligned_buf.0).expect("Boot prepare error");
} Self
}
/// Inspect the bootloader state and perform actions required before booting, such as swapping
/// firmware.
pub fn prepare(&mut self) {
self.boot
.prepare_boot(&mut self.aligned_buf.0)
.expect("Boot prepare error");
} }
/// Boots the application without softdevice mechanisms. /// Boots the application without softdevice mechanisms.
@ -45,8 +34,6 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
/// This modifies the stack pointer and reset vector and will run code placed in the active partition. /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
#[cfg(not(feature = "softdevice"))] #[cfg(not(feature = "softdevice"))]
pub unsafe fn load(self, start: u32) -> ! { pub unsafe fn load(self, start: u32) -> ! {
core::mem::drop(self.boot);
let mut p = cortex_m::Peripherals::steal(); let mut p = cortex_m::Peripherals::steal();
p.SCB.invalidate_icache(); p.SCB.invalidate_icache();
p.SCB.vtor.write(start); p.SCB.vtor.write(start);
@ -59,7 +46,7 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
/// ///
/// This modifies the stack pointer and reset vector and will run code placed in the active partition. /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
#[cfg(feature = "softdevice")] #[cfg(feature = "softdevice")]
pub unsafe fn load(&mut self, _app: u32) -> ! { pub unsafe fn load(self, _app: u32) -> ! {
use nrf_softdevice_mbr as mbr; use nrf_softdevice_mbr as mbr;
const NRF_SUCCESS: u32 = 0; const NRF_SUCCESS: u32 = 0;

View File

@ -15,28 +15,17 @@ use embassy_time::Duration;
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
/// A bootloader for RP2040 devices. /// A bootloader for RP2040 devices.
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = ERASE_SIZE> { pub struct BootLoader<const BUFFER_SIZE: usize = ERASE_SIZE>;
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
}
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE> /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
{ pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
/// Create a new bootloader instance using the supplied partitions for active, dfu and state. config: BootLoaderConfig<ACTIVE, DFU, STATE>,
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self { ) -> Self {
Self { let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
boot: embassy_boot::BootLoader::new(config), let mut boot = embassy_boot::BootLoader::new(config);
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
} Self
}
/// Inspect the bootloader state and perform actions required before booting, such as swapping
/// firmware.
pub fn prepare(&mut self) {
self.boot
.prepare_boot(self.aligned_buf.as_mut())
.expect("Boot prepare error");
} }
/// Boots the application. /// Boots the application.
@ -45,8 +34,6 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
/// ///
/// This modifies the stack pointer and reset vector and will run code placed in the active partition. /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
pub unsafe fn load(self, start: u32) -> ! { pub unsafe fn load(self, start: u32) -> ! {
core::mem::drop(self.boot);
trace!("Loading app at 0x{:x}", start); trace!("Loading app at 0x{:x}", start);
#[allow(unused_mut)] #[allow(unused_mut)]
let mut p = cortex_m::Peripherals::steal(); let mut p = cortex_m::Peripherals::steal();
@ -67,7 +54,7 @@ pub struct WatchdogFlash<'d, const SIZE: usize> {
impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
/// Start a new watchdog with a given flash and watchdog peripheral and a timeout /// Start a new watchdog with a given flash and watchdog peripheral and a timeout
pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self { pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self {
let flash = Flash::<_, Blocking, SIZE>::new(flash); let flash = Flash::<_, Blocking, SIZE>::new_blocking(flash);
let mut watchdog = Watchdog::new(watchdog); let mut watchdog = Watchdog::new(watchdog);
watchdog.start(timeout); watchdog.start(timeout);
Self { flash, watchdog } Self { flash, watchdog }
@ -84,11 +71,11 @@ impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> {
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.watchdog.feed(); self.watchdog.feed();
self.flash.erase(from, to) self.flash.blocking_erase(from, to)
} }
fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
self.watchdog.feed(); self.watchdog.feed();
self.flash.write(offset, data) self.flash.blocking_write(offset, data)
} }
} }
@ -96,7 +83,7 @@ impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> {
const READ_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as ReadNorFlash>::READ_SIZE; const READ_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as ReadNorFlash>::READ_SIZE;
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
self.watchdog.feed(); self.watchdog.feed();
self.flash.read(offset, data) self.flash.blocking_read(offset, data)
} }
fn capacity(&self) -> usize { fn capacity(&self) -> usize {
self.flash.capacity() self.flash.capacity()

View File

@ -11,28 +11,17 @@ pub use embassy_boot::{FirmwareState, FirmwareUpdater};
use embedded_storage::nor_flash::NorFlash; use embedded_storage::nor_flash::NorFlash;
/// A bootloader for STM32 devices. /// A bootloader for STM32 devices.
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> { pub struct BootLoader;
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
}
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> impl BootLoader {
BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE> /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
{ pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>(
/// Create a new bootloader instance using the supplied partitions for active, dfu and state. config: BootLoaderConfig<ACTIVE, DFU, STATE>,
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self { ) -> Self {
Self { let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
boot: embassy_boot::BootLoader::new(config), let mut boot = embassy_boot::BootLoader::new(config);
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
} Self
}
/// Inspect the bootloader state and perform actions required before booting, such as swapping
/// firmware.
pub fn prepare(&mut self) {
self.boot
.prepare_boot(self.aligned_buf.as_mut())
.expect("Boot prepare error");
} }
/// Boots the application. /// Boots the application.
@ -41,8 +30,6 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
/// ///
/// This modifies the stack pointer and reset vector and will run code placed in the active partition. /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
pub unsafe fn load(self, start: u32) -> ! { pub unsafe fn load(self, start: u32) -> ! {
core::mem::drop(self.boot);
trace!("Loading app at 0x{:x}", start); trace!("Loading app at 0x{:x}", start);
#[allow(unused_mut)] #[allow(unused_mut)]
let mut p = cortex_m::Peripherals::steal(); let mut p = cortex_m::Peripherals::steal();

View File

@ -25,8 +25,8 @@ embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
"unproven", "unproven",
] } ] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true } embedded-hal-async = { version = "=1.0.0-rc.1", optional = true }
embedded-storage = "0.3.0" embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.4.0", optional = true } embedded-storage-async = { version = "0.4.0", optional = true }
nb = "1.0.0" nb = "1.0.0"

View File

@ -1,4 +1,4 @@
use embedded_hal_02::{blocking, serial}; use embedded_hal_02::blocking;
/// Wrapper that implements async traits using blocking implementations. /// Wrapper that implements async traits using blocking implementations.
/// ///
@ -103,15 +103,6 @@ where
} }
} }
// Uart implementatinos
impl<T, E> embedded_hal_1::serial::ErrorType for BlockingAsync<T>
where
T: serial::Read<u8, Error = E>,
E: embedded_hal_1::serial::Error + 'static,
{
type Error = E;
}
/// NOR flash wrapper /// NOR flash wrapper
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};

View File

@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
- Replaced Pender. Implementations now must define an extern function called `__pender`.
- Made `raw::AvailableTask` public
- Made `SpawnToken::new_failed` public
## 0.2.1 - 2023-08-10
- Avoid calling `pend()` when waking expired timers
- Properly reset finished task state with `integrated-timers` enabled
- Introduce `InterruptExecutor::spawner()`
- Fix incorrect critical section in Xtensa executor
## 0.2.0 - 2023-04-27 ## 0.2.0 - 2023-04-27
- Replace unnecessary atomics in runqueue - Replace unnecessary atomics in runqueue

View File

@ -1,6 +1,6 @@
[package] [package]
name = "embassy-executor" name = "embassy-executor"
version = "0.2.0" version = "0.2.1"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "async/await executor designed for embedded usage" description = "async/await executor designed for embedded usage"
@ -14,7 +14,7 @@ categories = [
[package.metadata.embassy_docs] [package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
features = ["nightly", "defmt", "pender-callback"] features = ["nightly", "defmt"]
flavors = [ flavors = [
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
@ -25,7 +25,7 @@ flavors = [
[package.metadata.docs.rs] [package.metadata.docs.rs]
default-target = "thumbv7em-none-eabi" default-target = "thumbv7em-none-eabi"
targets = ["thumbv7em-none-eabi"] targets = ["thumbv7em-none-eabi"]
features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-thread", "executor-interrupt"] features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
[features] [features]
@ -37,9 +37,6 @@ arch-xtensa = ["_arch"]
arch-riscv32 = ["_arch"] arch-riscv32 = ["_arch"]
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
# Enable creating a `Pender` from an arbitrary function pointer callback.
pender-callback = []
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs) # Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
executor-thread = [] executor-thread = []
# Enable the interrupt-mode executor (available in Cortex-M only) # Enable the interrupt-mode executor (available in Cortex-M only)

View File

@ -1,3 +1,49 @@
const THREAD_PENDER: usize = usize::MAX;
#[export_name = "__pender"]
#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))]
fn __pender(context: *mut ()) {
unsafe {
// Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt
// request number given to `InterruptExecutor::start`.
let context = context as usize;
#[cfg(feature = "executor-thread")]
// Try to make Rust optimize the branching away if we only use thread mode.
if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER {
core::arch::asm!("sev");
return;
}
#[cfg(feature = "executor-interrupt")]
{
use cortex_m::interrupt::InterruptNumber;
use cortex_m::peripheral::NVIC;
#[derive(Clone, Copy)]
struct Irq(u16);
unsafe impl InterruptNumber for Irq {
fn number(self) -> u16 {
self.0
}
}
let irq = Irq(context as u16);
// STIR is faster, but is only available in v7 and higher.
#[cfg(not(armv6m))]
{
let mut nvic: NVIC = core::mem::transmute(());
nvic.request(irq);
}
#[cfg(armv6m)]
NVIC::pend(irq);
}
}
}
#[cfg(feature = "executor-thread")] #[cfg(feature = "executor-thread")]
pub use thread::*; pub use thread::*;
#[cfg(feature = "executor-thread")] #[cfg(feature = "executor-thread")]
@ -8,18 +54,9 @@ mod thread {
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
pub use embassy_macros::main_cortex_m as main; pub use embassy_macros::main_cortex_m as main;
use crate::raw::{Pender, PenderInner}; use crate::arch::THREAD_PENDER;
use crate::{raw, Spawner}; use crate::{raw, Spawner};
#[derive(Copy, Clone)]
pub(crate) struct ThreadPender;
impl ThreadPender {
pub(crate) fn pend(self) {
unsafe { core::arch::asm!("sev") }
}
}
/// Thread mode executor, using WFE/SEV. /// Thread mode executor, using WFE/SEV.
/// ///
/// This is the simplest and most common kind of executor. It runs on /// This is the simplest and most common kind of executor. It runs on
@ -39,7 +76,7 @@ mod thread {
/// Create a new Executor. /// Create a new Executor.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), inner: raw::Executor::new(THREAD_PENDER as *mut ()),
not_send: PhantomData, not_send: PhantomData,
} }
} }
@ -86,30 +123,7 @@ mod interrupt {
use cortex_m::interrupt::InterruptNumber; use cortex_m::interrupt::InterruptNumber;
use cortex_m::peripheral::NVIC; use cortex_m::peripheral::NVIC;
use crate::raw::{self, Pender, PenderInner}; use crate::raw;
#[derive(Clone, Copy)]
pub(crate) struct InterruptPender(u16);
impl InterruptPender {
pub(crate) fn pend(self) {
// STIR is faster, but is only available in v7 and higher.
#[cfg(not(armv6m))]
{
let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) };
nvic.request(self);
}
#[cfg(armv6m)]
cortex_m::peripheral::NVIC::pend(self);
}
}
unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender {
fn number(self) -> u16 {
self.0
}
}
/// Interrupt mode executor. /// Interrupt mode executor.
/// ///
@ -194,9 +208,7 @@ mod interrupt {
unsafe { unsafe {
(&mut *self.executor.get()) (&mut *self.executor.get())
.as_mut_ptr() .as_mut_ptr()
.write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender( .write(raw::Executor::new(irq.number() as *mut ()))
irq.number(),
)))))
} }
let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; let executor = unsafe { (&*self.executor.get()).assume_init_ref() };

View File

@ -11,22 +11,16 @@ mod thread {
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
pub use embassy_macros::main_riscv as main; pub use embassy_macros::main_riscv as main;
use crate::raw::{Pender, PenderInner};
use crate::{raw, Spawner}; use crate::{raw, Spawner};
#[derive(Copy, Clone)]
pub(crate) struct ThreadPender;
impl ThreadPender {
#[allow(unused)]
pub(crate) fn pend(self) {
SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
}
}
/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
#[export_name = "__pender"]
fn __pender(_context: *mut ()) {
SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
}
/// RISCV32 Executor /// RISCV32 Executor
pub struct Executor { pub struct Executor {
inner: raw::Executor, inner: raw::Executor,
@ -37,7 +31,7 @@ mod thread {
/// Create a new Executor. /// Create a new Executor.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), inner: raw::Executor::new(core::ptr::null_mut()),
not_send: PhantomData, not_send: PhantomData,
} }
} }

View File

@ -11,17 +11,12 @@ mod thread {
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
pub use embassy_macros::main_std as main; pub use embassy_macros::main_std as main;
use crate::raw::{Pender, PenderInner};
use crate::{raw, Spawner}; use crate::{raw, Spawner};
#[derive(Copy, Clone)] #[export_name = "__pender"]
pub(crate) struct ThreadPender(&'static Signaler); fn __pender(context: *mut ()) {
let signaler: &'static Signaler = unsafe { std::mem::transmute(context) };
impl ThreadPender { signaler.signal()
#[allow(unused)]
pub(crate) fn pend(self) {
self.0.signal()
}
} }
/// Single-threaded std-based executor. /// Single-threaded std-based executor.
@ -34,9 +29,9 @@ mod thread {
impl Executor { impl Executor {
/// Create a new Executor. /// Create a new Executor.
pub fn new() -> Self { pub fn new() -> Self {
let signaler = &*Box::leak(Box::new(Signaler::new())); let signaler = Box::leak(Box::new(Signaler::new()));
Self { Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))), inner: raw::Executor::new(signaler as *mut Signaler as *mut ()),
not_send: PhantomData, not_send: PhantomData,
signaler, signaler,
} }

View File

@ -14,14 +14,12 @@ mod thread {
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use crate::raw::util::UninitCell; use crate::raw::util::UninitCell;
use crate::raw::{Pender, PenderInner};
use crate::{raw, Spawner}; use crate::{raw, Spawner};
/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. #[export_name = "__pender"]
pub struct Executor { fn __pender(context: *mut ()) {
inner: raw::Executor, let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) };
ctx: &'static WasmContext, let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() });
not_send: PhantomData<*mut ()>,
} }
pub(crate) struct WasmContext { pub(crate) struct WasmContext {
@ -29,16 +27,6 @@ mod thread {
closure: UninitCell<Closure<dyn FnMut(JsValue)>>, closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
} }
#[derive(Copy, Clone)]
pub(crate) struct ThreadPender(&'static WasmContext);
impl ThreadPender {
#[allow(unused)]
pub(crate) fn pend(self) {
let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() });
}
}
impl WasmContext { impl WasmContext {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@ -48,14 +36,21 @@ mod thread {
} }
} }
/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
pub struct Executor {
inner: raw::Executor,
ctx: &'static WasmContext,
not_send: PhantomData<*mut ()>,
}
impl Executor { impl Executor {
/// Create a new Executor. /// Create a new Executor.
pub fn new() -> Self { pub fn new() -> Self {
let ctx = &*Box::leak(Box::new(WasmContext::new())); let ctx = Box::leak(Box::new(WasmContext::new()));
Self { Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))), inner: raw::Executor::new(ctx as *mut WasmContext as *mut ()),
not_send: PhantomData,
ctx, ctx,
not_send: PhantomData,
} }
} }

View File

@ -8,22 +8,16 @@ mod thread {
use core::marker::PhantomData; use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering}; use core::sync::atomic::{AtomicBool, Ordering};
use crate::raw::{Pender, PenderInner};
use crate::{raw, Spawner}; use crate::{raw, Spawner};
#[derive(Copy, Clone)]
pub(crate) struct ThreadPender;
impl ThreadPender {
#[allow(unused)]
pub(crate) fn pend(self) {
SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
}
}
/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
#[export_name = "__pender"]
fn __pender(_context: *mut ()) {
SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
}
/// Xtensa Executor /// Xtensa Executor
pub struct Executor { pub struct Executor {
inner: raw::Executor, inner: raw::Executor,
@ -34,7 +28,7 @@ mod thread {
/// Create a new Executor. /// Create a new Executor.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), inner: raw::Executor::new(core::ptr::null_mut()),
not_send: PhantomData, not_send: PhantomData,
} }
} }
@ -77,8 +71,8 @@ mod thread {
SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
core::arch::asm!( core::arch::asm!(
"wsr.ps {0}", "wsr.ps {0}",
"rsync", in(reg) token) "rsync", in(reg) token)
} else { } else {
// waiti sets the PS.INTLEVEL when slipping into sleep // waiti sets the PS.INTLEVEL when slipping into sleep
// because critical sections in Xtensa are implemented via increasing // because critical sections in Xtensa are implemented via increasing

View File

@ -147,10 +147,7 @@ impl<F: Future + 'static> TaskStorage<F> {
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> { pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
let task = AvailableTask::claim(self); let task = AvailableTask::claim(self);
match task { match task {
Some(task) => { Some(task) => task.initialize(future),
let task = task.initialize(future);
unsafe { SpawnToken::<F>::new(task) }
}
None => SpawnToken::new_failed(), None => SpawnToken::new_failed(),
} }
} }
@ -186,12 +183,16 @@ impl<F: Future + 'static> TaskStorage<F> {
} }
} }
struct AvailableTask<F: Future + 'static> { /// An uninitialized [`TaskStorage`].
pub struct AvailableTask<F: Future + 'static> {
task: &'static TaskStorage<F>, task: &'static TaskStorage<F>,
} }
impl<F: Future + 'static> AvailableTask<F> { impl<F: Future + 'static> AvailableTask<F> {
fn claim(task: &'static TaskStorage<F>) -> Option<Self> { /// Try to claim a [`TaskStorage`].
///
/// This function returns `None` if a task has already been spawned and has not finished running.
pub fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
task.raw task.raw
.state .state
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire) .compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
@ -199,61 +200,30 @@ impl<F: Future + 'static> AvailableTask<F> {
.map(|_| Self { task }) .map(|_| Self { task })
} }
fn initialize(self, future: impl FnOnce() -> F) -> TaskRef { fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
unsafe { unsafe {
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll)); self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
self.task.future.write(future()); self.task.future.write(future());
}
TaskRef::new(self.task)
}
}
/// Raw storage that can hold up to N tasks of the same type. let task = TaskRef::new(self.task);
///
/// This is essentially a `[TaskStorage<F>; N]`.
pub struct TaskPool<F: Future + 'static, const N: usize> {
pool: [TaskStorage<F>; N],
}
impl<F: Future + 'static, const N: usize> TaskPool<F, N> { SpawnToken::new(task)
/// Create a new TaskPool, with all tasks in non-spawned state.
pub const fn new() -> Self {
Self {
pool: [TaskStorage::NEW; N],
} }
} }
/// Try to spawn a task in the pool. /// Initialize the [`TaskStorage`] to run the given future.
/// pub fn initialize(self, future: impl FnOnce() -> F) -> SpawnToken<F> {
/// See [`TaskStorage::spawn()`] for details. self.initialize_impl::<F>(future)
///
/// This will loop over the pool and spawn the task in the first storage that
/// is currently free. If none is free, a "poisoned" SpawnToken is returned,
/// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
let task = self.pool.iter().find_map(AvailableTask::claim);
match task {
Some(task) => {
let task = task.initialize(future);
unsafe { SpawnToken::<F>::new(task) }
}
None => SpawnToken::new_failed(),
}
} }
/// Like spawn(), but allows the task to be send-spawned if the args are Send even if /// Initialize the [`TaskStorage`] to run the given future.
/// the future is !Send.
/// ///
/// Not covered by semver guarantees. DO NOT call this directly. Intended to be used /// # Safety
/// by the Embassy macros ONLY.
/// ///
/// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` /// `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
/// is an `async fn`, NOT a hand-written `Future`. /// is an `async fn`, NOT a hand-written `Future`.
#[doc(hidden)] #[doc(hidden)]
pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized> pub unsafe fn __initialize_async_fn<FutFn>(self, future: impl FnOnce() -> F) -> SpawnToken<FutFn> {
where
FutFn: FnOnce() -> F,
{
// When send-spawning a task, we construct the future in this thread, and effectively // When send-spawning a task, we construct the future in this thread, and effectively
// "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory, // "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory,
// send-spawning should require the future `F` to be `Send`. // send-spawning should require the future `F` to be `Send`.
@ -279,66 +249,73 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
// //
// This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly // This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly
// by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken<F>`. // by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken<F>`.
self.initialize_impl::<FutFn>(future)
}
}
let task = self.pool.iter().find_map(AvailableTask::claim); /// Raw storage that can hold up to N tasks of the same type.
match task { ///
Some(task) => { /// This is essentially a `[TaskStorage<F>; N]`.
let task = task.initialize(future); pub struct TaskPool<F: Future + 'static, const N: usize> {
unsafe { SpawnToken::<FutFn>::new(task) } pool: [TaskStorage<F>; N],
} }
impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
/// Create a new TaskPool, with all tasks in non-spawned state.
pub const fn new() -> Self {
Self {
pool: [TaskStorage::NEW; N],
}
}
fn spawn_impl<T>(&'static self, future: impl FnOnce() -> F) -> SpawnToken<T> {
match self.pool.iter().find_map(AvailableTask::claim) {
Some(task) => task.initialize_impl::<T>(future),
None => SpawnToken::new_failed(), None => SpawnToken::new_failed(),
} }
} }
/// Try to spawn a task in the pool.
///
/// See [`TaskStorage::spawn()`] for details.
///
/// This will loop over the pool and spawn the task in the first storage that
/// is currently free. If none is free, a "poisoned" SpawnToken is returned,
/// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
self.spawn_impl::<F>(future)
}
/// Like spawn(), but allows the task to be send-spawned if the args are Send even if
/// the future is !Send.
///
/// Not covered by semver guarantees. DO NOT call this directly. Intended to be used
/// by the Embassy macros ONLY.
///
/// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
/// is an `async fn`, NOT a hand-written `Future`.
#[doc(hidden)]
pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized>
where
FutFn: FnOnce() -> F,
{
// See the comment in AvailableTask::__initialize_async_fn for explanation.
self.spawn_impl::<FutFn>(future)
}
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub(crate) enum PenderInner { pub(crate) struct Pender(*mut ());
#[cfg(feature = "executor-thread")]
Thread(crate::arch::ThreadPender),
#[cfg(feature = "executor-interrupt")]
Interrupt(crate::arch::InterruptPender),
#[cfg(feature = "pender-callback")]
Callback { func: fn(*mut ()), context: *mut () },
}
unsafe impl Send for PenderInner {} unsafe impl Send for Pender {}
unsafe impl Sync for PenderInner {} unsafe impl Sync for Pender {}
/// Platform/architecture-specific action executed when an executor has pending work.
///
/// When a task within an executor is woken, the `Pender` is called. This does a
/// platform/architecture-specific action to signal there is pending work in the executor.
/// When this happens, you must arrange for [`Executor::poll`] to be called.
///
/// You can think of it as a waker, but for the whole executor.
pub struct Pender(pub(crate) PenderInner);
impl Pender { impl Pender {
/// Create a `Pender` that will call an arbitrary function pointer. pub(crate) fn pend(self) {
/// extern "Rust" {
/// # Arguments fn __pender(context: *mut ());
///
/// - `func`: The function pointer to call.
/// - `context`: Opaque context pointer, that will be passed to the function pointer.
#[cfg(feature = "pender-callback")]
pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self {
Self(PenderInner::Callback {
func,
context: context.into(),
})
}
}
impl Pender {
pub(crate) fn pend(&self) {
match self.0 {
#[cfg(feature = "executor-thread")]
PenderInner::Thread(x) => x.pend(),
#[cfg(feature = "executor-interrupt")]
PenderInner::Interrupt(x) => x.pend(),
#[cfg(feature = "pender-callback")]
PenderInner::Callback { func, context } => func(context),
} }
unsafe { __pender(self.0) };
} }
} }
@ -409,7 +386,7 @@ impl SyncExecutor {
#[allow(clippy::never_loop)] #[allow(clippy::never_loop)]
loop { loop {
#[cfg(feature = "integrated-timers")] #[cfg(feature = "integrated-timers")]
self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); self.timer_queue.dequeue_expired(Instant::now(), wake_task_no_pend);
self.run_queue.dequeue_all(|p| { self.run_queue.dequeue_all(|p| {
let task = p.header(); let task = p.header();
@ -472,15 +449,31 @@ impl SyncExecutor {
/// ///
/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks /// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
/// that "want to run"). /// that "want to run").
/// - You must supply a [`Pender`]. The executor will call it to notify you it has work /// - You must supply a pender function, as shown below. The executor will call it to notify you
/// to do. You must arrange for `poll()` to be called as soon as possible. /// it has work to do. You must arrange for `poll()` to be called as soon as possible.
/// - Enabling `arch-xx` features will define a pender function for you. This means that you
/// are limited to using the executors provided to you by the architecture/platform
/// implementation. If you need a different executor, you must not enable `arch-xx` features.
/// ///
/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority /// The pender can be called from *any* context: any thread, any interrupt priority
/// level, etc. It may be called synchronously from any `Executor` method call as well. /// level, etc. It may be called synchronously from any `Executor` method call as well.
/// You must deal with this correctly. /// You must deal with this correctly.
/// ///
/// In particular, you must NOT call `poll` directly from the pender callback, as this violates /// In particular, you must NOT call `poll` directly from the pender callback, as this violates
/// the requirement for `poll` to not be called reentrantly. /// the requirement for `poll` to not be called reentrantly.
///
/// The pender function must be exported with the name `__pender` and have the following signature:
///
/// ```rust
/// #[export_name = "__pender"]
/// fn pender(context: *mut ()) {
/// // schedule `poll()` to be called
/// }
/// ```
///
/// The `context` argument is a piece of arbitrary data the executor will pass to the pender.
/// You can set the `context` when calling [`Executor::new()`]. You can use it to, for example,
/// differentiate between executors, or to pass a pointer to a callback that should be called.
#[repr(transparent)] #[repr(transparent)]
pub struct Executor { pub struct Executor {
pub(crate) inner: SyncExecutor, pub(crate) inner: SyncExecutor,
@ -495,12 +488,12 @@ impl Executor {
/// Create a new executor. /// Create a new executor.
/// ///
/// When the executor has work to do, it will call the [`Pender`]. /// When the executor has work to do, it will call the pender function and pass `context` to it.
/// ///
/// See [`Executor`] docs for details on `Pender`. /// See [`Executor`] docs for details on the pender.
pub fn new(pender: Pender) -> Self { pub fn new(context: *mut ()) -> Self {
Self { Self {
inner: SyncExecutor::new(pender), inner: SyncExecutor::new(Pender(context)),
_not_sync: PhantomData, _not_sync: PhantomData,
} }
} }
@ -523,16 +516,16 @@ impl Executor {
/// This loops over all tasks that are queued to be polled (i.e. they're /// This loops over all tasks that are queued to be polled (i.e. they're
/// freshly spawned or they've been woken). Other tasks are not polled. /// freshly spawned or they've been woken). Other tasks are not polled.
/// ///
/// You must call `poll` after receiving a call to the [`Pender`]. It is OK /// You must call `poll` after receiving a call to the pender. It is OK
/// to call `poll` even when not requested by the `Pender`, but it wastes /// to call `poll` even when not requested by the pender, but it wastes
/// energy. /// energy.
/// ///
/// # Safety /// # Safety
/// ///
/// You must NOT call `poll` reentrantly on the same executor. /// You must NOT call `poll` reentrantly on the same executor.
/// ///
/// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you /// In particular, note that `poll` may call the pender synchronously. Therefore, you
/// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to /// must NOT directly call `poll()` from the pender callback. Instead, the callback has to
/// somehow schedule for `poll()` to be called later, at a time you know for sure there's /// somehow schedule for `poll()` to be called later, at a time you know for sure there's
/// no `poll()` already running. /// no `poll()` already running.
pub unsafe fn poll(&'static self) { pub unsafe fn poll(&'static self) {
@ -573,6 +566,31 @@ pub fn wake_task(task: TaskRef) {
} }
} }
/// Wake a task by `TaskRef` without calling pend.
///
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
pub fn wake_task_no_pend(task: TaskRef) {
let header = task.header();
let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
// If already scheduled, or if not started,
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
None
} else {
// Mark it as scheduled
Some(state | STATE_RUN_QUEUED)
}
});
if res.is_ok() {
// We have just marked the task as scheduled, so enqueue it.
unsafe {
let executor = header.executor.get().unwrap_unchecked();
executor.run_queue.enqueue(task);
}
}
}
#[cfg(feature = "integrated-timers")] #[cfg(feature = "integrated-timers")]
struct TimerQueue; struct TimerQueue;

View File

@ -33,7 +33,8 @@ impl<S> SpawnToken<S> {
} }
} }
pub(crate) fn new_failed() -> Self { /// Return a SpawnToken that represents a failed spawn.
pub fn new_failed() -> Self {
Self { Self {
raw_task: None, raw_task: None,
phantom: PhantomData, phantom: PhantomData,

View File

@ -23,9 +23,12 @@ log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
embedded-hal-async = { version = "=0.2.0-alpha.2" } embedded-hal-async = { version = "=1.0.0-rc.1" }
embedded-hal = { version = "0.2", features = ["unproven"] } embedded-hal = { version = "0.2", features = ["unproven"] }
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
lora-phy = { version = "1" } lora-phy = { version = "1" }
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true }
[patch.crates-io]
lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"}

View File

@ -76,7 +76,7 @@ These `embassy-net` drivers are implemented using this crate. You can look at th
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W - [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support. - [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip. - [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips.
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.

View File

@ -0,0 +1,23 @@
[package]
name = "embassy-net-enc28j60"
version = "0.1.0"
description = "embassy-net driver for the ENC28J60 ethernet chip"
keywords = ["embedded", "enc28j60", "embassy-net", "embedded-hal-async", "ethernet", "async"]
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/"
target = "thumbv7em-none-eabi"

View File

@ -0,0 +1,19 @@
# `embassy-net-enc28j60`
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the Microchip ENC28J60 Ethernet chip.
Based on [@japaric](https://github.com/japaric)'s [`enc28j60`](https://github.com/japaric/enc28j60) crate.
## Interoperability
This crate can run on any executor.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

View File

@ -0,0 +1,69 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
ERDPTL = 0x00,
ERDPTH = 0x01,
EWRPTL = 0x02,
EWRPTH = 0x03,
ETXSTL = 0x04,
ETXSTH = 0x05,
ETXNDL = 0x06,
ETXNDH = 0x07,
ERXSTL = 0x08,
ERXSTH = 0x09,
ERXNDL = 0x0a,
ERXNDH = 0x0b,
ERXRDPTL = 0x0c,
ERXRDPTH = 0x0d,
ERXWRPTL = 0x0e,
ERXWRPTH = 0x0f,
EDMASTL = 0x10,
EDMASTH = 0x11,
EDMANDL = 0x12,
EDMANDH = 0x13,
EDMADSTL = 0x14,
EDMADSTH = 0x15,
EDMACSL = 0x16,
EDMACSH = 0x17,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
pub(crate) fn is_eth_register(&self) -> bool {
match *self {
Register::ERDPTL => true,
Register::ERDPTH => true,
Register::EWRPTL => true,
Register::EWRPTH => true,
Register::ETXSTL => true,
Register::ETXSTH => true,
Register::ETXNDL => true,
Register::ETXNDH => true,
Register::ERXSTL => true,
Register::ERXSTH => true,
Register::ERXNDL => true,
Register::ERXNDH => true,
Register::ERXRDPTL => true,
Register::ERXRDPTH => true,
Register::ERXWRPTL => true,
Register::ERXWRPTH => true,
Register::EDMASTL => true,
Register::EDMASTH => true,
Register::EDMANDL => true,
Register::EDMANDH => true,
Register::EDMADSTL => true,
Register::EDMADSTH => true,
Register::EDMACSL => true,
Register::EDMACSH => true,
}
}
}
impl Into<super::Register> for Register {
fn into(self) -> super::Register {
super::Register::Bank0(self)
}
}

View File

@ -0,0 +1,84 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
EHT0 = 0x00,
EHT1 = 0x01,
EHT2 = 0x02,
EHT3 = 0x03,
EHT4 = 0x04,
EHT5 = 0x05,
EHT6 = 0x06,
EHT7 = 0x07,
EPMM0 = 0x08,
EPMM1 = 0x09,
EPMM2 = 0x0a,
EPMM3 = 0x0b,
EPMM4 = 0x0c,
EPMM5 = 0x0d,
EPMM6 = 0x0e,
EPMM7 = 0x0f,
EPMCSL = 0x10,
EPMCSH = 0x11,
EPMOL = 0x14,
EPMOH = 0x15,
ERXFCON = 0x18,
EPKTCNT = 0x19,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
pub(crate) fn is_eth_register(&self) -> bool {
match *self {
Register::EHT0 => true,
Register::EHT1 => true,
Register::EHT2 => true,
Register::EHT3 => true,
Register::EHT4 => true,
Register::EHT5 => true,
Register::EHT6 => true,
Register::EHT7 => true,
Register::EPMM0 => true,
Register::EPMM1 => true,
Register::EPMM2 => true,
Register::EPMM3 => true,
Register::EPMM4 => true,
Register::EPMM5 => true,
Register::EPMM6 => true,
Register::EPMM7 => true,
Register::EPMCSL => true,
Register::EPMCSH => true,
Register::EPMOL => true,
Register::EPMOH => true,
Register::ERXFCON => true,
Register::EPKTCNT => true,
}
}
}
impl Into<super::Register> for Register {
fn into(self) -> super::Register {
super::Register::Bank1(self)
}
}
register!(ERXFCON, 0b1010_0001, u8, {
#[doc = "Broadcast Filter Enable bit"]
bcen @ 0,
#[doc = "Multicast Filter Enable bit"]
mcen @ 1,
#[doc = "Hash Table Filter Enable bit"]
hten @ 2,
#[doc = "Magic Packet™ Filter Enable bit"]
mpen @ 3,
#[doc = "Pattern Match Filter Enable bit"]
pmen @ 4,
#[doc = "Post-Filter CRC Check Enable bit"]
crcen @ 5,
#[doc = "AND/OR Filter Select bit"]
andor @ 6,
#[doc = "Unicast Filter Enable bit"]
ucen @ 7,
});

View File

@ -0,0 +1,86 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
MACON1 = 0x00,
MACON3 = 0x02,
MACON4 = 0x03,
MABBIPG = 0x04,
MAIPGL = 0x06,
MAIPGH = 0x07,
MACLCON1 = 0x08,
MACLCON2 = 0x09,
MAMXFLL = 0x0a,
MAMXFLH = 0x0b,
MICMD = 0x12,
MIREGADR = 0x14,
MIWRL = 0x16,
MIWRH = 0x17,
MIRDL = 0x18,
MIRDH = 0x19,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
pub(crate) fn is_eth_register(&self) -> bool {
match *self {
Register::MACON1 => false,
Register::MACON3 => false,
Register::MACON4 => false,
Register::MABBIPG => false,
Register::MAIPGL => false,
Register::MAIPGH => false,
Register::MACLCON1 => false,
Register::MACLCON2 => false,
Register::MAMXFLL => false,
Register::MAMXFLH => false,
Register::MICMD => false,
Register::MIREGADR => false,
Register::MIWRL => false,
Register::MIWRH => false,
Register::MIRDL => false,
Register::MIRDH => false,
}
}
}
impl Into<super::Register> for Register {
fn into(self) -> super::Register {
super::Register::Bank2(self)
}
}
register!(MACON1, 0, u8, {
#[doc = "Enable packets to be received by the MAC"]
marxen @ 0,
#[doc = "Control frames will be discarded after being processed by the MAC"]
passall @ 1,
#[doc = "Inhibit transmissions when pause control frames are received"]
rxpaus @ 2,
#[doc = "Allow the MAC to transmit pause control frames"]
txpaus @ 3,
});
register!(MACON3, 0, u8, {
#[doc = "MAC will operate in Full-Duplex mode"]
fuldpx @ 0,
#[doc = "The type/length field of transmitted and received frames will be checked"]
frmlnen @ 1,
#[doc = "Frames bigger than MAMXFL will be aborted when transmitted or received"]
hfrmen @ 2,
#[doc = "No proprietary header is present"]
phdren @ 3,
#[doc = "MAC will append a valid CRC to all frames transmitted regardless of PADCFG bit"]
txcrcen @ 4,
#[doc = "All short frames will be zero-padded to 64 bytes and a valid CRC will then be appended"]
padcfg @ 5..7,
});
register!(MICMD, 0, u8, {
#[doc = "MII Read Enable bit"]
miird @ 0,
#[doc = "MII Scan Enable bit"]
miiscan @ 1,
});

View File

@ -0,0 +1,53 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
MAADR5 = 0x00,
MAADR6 = 0x01,
MAADR3 = 0x02,
MAADR4 = 0x03,
MAADR1 = 0x04,
MAADR2 = 0x05,
EBSTSD = 0x06,
EBSTCON = 0x07,
EBSTCSL = 0x08,
EBSTCSH = 0x09,
MISTAT = 0x0a,
EREVID = 0x12,
ECOCON = 0x15,
EFLOCON = 0x17,
EPAUSL = 0x18,
EPAUSH = 0x19,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
pub(crate) fn is_eth_register(&self) -> bool {
match *self {
Register::MAADR5 => false,
Register::MAADR6 => false,
Register::MAADR3 => false,
Register::MAADR4 => false,
Register::MAADR1 => false,
Register::MAADR2 => false,
Register::EBSTSD => true,
Register::EBSTCON => true,
Register::EBSTCSL => true,
Register::EBSTCSH => true,
Register::MISTAT => false,
Register::EREVID => true,
Register::ECOCON => true,
Register::EFLOCON => true,
Register::EPAUSL => true,
Register::EPAUSH => true,
}
}
}
impl Into<super::Register> for Register {
fn into(self) -> super::Register {
super::Register::Bank3(self)
}
}

View File

@ -0,0 +1,106 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
ECON1 = 0x1f,
ECON2 = 0x1e,
EIE = 0x1b,
EIR = 0x1c,
ESTAT = 0x1d,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
pub(crate) fn is_eth_register(&self) -> bool {
match *self {
Register::ECON1 => true,
Register::ECON2 => true,
Register::EIE => true,
Register::EIR => true,
Register::ESTAT => true,
}
}
}
impl Into<super::Register> for Register {
fn into(self) -> super::Register {
super::Register::Common(self)
}
}
register!(EIE, 0, u8, {
#[doc = "Receive Error Interrupt Enable bit"]
rxerie @ 0,
#[doc = "Transmit Error Interrupt Enable bit"]
txerie @ 1,
#[doc = "Transmit Enable bit"]
txie @ 3,
#[doc = "Link Status Change Interrupt Enable bit"]
linkie @ 4,
#[doc = "DMA Interrupt Enable bit"]
dmaie @ 5,
#[doc = "Receive Packet Pending Interrupt Enable bit"]
pktie @ 6,
#[doc = "Global INT Interrupt Enable bit"]
intie @ 7,
});
register!(EIR, 0, u8, {
#[doc = "Receive Error Interrupt Flag bit"]
rxerif @ 0,
#[doc = "Transmit Error Interrupt Flag bit"]
txerif @ 1,
#[doc = "Transmit Interrupt Flag bit"]
txif @ 3,
#[doc = "Link Change Interrupt Flag bit"]
linkif @ 4,
#[doc = "DMA Interrupt Flag bit"]
dmaif @ 5,
#[doc = "Receive Packet Pending Interrupt Flag bit"]
pktif @ 6,
});
register!(ESTAT, 0, u8, {
#[doc = "Clock Ready bit"]
clkrdy @ 0,
#[doc = "Transmit Abort Error bit"]
txabrt @ 1,
#[doc = "Receive Busy bit"]
rxbusy @ 2,
#[doc = "Late Collision Error bit"]
latecol @ 4,
#[doc = "Ethernet Buffer Error Status bit"]
bufer @ 6,
#[doc = "INT Interrupt Flag bit"]
int @ 7,
});
register!(ECON2, 0b1000_0000, u8, {
#[doc = "Voltage Regulator Power Save Enable bit"]
vrps @ 3,
#[doc = "Power Save Enable bit"]
pwrsv @ 5,
#[doc = "Packet Decrement bit"]
pktdec @ 6,
#[doc = "Automatic Buffer Pointer Increment Enable bit"]
autoinc @ 7,
});
register!(ECON1, 0, u8, {
#[doc = "Bank Select bits"]
bsel @ 0..1,
#[doc = "Receive Enable bi"]
rxen @ 2,
#[doc = "Transmit Request to Send bit"]
txrts @ 3,
#[doc = "DMA Checksum Enable bit"]
csumen @ 4,
#[doc = "DMA Start and Busy Status bit"]
dmast @ 5,
#[doc = "Receive Logic Reset bit"]
rxrst @ 6,
#[doc = "Transmit Logic Reset bit"]
txrst @ 7,
});

View File

@ -0,0 +1,225 @@
#![macro_use]
#![allow(unused_macros)]
#[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 _ = ($( & $x ),*);
}
};
}
macro_rules! debug {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::debug!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::debug!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! info {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::info!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::info!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! warn {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::warn!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::warn!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! error {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::error!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::error!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[cfg(feature = "defmt")]
macro_rules! unwrap {
($($x:tt)*) => {
::defmt::unwrap!($($x)*)
};
}
#[cfg(not(feature = "defmt"))]
macro_rules! unwrap {
($arg:expr) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
}
}
};
($arg:expr, $($msg:expr),+ $(,)? ) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;
pub trait Try {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
}
impl<T> Try for Option<T> {
type Ok = T;
type Error = NoneError;
#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}
}
impl<T, E> Try for Result<T, E> {
type Ok = T;
type Error = E;
#[inline]
fn into_result(self) -> Self {
self
}
}

View File

@ -0,0 +1,30 @@
register!(RxStatus, 0, u32, {
#[doc = "Indicates length of the received frame"]
byte_count @ 0..15,
#[doc = "Indicates a packet over 50,000 bit times occurred or that a packet was dropped since the last receive"]
long_event @ 16,
#[doc = "Indicates that at some time since the last receive, a carrier event was detected"]
carrier_event @ 18,
#[doc = "Indicates that frame CRC field value does not match the CRC calculated by the MAC"]
crc_error @ 20,
#[doc = "Indicates that frame length field value in the packet does not match the actual data byte length and specifies a valid length"]
length_check_error @ 21,
#[doc = "Indicates that frame type/length field was larger than 1500 bytes (type field)"]
length_out_of_range @ 22,
#[doc = "Indicates that at the packet had a valid CRC and no symbol errors"]
received_ok @ 23,
#[doc = "Indicates packet received had a valid Multicast address"]
multicast @ 24,
#[doc = "Indicates packet received had a valid Broadcast address."]
broadcast @ 25,
#[doc = "Indicates that after the end of this packet, an additional 1 to 7 bits were received"]
dribble_nibble @ 26,
#[doc = "Current frame was recognized as a control frame for having a valid type/length designating it as a control frame"]
receive_control_frame @ 27,
#[doc = "Current frame was recognized as a control frame containing a valid pause frame opcode and a valid destination address"]
receive_pause_control_frame @ 28,
#[doc = "Current frame was recognized as a control frame but it contained an unknown opcode"]
receive_unknown_opcode @ 29,
#[doc = "Current frame was recognized as a VLAN tagged frame"]
receive_vlan_type_detected @ 30,
});

View File

@ -0,0 +1,717 @@
#![no_std]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
// must go first.
mod fmt;
#[macro_use]
mod macros;
mod bank0;
mod bank1;
mod bank2;
mod bank3;
mod common;
mod header;
mod phy;
mod traits;
use core::cmp;
use core::convert::TryInto;
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium};
use embassy_time::Duration;
use embedded_hal::digital::OutputPin;
use embedded_hal::spi::{Operation, SpiDevice};
use traits::U16Ext;
// Total buffer size (see section 3.2)
const BUF_SZ: u16 = 8 * 1024;
// Maximum frame length
const MAX_FRAME_LENGTH: u16 = 1518; // value recommended in the data sheet
// Size of the Frame check sequence (32-bit CRC)
const CRC_SZ: u16 = 4;
// define the boundaries of the TX and RX buffers
// to workaround errata #5 we do the opposite of what section 6.1 of the data sheet
// says: we place the RX buffer at address 0 and the TX buffer after it
const RXST: u16 = 0x0000;
const RXND: u16 = 0x19ff;
const TXST: u16 = 0x1a00;
const _TXND: u16 = 0x1fff;
const MTU: usize = 1514; // 1500 IP + 14 ethernet header
/// ENC28J60 embassy-net driver
pub struct Enc28j60<S, O> {
mac_addr: [u8; 6],
spi: S,
rst: Option<O>,
bank: Bank,
// address of the next packet in buffer memory
next_packet: u16,
}
impl<S, O> Enc28j60<S, O>
where
S: SpiDevice,
O: OutputPin,
{
/// Create a new ENC28J60 driver instance.
///
/// The RST pin is optional. If None, reset will be done with a SPI
/// soft reset command, instead of via the RST pin.
pub fn new(spi: S, rst: Option<O>, mac_addr: [u8; 6]) -> Self {
let mut res = Self {
mac_addr,
spi,
rst,
bank: Bank::Bank0,
next_packet: RXST,
};
res.init();
res
}
fn init(&mut self) {
if let Some(rst) = &mut self.rst {
rst.set_low().unwrap();
embassy_time::block_for(Duration::from_millis(5));
rst.set_high().unwrap();
embassy_time::block_for(Duration::from_millis(5));
} else {
embassy_time::block_for(Duration::from_millis(5));
self.soft_reset();
embassy_time::block_for(Duration::from_millis(5));
}
debug!(
"enc28j60: erevid {=u8:x}",
self.read_control_register(bank3::Register::EREVID)
);
debug!("enc28j60: waiting for clk");
while common::ESTAT(self.read_control_register(common::Register::ESTAT)).clkrdy() == 0 {}
debug!("enc28j60: clk ok");
if self.read_control_register(bank3::Register::EREVID) == 0 {
panic!("ErevidIsZero");
}
// disable CLKOUT output
self.write_control_register(bank3::Register::ECOCON, 0);
self.init_rx();
// TX start
// "It is recommended that an even address be used for ETXST"
debug_assert_eq!(TXST % 2, 0);
self.write_control_register(bank0::Register::ETXSTL, TXST.low());
self.write_control_register(bank0::Register::ETXSTH, TXST.high());
// TX end is set in `transmit`
// MAC initialization (see section 6.5)
// 1. Set the MARXEN bit in MACON1 to enable the MAC to receive frames.
self.write_control_register(
bank2::Register::MACON1,
bank2::MACON1::default().marxen(1).passall(0).rxpaus(1).txpaus(1).bits(),
);
// 2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3.
self.write_control_register(
bank2::Register::MACON3,
bank2::MACON3::default().frmlnen(1).txcrcen(1).padcfg(0b001).bits(),
);
// 4. Program the MAMXFL registers with the maximum frame length to be permitted to be
// received or transmitted
self.write_control_register(bank2::Register::MAMXFLL, MAX_FRAME_LENGTH.low());
self.write_control_register(bank2::Register::MAMXFLH, MAX_FRAME_LENGTH.high());
// 5. Configure the Back-to-Back Inter-Packet Gap register, MABBIPG.
// Use recommended value of 0x12
self.write_control_register(bank2::Register::MABBIPG, 0x12);
// 6. Configure the Non-Back-to-Back Inter-Packet Gap register low byte, MAIPGL.
// Use recommended value of 0x12
self.write_control_register(bank2::Register::MAIPGL, 0x12);
self.write_control_register(bank2::Register::MAIPGH, 0x0c);
// 9. Program the local MAC address into the MAADR1:MAADR6 registers
self.write_control_register(bank3::Register::MAADR1, self.mac_addr[0]);
self.write_control_register(bank3::Register::MAADR2, self.mac_addr[1]);
self.write_control_register(bank3::Register::MAADR3, self.mac_addr[2]);
self.write_control_register(bank3::Register::MAADR4, self.mac_addr[3]);
self.write_control_register(bank3::Register::MAADR5, self.mac_addr[4]);
self.write_control_register(bank3::Register::MAADR6, self.mac_addr[5]);
// Set the PHCON2.HDLDIS bit to prevent automatic loopback of the data which is transmitted
self.write_phy_register(phy::Register::PHCON2, phy::PHCON2::default().hdldis(1).bits());
// Globally enable interrupts
//self.bit_field_set(common::Register::EIE, common::EIE::mask().intie());
// Set the per packet control byte; we'll always use the value 0
self.write_buffer_memory(Some(TXST), &mut [0]);
// Enable reception
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
}
fn init_rx(&mut self) {
// RX start
// "It is recommended that the ERXST Pointer be programmed with an even address"
self.write_control_register(bank0::Register::ERXSTL, RXST.low());
self.write_control_register(bank0::Register::ERXSTH, RXST.high());
// RX read pointer
// NOTE Errata #14 so we are using an *odd* address here instead of ERXST
self.write_control_register(bank0::Register::ERXRDPTL, RXND.low());
self.write_control_register(bank0::Register::ERXRDPTH, RXND.high());
// RX end
self.write_control_register(bank0::Register::ERXNDL, RXND.low());
self.write_control_register(bank0::Register::ERXNDH, RXND.high());
// decrease the packet count to 0
while self.read_control_register(bank1::Register::EPKTCNT) != 0 {
self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
}
self.next_packet = RXST;
}
fn reset_rx(&mut self) {
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxrst());
self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().rxrst());
self.init_rx();
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
}
/// Flushes the transmit buffer, ensuring all pending transmissions have completed
/// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always
/// return `None` on subsequent invocations
pub fn receive<'a>(&mut self, buf: &'a mut [u8]) -> Option<&'a mut [u8]> {
if self.pending_packets() == 0 {
// Errata #6: we can't rely on PKTIF so we check PKTCNT
return None;
}
let curr_packet = self.next_packet;
// read out the first 6 bytes
let mut temp_buf = [0; 6];
self.read_buffer_memory(Some(curr_packet), &mut temp_buf);
// next packet pointer
let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]);
// status vector
let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap()));
let len_with_crc = status.byte_count() as u16;
if len_with_crc < CRC_SZ || len_with_crc > 1600 || next_packet > RXND {
warn!("RX buffer corrupted, resetting RX logic to recover...");
self.reset_rx();
return None;
}
let len = len_with_crc - CRC_SZ;
self.read_buffer_memory(None, &mut buf[..len as usize]);
// update ERXRDPT
// due to Errata #14 we must write an odd address to ERXRDPT
// we know that ERXST = 0, that ERXND is odd and that next_packet is even
let rxrdpt = if self.next_packet < 1 || self.next_packet > RXND + 1 {
RXND
} else {
self.next_packet - 1
};
// "To move ERXRDPT, the host controller must write to ERXRDPTL first."
self.write_control_register(bank0::Register::ERXRDPTL, rxrdpt.low());
self.write_control_register(bank0::Register::ERXRDPTH, rxrdpt.high());
// decrease the packet count
self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
self.next_packet = next_packet;
Some(&mut buf[..len as usize])
}
fn wait_tx_ready(&mut self) {
for _ in 0u32..10000 {
if common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 0 {
return;
}
}
// work around errata #12 by resetting the transmit logic before every new
// transmission
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrst());
self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().txrst());
//self.bit_field_clear(common::Register::EIR, {
// let mask = common::EIR::mask();
// mask.txerif() | mask.txif()
//});
}
/// Starts the transmission of `bytes`
///
/// It's up to the caller to ensure that `bytes` is a valid Ethernet frame. The interface will
/// take care of appending a (4 byte) CRC to the frame and of padding the frame to the minimum
/// size allowed by the Ethernet specification (64 bytes, or 46 bytes of payload).
///
/// NOTE This method will flush any previous transmission that's in progress
///
/// # Panics
///
/// If `bytes` length is greater than 1514, the maximum frame length allowed by the interface,
/// or greater than the transmit buffer
pub fn transmit(&mut self, bytes: &[u8]) {
assert!(bytes.len() <= self.mtu() as usize);
self.wait_tx_ready();
// NOTE the plus one is to not overwrite the per packet control byte
let wrpt = TXST + 1;
// 1. ETXST was set during initialization
// 2. write the frame to the IC memory
self.write_buffer_memory(Some(wrpt), bytes);
let txnd = wrpt + bytes.len() as u16 - 1;
// 3. Set the end address of the transmit buffer
self.write_control_register(bank0::Register::ETXNDL, txnd.low());
self.write_control_register(bank0::Register::ETXNDH, txnd.high());
// 4. reset interrupt flag
//self.bit_field_clear(common::Register::EIR, common::EIR::mask().txif());
// 5. start transmission
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrts());
// Wait until transmission finishes
//while common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 1 {}
/*
// read the transmit status vector
let mut tx_stat = [0; 7];
self.read_buffer_memory(None, &mut tx_stat);
let stat = common::ESTAT(self.read_control_register(common::Register::ESTAT));
if stat.txabrt() == 1 {
// work around errata #12 by reading the transmit status vector
if stat.latecol() == 1 || (tx_stat[2] & (1 << 5)) != 0 {
panic!("LateCollision")
} else {
panic!("TransmitAbort")
}
}*/
}
/// Get whether the link is up
pub fn is_link_up(&mut self) -> bool {
let bits = self.read_phy_register(phy::Register::PHSTAT2);
phy::PHSTAT2(bits).lstat() == 1
}
/// Returns the interface Maximum Transmission Unit (MTU)
///
/// The value returned by this function will never exceed 1514 bytes. The actual value depends
/// on the memory assigned to the transmission buffer when initializing the device
pub fn mtu(&self) -> u16 {
cmp::min(BUF_SZ - RXND - 1, MAX_FRAME_LENGTH - CRC_SZ)
}
/* Miscellaneous */
/// Returns the number of packets that have been received but have not been processed yet
pub fn pending_packets(&mut self) -> u8 {
self.read_control_register(bank1::Register::EPKTCNT)
}
/// Adjusts the receive filter to *accept* these packet types
pub fn accept(&mut self, packets: &[Packet]) {
let mask = bank1::ERXFCON::mask();
let mut val = 0;
for packet in packets {
match packet {
Packet::Broadcast => val |= mask.bcen(),
Packet::Multicast => val |= mask.mcen(),
Packet::Unicast => val |= mask.ucen(),
}
}
self.bit_field_set(bank1::Register::ERXFCON, val)
}
/// Adjusts the receive filter to *ignore* these packet types
pub fn ignore(&mut self, packets: &[Packet]) {
let mask = bank1::ERXFCON::mask();
let mut val = 0;
for packet in packets {
match packet {
Packet::Broadcast => val |= mask.bcen(),
Packet::Multicast => val |= mask.mcen(),
Packet::Unicast => val |= mask.ucen(),
}
}
self.bit_field_clear(bank1::Register::ERXFCON, val)
}
/* Private */
/* Read */
fn read_control_register<R>(&mut self, register: R) -> u8
where
R: Into<Register>,
{
self._read_control_register(register.into())
}
fn _read_control_register(&mut self, register: Register) -> u8 {
self.change_bank(register);
if register.is_eth_register() {
let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0];
self.spi.transfer_in_place(&mut buffer).unwrap();
buffer[1]
} else {
// MAC, MII regs need a dummy byte.
let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0, 0];
self.spi.transfer_in_place(&mut buffer).unwrap();
buffer[2]
}
}
fn read_phy_register(&mut self, register: phy::Register) -> u16 {
// set PHY register address
self.write_control_register(bank2::Register::MIREGADR, register.addr());
// start read operation
self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(1).bits());
// wait until the read operation finishes
while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits());
let l = self.read_control_register(bank2::Register::MIRDL);
let h = self.read_control_register(bank2::Register::MIRDH);
(l as u16) | (h as u16) << 8
}
/* Write */
fn _write_control_register(&mut self, register: Register, value: u8) {
self.change_bank(register);
let buffer = [Instruction::WCR.opcode() | register.addr(), value];
self.spi.write(&buffer).unwrap();
}
fn write_control_register<R>(&mut self, register: R, value: u8)
where
R: Into<Register>,
{
self._write_control_register(register.into(), value)
}
fn write_phy_register(&mut self, register: phy::Register, value: u16) {
// set PHY register address
self.write_control_register(bank2::Register::MIREGADR, register.addr());
self.write_control_register(bank2::Register::MIWRL, (value & 0xff) as u8);
// this starts the write operation
self.write_control_register(bank2::Register::MIWRH, (value >> 8) as u8);
// wait until the write operation finishes
while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
}
/* RMW */
fn modify_control_register<R, F>(&mut self, register: R, f: F)
where
F: FnOnce(u8) -> u8,
R: Into<Register>,
{
self._modify_control_register(register.into(), f)
}
fn _modify_control_register<F>(&mut self, register: Register, f: F)
where
F: FnOnce(u8) -> u8,
{
let r = self._read_control_register(register);
self._write_control_register(register, f(r))
}
/* Auxiliary */
fn change_bank(&mut self, register: Register) {
let bank = register.bank();
if let Some(bank) = bank {
if self.bank == bank {
// already on the register bank
return;
}
// change bank
self.bank = bank;
match bank {
Bank::Bank0 => self.bit_field_clear(common::Register::ECON1, 0b11),
Bank::Bank1 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b01),
Bank::Bank2 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b10),
Bank::Bank3 => self.bit_field_set(common::Register::ECON1, 0b11),
}
} else {
// common register
}
}
/* Primitive operations */
fn bit_field_clear<R>(&mut self, register: R, mask: u8)
where
R: Into<Register>,
{
self._bit_field_clear(register.into(), mask)
}
fn _bit_field_clear(&mut self, register: Register, mask: u8) {
debug_assert!(register.is_eth_register());
self.change_bank(register);
self.spi
.write(&[Instruction::BFC.opcode() | register.addr(), mask])
.unwrap();
}
fn bit_field_set<R>(&mut self, register: R, mask: u8)
where
R: Into<Register>,
{
self._bit_field_set(register.into(), mask)
}
fn _bit_field_set(&mut self, register: Register, mask: u8) {
debug_assert!(register.is_eth_register());
self.change_bank(register);
self.spi
.write(&[Instruction::BFS.opcode() | register.addr(), mask])
.unwrap();
}
fn read_buffer_memory(&mut self, addr: Option<u16>, buf: &mut [u8]) {
if let Some(addr) = addr {
self.write_control_register(bank0::Register::ERDPTL, addr.low());
self.write_control_register(bank0::Register::ERDPTH, addr.high());
}
self.spi
.transaction(&mut [Operation::Write(&[Instruction::RBM.opcode()]), Operation::Read(buf)])
.unwrap();
}
fn soft_reset(&mut self) {
self.spi.write(&[Instruction::SRC.opcode()]).unwrap();
}
fn write_buffer_memory(&mut self, addr: Option<u16>, buffer: &[u8]) {
if let Some(addr) = addr {
self.write_control_register(bank0::Register::EWRPTL, addr.low());
self.write_control_register(bank0::Register::EWRPTH, addr.high());
}
self.spi
.transaction(&mut [Operation::Write(&[Instruction::WBM.opcode()]), Operation::Write(buffer)])
.unwrap();
}
}
#[derive(Clone, Copy, PartialEq)]
enum Bank {
Bank0,
Bank1,
Bank2,
Bank3,
}
#[derive(Clone, Copy)]
enum Instruction {
/// Read Control Register
RCR = 0b000_00000,
/// Read Buffer Memory
RBM = 0b001_11010,
/// Write Control Register
WCR = 0b010_00000,
/// Write Buffer Memory
WBM = 0b011_11010,
/// Bit Field Set
BFS = 0b100_00000,
/// Bit Field Clear
BFC = 0b101_00000,
/// System Reset Command
SRC = 0b111_11111,
}
impl Instruction {
fn opcode(&self) -> u8 {
*self as u8
}
}
#[derive(Clone, Copy)]
enum Register {
Bank0(bank0::Register),
Bank1(bank1::Register),
Bank2(bank2::Register),
Bank3(bank3::Register),
Common(common::Register),
}
impl Register {
fn addr(&self) -> u8 {
match *self {
Register::Bank0(r) => r.addr(),
Register::Bank1(r) => r.addr(),
Register::Bank2(r) => r.addr(),
Register::Bank3(r) => r.addr(),
Register::Common(r) => r.addr(),
}
}
fn bank(&self) -> Option<Bank> {
Some(match *self {
Register::Bank0(_) => Bank::Bank0,
Register::Bank1(_) => Bank::Bank1,
Register::Bank2(_) => Bank::Bank2,
Register::Bank3(_) => Bank::Bank3,
Register::Common(_) => return None,
})
}
fn is_eth_register(&self) -> bool {
match *self {
Register::Bank0(r) => r.is_eth_register(),
Register::Bank1(r) => r.is_eth_register(),
Register::Bank2(r) => r.is_eth_register(),
Register::Bank3(r) => r.is_eth_register(),
Register::Common(r) => r.is_eth_register(),
}
}
}
/// Packet type, used to configure receive filters
#[non_exhaustive]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Packet {
/// Broadcast packets
Broadcast,
/// Multicast packets
Multicast,
/// Unicast packets
Unicast,
}
static mut TX_BUF: [u8; MTU] = [0; MTU];
static mut RX_BUF: [u8; MTU] = [0; MTU];
impl<S, O> embassy_net_driver::Driver for Enc28j60<S, O>
where
S: SpiDevice,
O: OutputPin,
{
type RxToken<'a> = RxToken<'a>
where
Self: 'a;
type TxToken<'a> = TxToken<'a, S, O>
where
Self: 'a;
fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let rx_buf = unsafe { &mut RX_BUF };
let tx_buf = unsafe { &mut TX_BUF };
if let Some(pkt) = self.receive(rx_buf) {
let n = pkt.len();
Some((RxToken { buf: &mut pkt[..n] }, TxToken { buf: tx_buf, eth: self }))
} else {
cx.waker().wake_by_ref();
None
}
}
fn transmit(&mut self, _cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
let tx_buf = unsafe { &mut TX_BUF };
Some(TxToken { buf: tx_buf, eth: self })
}
fn link_state(&mut self, cx: &mut core::task::Context) -> LinkState {
cx.waker().wake_by_ref();
match self.is_link_up() {
true => LinkState::Up,
false => LinkState::Down,
}
}
fn capabilities(&self) -> Capabilities {
let mut caps = Capabilities::default();
caps.max_transmission_unit = MTU;
caps.medium = Medium::Ethernet;
caps
}
fn hardware_address(&self) -> HardwareAddress {
HardwareAddress::Ethernet(self.mac_addr)
}
}
/// embassy-net RX token.
pub struct RxToken<'a> {
buf: &'a mut [u8],
}
impl<'a> embassy_net_driver::RxToken for RxToken<'a> {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
f(self.buf)
}
}
/// embassy-net TX token.
pub struct TxToken<'a, S, O>
where
S: SpiDevice,
O: OutputPin,
{
eth: &'a mut Enc28j60<S, O>,
buf: &'a mut [u8],
}
impl<'a, S, O> embassy_net_driver::TxToken for TxToken<'a, S, O>
where
S: SpiDevice,
O: OutputPin,
{
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
assert!(len <= self.buf.len());
let r = f(&mut self.buf[..len]);
self.eth.transmit(&self.buf[..len]);
r
}
}

View File

@ -0,0 +1,89 @@
macro_rules! register {
($REGISTER:ident, $reset_value:expr, $uxx:ty, {
$(#[$($attr:tt)*] $bitfield:ident @ $range:expr,)+
}) => {
#[derive(Clone, Copy)]
pub(crate) struct $REGISTER<MODE> {
bits: $uxx,
_mode: ::core::marker::PhantomData<MODE>,
}
impl $REGISTER<super::traits::Mask> {
#[allow(dead_code)]
pub(crate) fn mask() -> $REGISTER<super::traits::Mask> {
$REGISTER { bits: 0, _mode: ::core::marker::PhantomData }
}
$(
#[allow(dead_code)]
pub(crate) fn $bitfield(&self) -> $uxx {
use super::traits::OffsetSize;
let size = $range.size();
let offset = $range.offset();
((1 << size) - 1) << offset
}
)+
}
impl ::core::default::Default for $REGISTER<super::traits::W> {
fn default() -> Self {
$REGISTER { bits: $reset_value, _mode: ::core::marker::PhantomData }
}
}
#[allow(non_snake_case)]
#[allow(dead_code)]
pub(crate) fn $REGISTER(bits: $uxx) -> $REGISTER<super::traits::R> {
$REGISTER { bits, _mode: ::core::marker::PhantomData }
}
impl $REGISTER<super::traits::R> {
#[allow(dead_code)]
pub(crate) fn modify(self) -> $REGISTER<super::traits::W> {
$REGISTER { bits: self.bits, _mode: ::core::marker::PhantomData }
}
$(
#[$($attr)*]
#[allow(dead_code)]
pub(crate) fn $bitfield(&self) -> $uxx {
use super::traits::OffsetSize;
let offset = $range.offset();
let size = $range.size();
let mask = (1 << size) - 1;
(self.bits >> offset) & mask
}
)+
}
impl $REGISTER<super::traits::W> {
#[allow(dead_code)]
pub(crate) fn bits(self) -> $uxx {
self.bits
}
$(
#[$($attr)*]
#[allow(dead_code)]
pub(crate) fn $bitfield(&mut self, mut bits: $uxx) -> &mut Self {
use super::traits::OffsetSize;
let offset = $range.offset();
let size = $range.size();
let mask = (1 << size) - 1;
debug_assert!(bits <= mask);
bits &= mask;
self.bits &= !(mask << offset);
self.bits |= bits << offset;
self
}
)+
}
}
}

View File

@ -0,0 +1,35 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
PHCON1 = 0x00,
PHSTAT1 = 0x01,
PHID1 = 0x02,
PHID2 = 0x03,
PHCON2 = 0x10,
PHSTAT2 = 0x11,
PHIE = 0x12,
PHIR = 0x13,
PHLCON = 0x14,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
}
register!(PHCON2, 0, u16, {
#[doc = "PHY Half-Duplex Loopback Disable bit"]
hdldis @ 8,
#[doc = "Jabber Correction Disable bit"]
jabber @ 10,
#[doc = "Twisted-Pair Transmitter Disable bit"]
txdis @ 13,
#[doc = "PHY Force Linkup bit"]
frclnk @ 14,
});
register!(PHSTAT2, 0, u16, {
#[doc = "Link Status bit"]
lstat @ 10,
});

View File

@ -0,0 +1,57 @@
use core::ops::Range;
pub(crate) trait OffsetSize {
fn offset(self) -> u8;
fn size(self) -> u8;
}
impl OffsetSize for u8 {
fn offset(self) -> u8 {
self
}
fn size(self) -> u8 {
1
}
}
impl OffsetSize for Range<u8> {
fn offset(self) -> u8 {
self.start
}
fn size(self) -> u8 {
self.end - self.start
}
}
pub(crate) trait U16Ext {
fn from_parts(low: u8, high: u8) -> Self;
fn low(self) -> u8;
fn high(self) -> u8;
}
impl U16Ext for u16 {
fn from_parts(low: u8, high: u8) -> u16 {
((high as u16) << 8) + low as u16
}
fn low(self) -> u8 {
(self & 0xff) as u8
}
fn high(self) -> u8 {
(self >> 8) as u8
}
}
#[derive(Clone, Copy)]
pub struct Mask;
#[derive(Clone, Copy)]
pub struct R;
#[derive(Clone, Copy)]
pub struct W;

View File

@ -12,8 +12,8 @@ embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embedded-hal = { version = "1.0.0-alpha.11" } embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=0.2.0-alpha.2" } embedded-hal-async = { version = "=1.0.0-rc.1" }
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }

View File

@ -5,9 +5,12 @@ use heapless::String;
use crate::ioctl::Shared; use crate::ioctl::Shared;
use crate::proto::{self, CtrlMsg}; use crate::proto::{self, CtrlMsg};
#[derive(Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Error { #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub status: u32, pub enum Error {
Failed(u32),
Timeout,
Internal,
} }
pub struct Control<'a> { pub struct Control<'a> {
@ -23,58 +26,78 @@ enum WifiMode {
ApSta = 3, ApSta = 3,
} }
macro_rules! ioctl {
($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => {
let mut msg = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::$req_variant as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::$req_variant($req)),
};
$self.ioctl(&mut msg).await?;
let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else {
warn!("unexpected response variant");
return Err(Error::Internal);
};
if $resp.resp != 0 {
return Err(Error::Failed($resp.resp));
}
};
}
impl<'a> Control<'a> { impl<'a> Control<'a> {
pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self { pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self {
Self { state_ch, shared } Self { state_ch, shared }
} }
pub async fn init(&mut self) { pub async fn init(&mut self) -> Result<(), Error> {
debug!("wait for init event..."); debug!("wait for init event...");
self.shared.init_wait().await; self.shared.init_wait().await;
debug!("set wifi mode"); debug!("set heartbeat");
self.set_wifi_mode(WifiMode::Sta as _).await; self.set_heartbeat(10).await?;
let mac_addr = self.get_mac_addr().await; debug!("set wifi mode");
self.set_wifi_mode(WifiMode::Sta as _).await?;
let mac_addr = self.get_mac_addr().await?;
debug!("mac addr: {:02x}", mac_addr); debug!("mac addr: {:02x}", mac_addr);
self.state_ch.set_ethernet_address(mac_addr); self.state_ch.set_ethernet_address(mac_addr);
Ok(())
} }
pub async fn join(&mut self, ssid: &str, password: &str) { pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
let req = proto::CtrlMsg { let req = proto::CtrlMsgReqConnectAp {
msg_id: proto::CtrlMsgId::ReqConnectAp as _, ssid: String::from(ssid),
msg_type: proto::CtrlMsgType::Req as _, pwd: String::from(password),
payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp { bssid: String::new(),
ssid: String::from(ssid), listen_interval: 3,
pwd: String::from(password), is_wpa3_supported: false,
bssid: String::new(),
listen_interval: 3,
is_wpa3_supported: false,
})),
}; };
let resp = self.ioctl(req).await; ioctl!(self, ReqConnectAp, RespConnectAp, req, resp);
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
assert_eq!(resp.resp, 0);
self.state_ch.set_link_state(LinkState::Up); self.state_ch.set_link_state(LinkState::Up);
Ok(())
} }
async fn get_mac_addr(&mut self) -> [u8; 6] { pub async fn disconnect(&mut self) -> Result<(), Error> {
let req = proto::CtrlMsg { let req = proto::CtrlMsgReqGetStatus {};
msg_id: proto::CtrlMsgId::ReqGetMacAddress as _, ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp);
msg_type: proto::CtrlMsgType::Req as _, self.state_ch.set_link_state(LinkState::Up);
payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress( Ok(())
proto::CtrlMsgReqGetMacAddress { }
mode: WifiMode::Sta as _,
}, /// duration in seconds, clamped to [10, 3600]
)), async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> {
let req = proto::CtrlMsgReqConfigHeartbeat { enable: true, duration };
ioctl!(self, ReqConfigHeartbeat, RespConfigHeartbeat, req, resp);
Ok(())
}
async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> {
let req = proto::CtrlMsgReqGetMacAddress {
mode: WifiMode::Sta as _,
}; };
let resp = self.ioctl(req).await; ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp);
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
assert_eq!(resp.resp, 0);
// WHY IS THIS A STRING? WHYYYY // WHY IS THIS A STRING? WHYYYY
fn nibble_from_hex(b: u8) -> u8 { fn nibble_from_hex(b: u8) -> u8 {
@ -88,32 +111,32 @@ impl<'a> Control<'a> {
let mac = resp.mac.as_bytes(); let mac = resp.mac.as_bytes();
let mut res = [0; 6]; let mut res = [0; 6];
assert_eq!(mac.len(), 17); if mac.len() != 17 {
warn!("unexpected MAC respnse length");
return Err(Error::Internal);
}
for (i, b) in res.iter_mut().enumerate() { for (i, b) in res.iter_mut().enumerate() {
*b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1]) *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1])
} }
res Ok(res)
} }
async fn set_wifi_mode(&mut self, mode: u32) { async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> {
let req = proto::CtrlMsg { let req = proto::CtrlMsgReqSetMode { mode };
msg_id: proto::CtrlMsgId::ReqSetWifiMode as _, ioctl!(self, ReqSetWifiMode, RespSetWifiMode, req, resp);
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })), Ok(())
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
assert_eq!(resp.resp, 0);
} }
async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg { async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> {
debug!("ioctl req: {:?}", &req); debug!("ioctl req: {:?}", &msg);
let mut buf = [0u8; 128]; let mut buf = [0u8; 128];
let req_len = noproto::write(&req, &mut buf).unwrap(); let req_len = noproto::write(msg, &mut buf).map_err(|_| {
warn!("failed to serialize control request");
Error::Internal
})?;
struct CancelOnDrop<'a>(&'a Shared); struct CancelOnDrop<'a>(&'a Shared);
@ -135,9 +158,12 @@ impl<'a> Control<'a> {
ioctl.defuse(); ioctl.defuse();
let res = noproto::read(&buf[..resp_len]).unwrap(); *msg = noproto::read(&buf[..resp_len]).map_err(|_| {
debug!("ioctl resp: {:?}", &res); warn!("failed to serialize control request");
Error::Internal
})?;
debug!("ioctl resp: {:?}", msg);
res Ok(())
} }
} }

View File

@ -1,17 +1,15 @@
#![no_std] #![no_std]
use control::Control; use embassy_futures::select::{select4, Either4};
use embassy_futures::select::{select3, Either3};
use embassy_net_driver_channel as ch; use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::LinkState;
use embassy_time::{Duration, Instant, Timer}; use embassy_time::{Duration, Instant, Timer};
use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal_async::digital::Wait; use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiDevice; use embedded_hal_async::spi::SpiDevice;
use ioctl::Shared;
use proto::CtrlMsg;
use crate::ioctl::PendingIoctl; use crate::ioctl::{PendingIoctl, Shared};
use crate::proto::CtrlMsgPayload; use crate::proto::{CtrlMsg, CtrlMsgPayload};
mod proto; mod proto;
@ -21,6 +19,8 @@ mod fmt;
mod control; mod control;
mod ioctl; mod ioctl;
pub use control::*;
const MTU: usize = 1514; const MTU: usize = 1514;
macro_rules! impl_bytes { macro_rules! impl_bytes {
@ -95,6 +95,7 @@ enum InterfaceType {
} }
const MAX_SPI_BUFFER_SIZE: usize = 1600; const MAX_SPI_BUFFER_SIZE: usize = 1600;
const HEARTBEAT_MAX_GAP: Duration = Duration::from_secs(20);
pub struct State { pub struct State {
shared: Shared, shared: Shared,
@ -129,12 +130,14 @@ where
let mut runner = Runner { let mut runner = Runner {
ch: ch_runner, ch: ch_runner,
state_ch,
shared: &state.shared, shared: &state.shared,
next_seq: 1, next_seq: 1,
handshake, handshake,
ready, ready,
reset, reset,
spi, spi,
heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP,
}; };
runner.init().await; runner.init().await;
@ -143,9 +146,11 @@ where
pub struct Runner<'a, SPI, IN, OUT> { pub struct Runner<'a, SPI, IN, OUT> {
ch: ch::Runner<'a, MTU>, ch: ch::Runner<'a, MTU>,
state_ch: ch::StateRunner<'a>,
shared: &'a Shared, shared: &'a Shared,
next_seq: u16, next_seq: u16,
heartbeat_deadline: Instant,
spi: SPI, spi: SPI,
handshake: IN, handshake: IN,
@ -177,9 +182,10 @@ where
let ioctl = self.shared.ioctl_wait_pending(); let ioctl = self.shared.ioctl_wait_pending();
let tx = self.ch.tx_buf(); let tx = self.ch.tx_buf();
let ev = async { self.ready.wait_for_high().await.unwrap() }; let ev = async { self.ready.wait_for_high().await.unwrap() };
let hb = Timer::at(self.heartbeat_deadline);
match select3(ioctl, tx, ev).await { match select4(ioctl, tx, ev, hb).await {
Either3::First(PendingIoctl { buf, req_len }) => { Either4::First(PendingIoctl { buf, req_len }) => {
tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02"); tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02");
tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes()); tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes());
tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]); tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]);
@ -198,7 +204,7 @@ where
header.checksum = checksum(&tx_buf[..26 + req_len]); header.checksum = checksum(&tx_buf[..26 + req_len]);
tx_buf[0..12].copy_from_slice(&header.to_bytes()); tx_buf[0..12].copy_from_slice(&header.to_bytes());
} }
Either3::Second(packet) => { Either4::Second(packet) => {
tx_buf[12..][..packet.len()].copy_from_slice(packet); tx_buf[12..][..packet.len()].copy_from_slice(packet);
let mut header = PayloadHeader { let mut header = PayloadHeader {
@ -217,9 +223,12 @@ where
self.ch.tx_done(); self.ch.tx_done();
} }
Either3::Third(()) => { Either4::Third(()) => {
tx_buf[..PayloadHeader::SIZE].fill(0); tx_buf[..PayloadHeader::SIZE].fill(0);
} }
Either4::Fourth(()) => {
panic!("heartbeat from esp32 stopped")
}
} }
if tx_buf[0] != 0 { if tx_buf[0] != 0 {
@ -308,7 +317,7 @@ where
} }
} }
fn handle_event(&self, data: &[u8]) { fn handle_event(&mut self, data: &[u8]) {
let Ok(event) = noproto::read::<CtrlMsg>(data) else { let Ok(event) = noproto::read::<CtrlMsg>(data) else {
warn!("failed to parse event"); warn!("failed to parse event");
return; return;
@ -323,6 +332,11 @@ where
match payload { match payload {
CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(),
CtrlMsgPayload::EventHeartbeat(_) => self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP,
CtrlMsgPayload::EventStationDisconnectFromAp(e) => {
info!("disconnected, code {}", e.resp);
self.state_ch.set_link_state(LinkState::Down);
}
_ => {} _ => {}
} }
} }

View File

@ -1,7 +0,0 @@
# WIZnet W5500 `embassy-net` integration
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet W5500 SPI ethernet chip, operating in MACRAW mode.
Supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async)
See [`examples`](https://github.com/kalkyl/embassy-net-w5500/tree/main/examples) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module.

View File

@ -1,131 +0,0 @@
use embedded_hal_async::spi::SpiDevice;
use crate::socket;
use crate::spi::SpiInterface;
pub const MODE: u16 = 0x00;
pub const MAC: u16 = 0x09;
pub const SOCKET_INTR: u16 = 0x18;
pub const PHY_CFG: u16 = 0x2E;
#[repr(u8)]
pub enum RegisterBlock {
Common = 0x00,
Socket0 = 0x01,
TxBuf = 0x02,
RxBuf = 0x03,
}
/// W5500 in MACRAW mode
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct W5500<SPI> {
bus: SpiInterface<SPI>,
}
impl<SPI: SpiDevice> W5500<SPI> {
/// Create and initialize the W5500 driver
pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result<W5500<SPI>, SPI::Error> {
let mut bus = SpiInterface(spi);
// Reset device
bus.write_frame(RegisterBlock::Common, MODE, &[0x80]).await?;
// Enable interrupt pin
bus.write_frame(RegisterBlock::Common, SOCKET_INTR, &[0x01]).await?;
// Enable receive interrupt
bus.write_frame(
RegisterBlock::Socket0,
socket::SOCKET_INTR_MASK,
&[socket::Interrupt::Receive as u8],
)
.await?;
// Set MAC address
bus.write_frame(RegisterBlock::Common, MAC, &mac_addr).await?;
// Set the raw socket RX/TX buffer sizes to 16KB
bus.write_frame(RegisterBlock::Socket0, socket::TXBUF_SIZE, &[16])
.await?;
bus.write_frame(RegisterBlock::Socket0, socket::RXBUF_SIZE, &[16])
.await?;
// MACRAW mode with MAC filtering.
let mode: u8 = (1 << 2) | (1 << 7);
bus.write_frame(RegisterBlock::Socket0, socket::MODE, &[mode]).await?;
socket::command(&mut bus, socket::Command::Open).await?;
Ok(Self { bus })
}
/// Read bytes from the RX buffer. Returns the number of bytes read.
async fn read_bytes(&mut self, buffer: &mut [u8], offset: u16) -> Result<usize, SPI::Error> {
let rx_size = socket::get_rx_size(&mut self.bus).await? as usize;
let read_buffer = if rx_size > buffer.len() + offset as usize {
buffer
} else {
&mut buffer[..rx_size - offset as usize]
};
let read_ptr = socket::get_rx_read_ptr(&mut self.bus).await?.wrapping_add(offset);
self.bus.read_frame(RegisterBlock::RxBuf, read_ptr, read_buffer).await?;
socket::set_rx_read_ptr(&mut self.bus, read_ptr.wrapping_add(read_buffer.len() as u16)).await?;
Ok(read_buffer.len())
}
/// Read an ethernet frame from the device. Returns the number of bytes read.
pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result<usize, SPI::Error> {
let rx_size = socket::get_rx_size(&mut self.bus).await? as usize;
if rx_size == 0 {
return Ok(0);
}
socket::reset_interrupt(&mut self.bus, socket::Interrupt::Receive).await?;
// First two bytes gives the size of the received ethernet frame
let expected_frame_size: usize = {
let mut frame_bytes = [0u8; 2];
assert!(self.read_bytes(&mut frame_bytes[..], 0).await? == 2);
u16::from_be_bytes(frame_bytes) as usize - 2
};
// Read the ethernet frame
let read_buffer = if frame.len() > expected_frame_size {
&mut frame[..expected_frame_size]
} else {
frame
};
let recvd_frame_size = self.read_bytes(read_buffer, 2).await?;
// Register RX as completed
socket::command(&mut self.bus, socket::Command::Receive).await?;
// If the whole frame wasn't read, drop it
if recvd_frame_size < expected_frame_size {
Ok(0)
} else {
Ok(recvd_frame_size)
}
}
/// Write an ethernet frame to the device. Returns number of bytes written
pub async fn write_frame(&mut self, frame: &[u8]) -> Result<usize, SPI::Error> {
while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {}
let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?;
self.bus.write_frame(RegisterBlock::TxBuf, write_ptr, frame).await?;
socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?;
socket::command(&mut self.bus, socket::Command::Send).await?;
Ok(frame.len())
}
pub async fn is_link_up(&mut self) -> bool {
let mut link = [0];
self.bus
.read_frame(RegisterBlock::Common, PHY_CFG, &mut link)
.await
.ok();
link[0] & 1 == 1
}
}

View File

@ -1,80 +0,0 @@
use embedded_hal_async::spi::SpiDevice;
use crate::device::RegisterBlock;
use crate::spi::SpiInterface;
pub const MODE: u16 = 0x00;
pub const COMMAND: u16 = 0x01;
pub const RXBUF_SIZE: u16 = 0x1E;
pub const TXBUF_SIZE: u16 = 0x1F;
pub const TX_FREE_SIZE: u16 = 0x20;
pub const TX_DATA_WRITE_PTR: u16 = 0x24;
pub const RECVD_SIZE: u16 = 0x26;
pub const RX_DATA_READ_PTR: u16 = 0x28;
pub const SOCKET_INTR_MASK: u16 = 0x2C;
#[repr(u8)]
pub enum Command {
Open = 0x01,
Send = 0x20,
Receive = 0x40,
}
pub const INTR: u16 = 0x02;
#[repr(u8)]
pub enum Interrupt {
Receive = 0b00100_u8,
}
pub async fn reset_interrupt<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>, code: Interrupt) -> Result<(), SPI::Error> {
let data = [code as u8];
bus.write_frame(RegisterBlock::Socket0, INTR, &data).await
}
pub async fn get_tx_write_ptr<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>) -> Result<u16, SPI::Error> {
let mut data = [0u8; 2];
bus.read_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &mut data)
.await?;
Ok(u16::from_be_bytes(data))
}
pub async fn set_tx_write_ptr<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>, ptr: u16) -> Result<(), SPI::Error> {
let data = ptr.to_be_bytes();
bus.write_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &data).await
}
pub async fn get_rx_read_ptr<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>) -> Result<u16, SPI::Error> {
let mut data = [0u8; 2];
bus.read_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &mut data)
.await?;
Ok(u16::from_be_bytes(data))
}
pub async fn set_rx_read_ptr<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>, ptr: u16) -> Result<(), SPI::Error> {
let data = ptr.to_be_bytes();
bus.write_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &data).await
}
pub async fn command<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>, command: Command) -> Result<(), SPI::Error> {
let data = [command as u8];
bus.write_frame(RegisterBlock::Socket0, COMMAND, &data).await
}
pub async fn get_rx_size<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>) -> Result<u16, SPI::Error> {
loop {
// Wait until two sequential reads are equal
let mut res0 = [0u8; 2];
bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res0).await?;
let mut res1 = [0u8; 2];
bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res1).await?;
if res0 == res1 {
break Ok(u16::from_be_bytes(res0));
}
}
}
pub async fn get_tx_free_size<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>) -> Result<u16, SPI::Error> {
let mut data = [0; 2];
bus.read_frame(RegisterBlock::Socket0, TX_FREE_SIZE, &mut data).await?;
Ok(u16::from_be_bytes(data))
}

View File

@ -1,32 +0,0 @@
use embedded_hal_async::spi::{Operation, SpiDevice};
use crate::device::RegisterBlock;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpiInterface<SPI>(pub SPI);
impl<SPI: SpiDevice> SpiInterface<SPI> {
pub async fn read_frame(&mut self, block: RegisterBlock, address: u16, data: &mut [u8]) -> Result<(), SPI::Error> {
let address_phase = address.to_be_bytes();
let control_phase = [(block as u8) << 3];
let operations = &mut [
Operation::Write(&address_phase),
Operation::Write(&control_phase),
Operation::TransferInPlace(data),
];
self.0.transaction(operations).await
}
pub async fn write_frame(&mut self, block: RegisterBlock, address: u16, data: &[u8]) -> Result<(), SPI::Error> {
let address_phase = address.to_be_bytes();
let control_phase = [(block as u8) << 3 | 0b0000_0100];
let data_phase = data;
let operations = &mut [
Operation::Write(&address_phase[..]),
Operation::Write(&control_phase),
Operation::Write(&data_phase),
];
self.0.transaction(operations).await
}
}

View File

@ -1,21 +1,22 @@
[package] [package]
name = "embassy-net-w5500" name = "embassy-net-wiznet"
version = "0.1.0" version = "0.1.0"
description = "embassy-net driver for the W5500 ethernet chip" description = "embassy-net driver for WIZnet SPI Ethernet chips"
keywords = ["embedded", "w5500", "embassy-net", "embedded-hal-async", "ethernet", "async"] keywords = ["embedded", "wiznet", "embassy-net", "embedded-hal-async", "ethernet", "async"]
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
embedded-hal = { version = "1.0.0-alpha.11" } embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=0.2.0-alpha.2" } embedded-hal-async = { version = "=1.0.0-rc.1" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }
[package.metadata.embassy_docs] [package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-w5500-v$VERSION/embassy-net-w5500/src/" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-wiznet-v$VERSION/embassy-net-wiznet/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-w5500/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-wiznet/src/"
target = "thumbv7em-none-eabi" target = "thumbv7em-none-eabi"
features = ["defmt"]

View File

@ -0,0 +1,27 @@
# WIZnet `embassy-net` integration
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet SPI ethernet chips, operating in MACRAW mode.
See [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/rp) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module.
## Supported chips
- W5500
- W5100S
## Interoperability
This crate can run on any executor.
It supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async).
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

View File

@ -0,0 +1,48 @@
mod w5500;
pub use w5500::W5500;
mod w5100s;
pub use w5100s::W5100S;
pub(crate) mod sealed {
use embedded_hal_async::spi::SpiDevice;
pub trait Chip {
type Address;
const COMMON_MODE: Self::Address;
const COMMON_MAC: Self::Address;
const COMMON_SOCKET_INTR: Self::Address;
const COMMON_PHY_CFG: Self::Address;
const SOCKET_MODE: Self::Address;
const SOCKET_COMMAND: Self::Address;
const SOCKET_RXBUF_SIZE: Self::Address;
const SOCKET_TXBUF_SIZE: Self::Address;
const SOCKET_TX_FREE_SIZE: Self::Address;
const SOCKET_TX_DATA_WRITE_PTR: Self::Address;
const SOCKET_RECVD_SIZE: Self::Address;
const SOCKET_RX_DATA_READ_PTR: Self::Address;
const SOCKET_INTR_MASK: Self::Address;
const SOCKET_INTR: Self::Address;
const SOCKET_MODE_VALUE: u8;
const BUF_SIZE: u16;
const AUTO_WRAP: bool;
fn rx_addr(addr: u16) -> Self::Address;
fn tx_addr(addr: u16) -> Self::Address;
async fn bus_read<SPI: SpiDevice>(
spi: &mut SPI,
address: Self::Address,
data: &mut [u8],
) -> Result<(), SPI::Error>;
async fn bus_write<SPI: SpiDevice>(
spi: &mut SPI,
address: Self::Address,
data: &[u8],
) -> Result<(), SPI::Error>;
}
}
pub trait Chip: sealed::Chip {}

View File

@ -0,0 +1,61 @@
use embedded_hal_async::spi::{Operation, SpiDevice};
const SOCKET_BASE: u16 = 0x400;
const TX_BASE: u16 = 0x4000;
const RX_BASE: u16 = 0x6000;
pub enum W5100S {}
impl super::Chip for W5100S {}
impl super::sealed::Chip for W5100S {
type Address = u16;
const COMMON_MODE: Self::Address = 0x00;
const COMMON_MAC: Self::Address = 0x09;
const COMMON_SOCKET_INTR: Self::Address = 0x16;
const COMMON_PHY_CFG: Self::Address = 0x3c;
const SOCKET_MODE: Self::Address = SOCKET_BASE + 0x00;
const SOCKET_COMMAND: Self::Address = SOCKET_BASE + 0x01;
const SOCKET_RXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1E;
const SOCKET_TXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1F;
const SOCKET_TX_FREE_SIZE: Self::Address = SOCKET_BASE + 0x20;
const SOCKET_TX_DATA_WRITE_PTR: Self::Address = SOCKET_BASE + 0x24;
const SOCKET_RECVD_SIZE: Self::Address = SOCKET_BASE + 0x26;
const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28;
const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C;
const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02;
const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6);
const BUF_SIZE: u16 = 0x2000;
const AUTO_WRAP: bool = false;
fn rx_addr(addr: u16) -> Self::Address {
RX_BASE + addr
}
fn tx_addr(addr: u16) -> Self::Address {
TX_BASE + addr
}
async fn bus_read<SPI: SpiDevice>(
spi: &mut SPI,
address: Self::Address,
data: &mut [u8],
) -> Result<(), SPI::Error> {
spi.transaction(&mut [
Operation::Write(&[0x0F, (address >> 8) as u8, address as u8]),
Operation::Read(data),
])
.await
}
async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> {
spi.transaction(&mut [
Operation::Write(&[0xF0, (address >> 8) as u8, address as u8]),
Operation::Write(data),
])
.await
}
}

View File

@ -0,0 +1,72 @@
use embedded_hal_async::spi::{Operation, SpiDevice};
#[repr(u8)]
pub enum RegisterBlock {
Common = 0x00,
Socket0 = 0x01,
TxBuf = 0x02,
RxBuf = 0x03,
}
pub enum W5500 {}
impl super::Chip for W5500 {}
impl super::sealed::Chip for W5500 {
type Address = (RegisterBlock, u16);
const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00);
const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x09);
const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x18);
const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x2E);
const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x00);
const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x01);
const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1E);
const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1F);
const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x20);
const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x24);
const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x26);
const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28);
const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C);
const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02);
const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7);
const BUF_SIZE: u16 = 0x4000;
const AUTO_WRAP: bool = true;
fn rx_addr(addr: u16) -> Self::Address {
(RegisterBlock::RxBuf, addr)
}
fn tx_addr(addr: u16) -> Self::Address {
(RegisterBlock::TxBuf, addr)
}
async fn bus_read<SPI: SpiDevice>(
spi: &mut SPI,
address: Self::Address,
data: &mut [u8],
) -> Result<(), SPI::Error> {
let address_phase = address.1.to_be_bytes();
let control_phase = [(address.0 as u8) << 3];
let operations = &mut [
Operation::Write(&address_phase),
Operation::Write(&control_phase),
Operation::TransferInPlace(data),
];
spi.transaction(operations).await
}
async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> {
let address_phase = address.1.to_be_bytes();
let control_phase = [(address.0 as u8) << 3 | 0b0000_0100];
let data_phase = data;
let operations = &mut [
Operation::Write(&address_phase[..]),
Operation::Write(&control_phase),
Operation::Write(&data_phase),
];
spi.transaction(operations).await
}
}

View File

@ -0,0 +1,195 @@
use core::marker::PhantomData;
use embedded_hal_async::spi::SpiDevice;
use crate::chip::Chip;
#[repr(u8)]
enum Command {
Open = 0x01,
Send = 0x20,
Receive = 0x40,
}
#[repr(u8)]
enum Interrupt {
Receive = 0b00100_u8,
}
/// Wiznet chip in MACRAW mode
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct WiznetDevice<C, SPI> {
spi: SPI,
_phantom: PhantomData<C>,
}
impl<C: Chip, SPI: SpiDevice> WiznetDevice<C, SPI> {
/// Create and initialize the driver
pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result<Self, SPI::Error> {
let mut this = Self {
spi,
_phantom: PhantomData,
};
// Reset device
this.bus_write(C::COMMON_MODE, &[0x80]).await?;
// Enable interrupt pin
this.bus_write(C::COMMON_SOCKET_INTR, &[0x01]).await?;
// Enable receive interrupt
this.bus_write(C::SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?;
// Set MAC address
this.bus_write(C::COMMON_MAC, &mac_addr).await?;
// Set the raw socket RX/TX buffer sizes.
let buf_kbs = (C::BUF_SIZE / 1024) as u8;
this.bus_write(C::SOCKET_TXBUF_SIZE, &[buf_kbs]).await?;
this.bus_write(C::SOCKET_RXBUF_SIZE, &[buf_kbs]).await?;
// MACRAW mode with MAC filtering.
this.bus_write(C::SOCKET_MODE, &[C::SOCKET_MODE_VALUE]).await?;
this.command(Command::Open).await?;
Ok(this)
}
async fn bus_read(&mut self, address: C::Address, data: &mut [u8]) -> Result<(), SPI::Error> {
C::bus_read(&mut self.spi, address, data).await
}
async fn bus_write(&mut self, address: C::Address, data: &[u8]) -> Result<(), SPI::Error> {
C::bus_write(&mut self.spi, address, data).await
}
async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> {
let data = [code as u8];
self.bus_write(C::SOCKET_INTR, &data).await
}
async fn get_tx_write_ptr(&mut self) -> Result<u16, SPI::Error> {
let mut data = [0u8; 2];
self.bus_read(C::SOCKET_TX_DATA_WRITE_PTR, &mut data).await?;
Ok(u16::from_be_bytes(data))
}
async fn set_tx_write_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> {
let data = ptr.to_be_bytes();
self.bus_write(C::SOCKET_TX_DATA_WRITE_PTR, &data).await
}
async fn get_rx_read_ptr(&mut self) -> Result<u16, SPI::Error> {
let mut data = [0u8; 2];
self.bus_read(C::SOCKET_RX_DATA_READ_PTR, &mut data).await?;
Ok(u16::from_be_bytes(data))
}
async fn set_rx_read_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> {
let data = ptr.to_be_bytes();
self.bus_write(C::SOCKET_RX_DATA_READ_PTR, &data).await
}
async fn command(&mut self, command: Command) -> Result<(), SPI::Error> {
let data = [command as u8];
self.bus_write(C::SOCKET_COMMAND, &data).await
}
async fn get_rx_size(&mut self) -> Result<u16, SPI::Error> {
loop {
// Wait until two sequential reads are equal
let mut res0 = [0u8; 2];
self.bus_read(C::SOCKET_RECVD_SIZE, &mut res0).await?;
let mut res1 = [0u8; 2];
self.bus_read(C::SOCKET_RECVD_SIZE, &mut res1).await?;
if res0 == res1 {
break Ok(u16::from_be_bytes(res0));
}
}
}
async fn get_tx_free_size(&mut self) -> Result<u16, SPI::Error> {
let mut data = [0; 2];
self.bus_read(C::SOCKET_TX_FREE_SIZE, &mut data).await?;
Ok(u16::from_be_bytes(data))
}
/// Read bytes from the RX buffer.
async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> {
if C::AUTO_WRAP {
self.bus_read(C::rx_addr(*read_ptr), buffer).await?;
} else {
let addr = *read_ptr % C::BUF_SIZE;
if addr as usize + buffer.len() <= C::BUF_SIZE as usize {
self.bus_read(C::rx_addr(addr), buffer).await?;
} else {
let n = C::BUF_SIZE - addr;
self.bus_read(C::rx_addr(addr), &mut buffer[..n as usize]).await?;
self.bus_read(C::rx_addr(0), &mut buffer[n as usize..]).await?;
}
}
*read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16);
Ok(())
}
/// Read an ethernet frame from the device. Returns the number of bytes read.
pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result<usize, SPI::Error> {
let rx_size = self.get_rx_size().await? as usize;
if rx_size == 0 {
return Ok(0);
}
self.reset_interrupt(Interrupt::Receive).await?;
let mut read_ptr = self.get_rx_read_ptr().await?;
// First two bytes gives the size of the received ethernet frame
let expected_frame_size: usize = {
let mut frame_bytes = [0u8; 2];
self.read_bytes(&mut read_ptr, &mut frame_bytes).await?;
u16::from_be_bytes(frame_bytes) as usize - 2
};
// Read the ethernet frame
self.read_bytes(&mut read_ptr, &mut frame[..expected_frame_size])
.await?;
// Register RX as completed
self.set_rx_read_ptr(read_ptr).await?;
self.command(Command::Receive).await?;
Ok(expected_frame_size)
}
/// Write an ethernet frame to the device. Returns number of bytes written
pub async fn write_frame(&mut self, frame: &[u8]) -> Result<usize, SPI::Error> {
while self.get_tx_free_size().await? < frame.len() as u16 {}
let write_ptr = self.get_tx_write_ptr().await?;
if C::AUTO_WRAP {
self.bus_write(C::tx_addr(write_ptr), frame).await?;
} else {
let addr = write_ptr % C::BUF_SIZE;
if addr as usize + frame.len() <= C::BUF_SIZE as usize {
self.bus_write(C::tx_addr(addr), frame).await?;
} else {
let n = C::BUF_SIZE - addr;
self.bus_write(C::tx_addr(addr), &frame[..n as usize]).await?;
self.bus_write(C::tx_addr(0), &frame[n as usize..]).await?;
}
}
self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16))
.await?;
self.command(Command::Send).await?;
Ok(frame.len())
}
pub async fn is_link_up(&mut self) -> bool {
let mut link = [0];
self.bus_read(C::COMMON_PHY_CFG, &mut link).await.ok();
link[0] & 1 == 1
}
}

View File

@ -1,9 +1,9 @@
//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. //! [`embassy-net`](https://crates.io/crates/embassy-net) driver for WIZnet ethernet chips.
#![no_std] #![no_std]
#![feature(async_fn_in_trait)]
pub mod chip;
mod device; mod device;
mod socket;
mod spi;
use embassy_futures::select::{select, Either}; use embassy_futures::select::{select, Either};
use embassy_net_driver_channel as ch; use embassy_net_driver_channel as ch;
@ -13,10 +13,12 @@ use embedded_hal::digital::OutputPin;
use embedded_hal_async::digital::Wait; use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiDevice; use embedded_hal_async::spi::SpiDevice;
use crate::device::W5500; use crate::chip::Chip;
use crate::device::WiznetDevice;
const MTU: usize = 1514; const MTU: usize = 1514;
/// Type alias for the embassy-net driver for W5500 /// Type alias for the embassy-net driver.
pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;
/// Internal state for the embassy-net integration. /// Internal state for the embassy-net integration.
@ -33,18 +35,18 @@ impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {
} }
} }
/// Background runner for the W5500. /// Background runner for the driver.
/// ///
/// You must call `.run()` in a background task for the W5500 to operate. /// You must call `.run()` in a background task for the driver to operate.
pub struct Runner<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> { pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> {
mac: W5500<SPI>, mac: WiznetDevice<C, SPI>,
ch: ch::Runner<'d, MTU>, ch: ch::Runner<'d, MTU>,
int: INT, int: INT,
_reset: RST, _reset: RST,
} }
/// You must call this in a background task for the W5500 to operate. /// You must call this in a background task for the driver to operate.
impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> {
pub async fn run(mut self) -> ! { pub async fn run(mut self) -> ! {
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
loop { loop {
@ -78,23 +80,29 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {
} }
} }
/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net). /// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net).
pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>( ///
/// This returns two structs:
/// - a `Device` that you must pass to the `embassy-net` stack.
/// - a `Runner`. You must call `.run()` on it in a background task.
pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
mac_addr: [u8; 6], mac_addr: [u8; 6],
state: &'a mut State<N_RX, N_TX>, state: &'a mut State<N_RX, N_TX>,
spi_dev: SPI, spi_dev: SPI,
int: INT, int: INT,
mut reset: RST, mut reset: RST,
) -> (Device<'a>, Runner<'a, SPI, INT, RST>) { ) -> (Device<'a>, Runner<'a, C, SPI, INT, RST>) {
// Reset the W5500. // Reset the chip.
reset.set_low().ok(); reset.set_low().ok();
// Ensure the reset is registered. // Ensure the reset is registered.
Timer::after(Duration::from_millis(1)).await; Timer::after(Duration::from_millis(1)).await;
reset.set_high().ok(); reset.set_high().ok();
// Wait for the W5500 to achieve PLL lock.
Timer::after(Duration::from_millis(2)).await;
let mac = W5500::new(spi_dev, mac_addr).await.unwrap(); // Wait for PLL lock. Some chips are slower than others.
// Slowest is w5100s which is 100ms, so let's just wait that.
Timer::after(Duration::from_millis(100)).await;
let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap();
let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr)); let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr));
( (

View File

@ -22,7 +22,7 @@ unimplemented features of the network protocols.
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W - [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support. - [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
- [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5). - [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5).
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip. - [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips (W5100S, W5500)
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
## Examples ## Examples

View File

@ -82,6 +82,22 @@ impl<'a> TcpReader<'a> {
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
self.io.read(buf).await self.io.read(buf).await
} }
/// Call `f` with the largest contiguous slice of octets in the receive buffer,
/// and dequeue the amount of elements returned by `f`.
///
/// If no data is available, it waits until there is at least one byte available.
pub async fn read_with<F, R>(&mut self, f: F) -> Result<R, Error>
where
F: FnOnce(&mut [u8]) -> (usize, R),
{
self.io.read_with(f).await
}
/// Return the maximum number of bytes inside the transmit buffer.
pub fn recv_capacity(&self) -> usize {
self.io.recv_capacity()
}
} }
impl<'a> TcpWriter<'a> { impl<'a> TcpWriter<'a> {
@ -100,6 +116,22 @@ impl<'a> TcpWriter<'a> {
pub async fn flush(&mut self) -> Result<(), Error> { pub async fn flush(&mut self) -> Result<(), Error> {
self.io.flush().await self.io.flush().await
} }
/// Call `f` with the largest contiguous slice of octets in the transmit buffer,
/// and enqueue the amount of elements returned by `f`.
///
/// If the socket is not ready to accept data, it waits until it is.
pub async fn write_with<F, R>(&mut self, f: F) -> Result<R, Error>
where
F: FnOnce(&mut [u8]) -> (usize, R),
{
self.io.write_with(f).await
}
/// Return the maximum number of bytes inside the transmit buffer.
pub fn send_capacity(&self) -> usize {
self.io.send_capacity()
}
} }
impl<'a> TcpSocket<'a> { impl<'a> TcpSocket<'a> {
@ -121,6 +153,38 @@ impl<'a> TcpSocket<'a> {
} }
} }
/// Return the maximum number of bytes inside the recv buffer.
pub fn recv_capacity(&self) -> usize {
self.io.recv_capacity()
}
/// Return the maximum number of bytes inside the transmit buffer.
pub fn send_capacity(&self) -> usize {
self.io.send_capacity()
}
/// Call `f` with the largest contiguous slice of octets in the transmit buffer,
/// and enqueue the amount of elements returned by `f`.
///
/// If the socket is not ready to accept data, it waits until it is.
pub async fn write_with<F, R>(&mut self, f: F) -> Result<R, Error>
where
F: FnOnce(&mut [u8]) -> (usize, R),
{
self.io.write_with(f).await
}
/// Call `f` with the largest contiguous slice of octets in the receive buffer,
/// and dequeue the amount of elements returned by `f`.
///
/// If no data is available, it waits until there is at least one byte available.
pub async fn read_with<F, R>(&mut self, f: F) -> Result<R, Error>
where
F: FnOnce(&mut [u8]) -> (usize, R),
{
self.io.read_with(f).await
}
/// Split the socket into reader and a writer halves. /// Split the socket into reader and a writer halves.
pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) {
(TcpReader { io: self.io }, TcpWriter { io: self.io }) (TcpReader { io: self.io }, TcpWriter { io: self.io })
@ -359,6 +423,64 @@ impl<'d> TcpIo<'d> {
.await .await
} }
async fn write_with<F, R>(&mut self, f: F) -> Result<R, Error>
where
F: FnOnce(&mut [u8]) -> (usize, R),
{
let mut f = Some(f);
poll_fn(move |cx| {
self.with_mut(|s, _| {
if !s.can_send() {
if s.may_send() {
// socket buffer is full wait until it has atleast one byte free
s.register_send_waker(cx.waker());
Poll::Pending
} else {
// if we can't transmit because the transmit half of the duplex connection is closed then return an error
Poll::Ready(Err(Error::ConnectionReset))
}
} else {
Poll::Ready(match s.send(f.take().unwrap()) {
// Connection reset. TODO: this can also be timeouts etc, investigate.
Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset),
Ok(r) => Ok(r),
})
}
})
})
.await
}
async fn read_with<F, R>(&mut self, f: F) -> Result<R, Error>
where
F: FnOnce(&mut [u8]) -> (usize, R),
{
let mut f = Some(f);
poll_fn(move |cx| {
self.with_mut(|s, _| {
if !s.can_recv() {
if s.may_recv() {
// socket buffer is empty wait until it has atleast one byte has arrived
s.register_recv_waker(cx.waker());
Poll::Pending
} else {
// if we can't receive because the recieve half of the duplex connection is closed then return an error
Poll::Ready(Err(Error::ConnectionReset))
}
} else {
Poll::Ready(match s.recv(f.take().unwrap()) {
// Connection reset. TODO: this can also be timeouts etc, investigate.
Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => {
Err(Error::ConnectionReset)
}
Ok(r) => Ok(r),
})
}
})
})
.await
}
async fn flush(&mut self) -> Result<(), Error> { async fn flush(&mut self) -> Result<(), Error> {
poll_fn(move |cx| { poll_fn(move |cx| {
self.with_mut(|s, _| { self.with_mut(|s, _| {
@ -376,6 +498,14 @@ impl<'d> TcpIo<'d> {
}) })
.await .await
} }
fn recv_capacity(&self) -> usize {
self.with(|s, _| s.recv_capacity())
}
fn send_capacity(&self) -> usize {
self.with(|s, _| s.send_capacity())
}
} }
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
@ -384,13 +514,20 @@ mod embedded_io_impls {
impl embedded_io_async::Error for ConnectError { impl embedded_io_async::Error for ConnectError {
fn kind(&self) -> embedded_io_async::ErrorKind { fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other match self {
ConnectError::ConnectionReset => embedded_io_async::ErrorKind::ConnectionReset,
ConnectError::TimedOut => embedded_io_async::ErrorKind::TimedOut,
ConnectError::NoRoute => embedded_io_async::ErrorKind::NotConnected,
ConnectError::InvalidState => embedded_io_async::ErrorKind::Other,
}
} }
} }
impl embedded_io_async::Error for Error { impl embedded_io_async::Error for Error {
fn kind(&self) -> embedded_io_async::ErrorKind { fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other match self {
Error::ConnectionReset => embedded_io_async::ErrorKind::ConnectionReset,
}
} }
} }

View File

@ -184,6 +184,26 @@ impl<'a> UdpSocket<'a> {
pub fn may_recv(&self) -> bool { pub fn may_recv(&self) -> bool {
self.with(|s, _| s.can_recv()) self.with(|s, _| s.can_recv())
} }
/// Return the maximum number packets the socket can receive.
pub fn packet_recv_capacity(&self) -> usize {
self.with(|s, _| s.packet_recv_capacity())
}
/// Return the maximum number packets the socket can receive.
pub fn packet_send_capacity(&self) -> usize {
self.with(|s, _| s.packet_send_capacity())
}
/// Return the maximum number of bytes inside the recv buffer.
pub fn payload_recv_capacity(&self) -> usize {
self.with(|s, _| s.payload_recv_capacity())
}
/// Return the maximum number of bytes inside the transmit buffer.
pub fn payload_send_capacity(&self) -> usize {
self.with(|s, _| s.payload_send_capacity())
}
} }
impl Drop for UdpSocket<'_> { impl Drop for UdpSocket<'_> {

View File

@ -32,7 +32,7 @@ rt = [
time = ["dep:embassy-time"] time = ["dep:embassy-time"]
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embassy-embedded-hal/defmt"] defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embassy-embedded-hal/defmt"]
# Enable nightly-only features # Enable nightly-only features
nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io-async", "embassy-embedded-hal/nightly"] nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io-async", "embassy-embedded-hal/nightly"]
@ -98,8 +98,8 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
embedded-io = { version = "0.5.0" } embedded-io = { version = "0.5.0" }
embedded-io-async = { version = "0.5.0", optional = true } embedded-io-async = { version = "0.5.0", optional = true }

View File

@ -378,6 +378,9 @@ impl<'d, T: Instance> Drop for Spim<'d, T> {
gpio::deconfigure_pin(r.psel.miso.read().bits()); gpio::deconfigure_pin(r.psel.miso.read().bits());
gpio::deconfigure_pin(r.psel.mosi.read().bits()); gpio::deconfigure_pin(r.psel.mosi.read().bits());
// Disable all events interrupts
T::Interrupt::disable();
trace!("spim drop: done"); trace!("spim drop: done");
} }
} }

View File

@ -949,51 +949,3 @@ mod eh02 {
} }
} }
} }
#[cfg(feature = "unstable-traits")]
mod eh1 {
use super::*;
impl embedded_hal_1::serial::Error for Error {
fn kind(&self) -> embedded_hal_1::serial::ErrorKind {
match *self {
Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other,
Self::BufferNotInRAM => embedded_hal_1::serial::ErrorKind::Other,
}
}
}
// =====================
impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for Uarte<'d, T> {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::serial::Write for Uarte<'d, T> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteTx<'d, T> {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::serial::Write for UarteTx<'d, T> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> {
type Error = Error;
}
}

View File

@ -85,9 +85,9 @@ fixed = "1.23.1"
rp-pac = { version = "6" } rp-pac = { version = "6" }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true}
paste = "1.0" paste = "1.0"
pio-proc = {version= "0.2" } pio-proc = {version= "0.2" }

View File

@ -76,7 +76,8 @@ pub unsafe fn write<'a, C: Channel, W: Word>(
) )
} }
static DUMMY: u32 = 0; // static mut so that this is allocated in RAM.
static mut DUMMY: u32 = 0;
pub unsafe fn write_repeated<'a, C: Channel, W: Word>( pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
ch: impl Peripheral<P = C> + 'a, ch: impl Peripheral<P = C> + 'a,
@ -86,7 +87,7 @@ pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
) -> Transfer<'a, C> { ) -> Transfer<'a, C> {
copy_inner( copy_inner(
ch, ch,
&DUMMY as *const u32, &mut DUMMY as *const u32,
to as *mut u32, to as *mut u32,
len, len,
W::size(), W::size(),

View File

@ -102,7 +102,7 @@ pub struct Flash<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> {
} }
impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> { impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> {
pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
trace!( trace!(
"Reading from 0x{:x} to 0x{:x}", "Reading from 0x{:x} to 0x{:x}",
FLASH_BASE as u32 + offset, FLASH_BASE as u32 + offset,
@ -120,7 +120,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
FLASH_SIZE FLASH_SIZE
} }
pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
check_erase(self, from, to)?; check_erase(self, from, to)?;
trace!( trace!(
@ -136,7 +136,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
Ok(()) Ok(())
} }
pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {
check_write(self, offset, bytes.len())?; check_write(self, offset, bytes.len())?;
trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset); trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset);
@ -233,13 +233,13 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
} }
/// Read SPI flash unique ID /// Read SPI flash unique ID
pub fn unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> {
unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid))? }; unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid))? };
Ok(()) Ok(())
} }
/// Read SPI flash JEDEC ID /// Read SPI flash JEDEC ID
pub fn jedec_id(&mut self) -> Result<u32, Error> { pub fn blocking_jedec_id(&mut self) -> Result<u32, Error> {
let mut jedec = None; let mut jedec = None;
unsafe { unsafe {
self.in_ram(|| { self.in_ram(|| {
@ -251,7 +251,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
} }
impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> { impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> {
pub fn new(_flash: impl Peripheral<P = T> + 'd) -> Self { pub fn new_blocking(_flash: impl Peripheral<P = T> + 'd) -> Self {
Self { Self {
dma: None, dma: None,
phantom: PhantomData, phantom: PhantomData,
@ -310,47 +310,8 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> {
transfer, transfer,
}) })
} }
}
impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> { pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
type Error = Error;
}
impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> {
const READ_SIZE: usize = READ_SIZE;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.read(offset, bytes)
}
fn capacity(&self) -> usize {
self.capacity()
}
}
impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {}
impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> {
const WRITE_SIZE: usize = WRITE_SIZE;
const ERASE_SIZE: usize = ERASE_SIZE;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.erase(from, to)
}
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
self.write(offset, bytes)
}
}
#[cfg(feature = "nightly")]
impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash
for Flash<'d, T, Async, FLASH_SIZE>
{
const READ_SIZE: usize = ASYNC_READ_SIZE;
async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
// Checked early to simplify address validity checks // Checked early to simplify address validity checks
@ -389,6 +350,49 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash
Ok(()) Ok(())
} }
}
impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> {
type Error = Error;
}
impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> {
const READ_SIZE: usize = READ_SIZE;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(offset, bytes)
}
fn capacity(&self) -> usize {
self.capacity()
}
}
impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {}
impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> {
const WRITE_SIZE: usize = WRITE_SIZE;
const ERASE_SIZE: usize = ERASE_SIZE;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.blocking_erase(from, to)
}
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(offset, bytes)
}
}
#[cfg(feature = "nightly")]
impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash
for Flash<'d, T, Async, FLASH_SIZE>
{
const READ_SIZE: usize = ASYNC_READ_SIZE;
async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.read(offset, bytes).await
}
fn capacity(&self) -> usize { fn capacity(&self) -> usize {
self.capacity() self.capacity()
@ -404,11 +408,11 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash
const ERASE_SIZE: usize = ERASE_SIZE; const ERASE_SIZE: usize = ERASE_SIZE;
async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.erase(from, to) self.blocking_erase(from, to)
} }
async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
self.write(offset, bytes) self.blocking_write(offset, bytes)
} }
} }

View File

@ -749,15 +749,15 @@ mod eh02 {
mod eh1 { mod eh1 {
use super::*; use super::*;
impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> {
type Error = Error; type Error = Error;
} }
impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> {
type Error = Error; type Error = Error;
} }
impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> {
type Error = Error; type Error = Error;
} }
@ -767,16 +767,6 @@ mod eh1 {
} }
} }
impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer).map(drop)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
}
}
impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)
@ -793,16 +783,6 @@ mod eh1 {
} }
} }
impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUart<'d, T> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer).map(drop)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
}
}
impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)

View File

@ -807,26 +807,26 @@ mod eh02 {
mod eh1 { mod eh1 {
use super::*; use super::*;
impl embedded_hal_1::serial::Error for Error { impl embedded_hal_nb::serial::Error for Error {
fn kind(&self) -> embedded_hal_1::serial::ErrorKind { fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match *self { match *self {
Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat, Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
Self::Break => embedded_hal_1::serial::ErrorKind::Other, Self::Break => embedded_hal_nb::serial::ErrorKind::Other,
Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
} }
} }
} }
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for Uart<'d, T, M> { impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, M> {
type Error = Error; type Error = Error;
} }
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartTx<'d, T, M> { impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, M> {
type Error = Error; type Error = Error;
} }
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> { impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T, M> {
type Error = Error; type Error = Error;
} }
@ -851,16 +851,6 @@ mod eh1 {
} }
} }
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for UartTx<'d, T, M> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> { impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map_err(nb::Error::Other) self.blocking_write(&[char]).map_err(nb::Error::Other)
@ -877,16 +867,6 @@ mod eh1 {
} }
} }
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for Uart<'d, T, M> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> { impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map_err(nb::Error::Other) self.blocking_write(&[char]).map_err(nb::Error::Other)

View File

@ -28,7 +28,9 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> {
type TxToken<'a> = TxToken<'d> where Self: 'a; type TxToken<'a> = TxToken<'d> where Self: 'a;
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
if self.runner.rx_channel.poll_ready_to_receive(cx) && self.runner.tx_buf_channel.poll_ready_to_receive(cx) { if self.runner.rx_channel.poll_ready_to_receive(cx).is_ready()
&& self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready()
{
Some(( Some((
RxToken { RxToken {
rx: &self.runner.rx_channel, rx: &self.runner.rx_channel,
@ -44,7 +46,7 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> {
} }
fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> { fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
if self.runner.tx_buf_channel.poll_ready_to_receive(cx) { if self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() {
Some(TxToken { Some(TxToken {
tx: &self.runner.tx_channel, tx: &self.runner.tx_channel,
tx_buf: &self.runner.tx_buf_channel, tx_buf: &self.runner.tx_buf_channel,
@ -91,7 +93,7 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> {
{ {
// Only valid data events should be put into the queue // Only valid data events should be put into the queue
let data_event = match self.rx.try_recv().unwrap() { let data_event = match self.rx.try_receive().unwrap() {
MacEvent::McpsDataInd(data_event) => data_event, MacEvent::McpsDataInd(data_event) => data_event,
_ => unreachable!(), _ => unreachable!(),
}; };
@ -111,7 +113,7 @@ impl<'d> embassy_net_driver::TxToken for TxToken<'d> {
F: FnOnce(&mut [u8]) -> R, F: FnOnce(&mut [u8]) -> R,
{ {
// Only valid tx buffers should be put into the queue // Only valid tx buffers should be put into the queue
let buf = self.tx_buf.try_recv().unwrap(); let buf = self.tx_buf.try_receive().unwrap();
let r = f(&mut buf[..len]); let r = f(&mut buf[..len]);
// The tx channel should always be of equal capacity to the tx_buf channel // The tx channel should always be of equal capacity to the tx_buf channel

View File

@ -73,7 +73,7 @@ impl<'a> Runner<'a> {
let mut msdu_handle = 0x02; let mut msdu_handle = 0x02;
loop { loop {
let (buf, len) = self.tx_channel.recv().await; let (buf, len) = self.tx_channel.receive().await;
let _wm = self.write_mutex.lock().await; let _wm = self.write_mutex.lock().await;
// The mutex should be dropped on the next loop iteration // The mutex should be dropped on the next loop iteration

View File

@ -40,9 +40,9 @@ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true}
embedded-storage = "0.3.0" embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.4.0", optional = true } embedded-storage-async = { version = "0.4.0", optional = true }
@ -57,7 +57,7 @@ sdio-host = "0.5.0"
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
critical-section = "1.1" critical-section = "1.1"
atomic-polyfill = "1.0.1" atomic-polyfill = "1.0.1"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-82a24863823a3daf0ca664c7fdf008379d0a0d42" } stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7" }
vcell = "0.1.3" vcell = "0.1.3"
bxcan = "0.7.0" bxcan = "0.7.0"
nb = "1.0.0" nb = "1.0.0"
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
[build-dependencies] [build-dependencies]
proc-macro2 = "1.0.36" proc-macro2 = "1.0.36"
quote = "1.0.15" quote = "1.0.15"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-82a24863823a3daf0ca664c7fdf008379d0a0d42", default-features = false, features = ["metadata"]} stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7", default-features = false, features = ["metadata"]}
[features] [features]
default = ["rt"] default = ["rt"]

View File

@ -310,7 +310,11 @@ fn main() {
for p in METADATA.peripherals { for p in METADATA.peripherals {
// generating RccPeripheral impl for H7 ADC3 would result in bad frequency // 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")) { 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"))
{
continue; continue;
} }

View File

@ -1,6 +1,6 @@
#![macro_use] #![macro_use]
#[cfg(not(adc_f3))] #[cfg(not(any(adc_f3, adc_f3_v2)))]
#[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_f1, path = "f1.rs")]
#[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")]
#[cfg_attr(adc_v2, path = "v2.rs")] #[cfg_attr(adc_v2, path = "v2.rs")]
@ -8,16 +8,16 @@
#[cfg_attr(adc_v4, path = "v4.rs")] #[cfg_attr(adc_v4, path = "v4.rs")]
mod _version; mod _version;
#[cfg(not(any(adc_f1, adc_f3)))] #[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))]
mod resolution; mod resolution;
mod sample_time; mod sample_time;
#[cfg(not(adc_f3))] #[cfg(not(any(adc_f3, adc_f3_v2)))]
#[allow(unused)] #[allow(unused)]
pub use _version::*; pub use _version::*;
#[cfg(not(any(adc_f1, adc_f3)))] #[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))]
pub use resolution::Resolution; pub use resolution::Resolution;
#[cfg(not(adc_f3))] #[cfg(not(any(adc_f3, adc_f3_v2)))]
pub use sample_time::SampleTime; pub use sample_time::SampleTime;
use crate::peripherals; use crate::peripherals;
@ -25,14 +25,14 @@ use crate::peripherals;
pub struct Adc<'d, T: Instance> { pub struct Adc<'d, T: Instance> {
#[allow(unused)] #[allow(unused)]
adc: crate::PeripheralRef<'d, T>, adc: crate::PeripheralRef<'d, T>,
#[cfg(not(adc_f3))] #[cfg(not(any(adc_f3, adc_f3_v2)))]
sample_time: SampleTime, sample_time: SampleTime,
} }
pub(crate) mod sealed { pub(crate) mod sealed {
pub trait Instance { pub trait Instance {
fn regs() -> crate::pac::adc::Adc; fn regs() -> crate::pac::adc::Adc;
#[cfg(not(any(adc_f1, adc_v1, adc_f3)))] #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))]
fn common_regs() -> crate::pac::adccommon::AdcCommon; fn common_regs() -> crate::pac::adccommon::AdcCommon;
} }
@ -60,7 +60,7 @@ foreach_peripheral!(
fn regs() -> crate::pac::adc::Adc { fn regs() -> crate::pac::adc::Adc {
crate::pac::$inst crate::pac::$inst
} }
#[cfg(not(any(adc_f1, adc_v1, adc_f3)))] #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))]
fn common_regs() -> crate::pac::adccommon::AdcCommon { fn common_regs() -> crate::pac::adccommon::AdcCommon {
foreach_peripheral!{ foreach_peripheral!{
(adccommon, $common_inst:ident) => { (adccommon, $common_inst:ident) => {

View File

@ -1,4 +1,4 @@
#[cfg(not(adc_f3))] #[cfg(not(any(adc_f3, adc_f3_v2)))]
macro_rules! impl_sample_time { macro_rules! impl_sample_time {
($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { ($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.")] #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")]

View File

@ -478,7 +478,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
pub async fn read(&mut self) -> Result<Envelope, BusError> { pub async fn read(&mut self) -> Result<Envelope, BusError> {
poll_fn(|cx| { poll_fn(|cx| {
T::state().err_waker.register(cx.waker()); T::state().err_waker.register(cx.waker());
if let Poll::Ready(envelope) = T::state().rx_queue.recv().poll_unpin(cx) { if let Poll::Ready(envelope) = T::state().rx_queue.receive().poll_unpin(cx) {
return Poll::Ready(Ok(envelope)); return Poll::Ready(Ok(envelope));
} else if let Some(err) = self.curr_error() { } else if let Some(err) = self.curr_error() {
return Poll::Ready(Err(err)); return Poll::Ready(Err(err));
@ -493,7 +493,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
/// ///
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
if let Ok(envelope) = T::state().rx_queue.try_recv() { if let Ok(envelope) = T::state().rx_queue.try_receive() {
return Ok(envelope); return Ok(envelope);
} }
@ -506,14 +506,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
/// Waits while receive queue is empty. /// Waits while receive queue is empty.
pub async fn wait_not_empty(&mut self) { pub async fn wait_not_empty(&mut self) {
poll_fn(|cx| { poll_fn(|cx| T::state().rx_queue.poll_ready_to_receive(cx)).await
if T::state().rx_queue.poll_ready_to_receive(cx) {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await
} }
fn curr_error(&self) -> Option<BusError> { fn curr_error(&self) -> Option<BusError> {

View File

@ -393,6 +393,10 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
fn reset_complete_count(&mut self) -> usize { fn reset_complete_count(&mut self) -> usize {
STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel)
} }
fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.0.index()].register(waker);
}
} }
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
@ -463,7 +467,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
} }
/// Read elements from the ring buffer /// Read elements from the ring buffer
@ -472,7 +476,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
/// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf)
} }
/// Read an exact number of elements from the ringbuffer. /// Read an exact number of elements from the ringbuffer.
@ -487,39 +491,18 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
/// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.
/// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.
pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> { pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> {
use core::future::poll_fn; self.ringbuf
use core::sync::atomic::compiler_fence; .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
let mut read_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
self.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.read(&mut buffer[read_data..buffer_len]) {
Ok((len, remaining)) => {
read_data += len;
if read_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
/// The capacity of the ringbuffer. /// The capacity of the ringbuffer.
pub fn cap(&self) -> usize { pub const fn cap(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()
} }
pub fn set_waker(&mut self, waker: &Waker) { pub fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.channel.index()].register(waker); DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
} }
fn clear_irqs(&mut self) { fn clear_irqs(&mut self) {
@ -628,50 +611,29 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
} }
/// Write elements to the ring buffer /// Write elements to the ring buffer
/// Return a tuple of the length written and the length remaining in the buffer /// Return a tuple of the length written and the length remaining in the buffer
pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf)
} }
/// Write an exact number of elements to the ringbuffer. /// Write an exact number of elements to the ringbuffer.
pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> { pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> {
use core::future::poll_fn; self.ringbuf
use core::sync::atomic::compiler_fence; .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
let mut written_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
self.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.write(&buffer[written_data..buffer_len]) {
Ok((len, remaining)) => {
written_data += len;
if written_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
/// The capacity of the ringbuffer. /// The capacity of the ringbuffer.
pub fn cap(&self) -> usize { pub const fn cap(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()
} }
pub fn set_waker(&mut self, waker: &Waker) { pub fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.channel.index()].register(waker); DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
} }
fn clear_irqs(&mut self) { fn clear_irqs(&mut self) {

View File

@ -623,6 +623,10 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
fn reset_complete_count(&mut self) -> usize { fn reset_complete_count(&mut self) -> usize {
STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel)
} }
fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.0.index()].register(waker);
}
} }
pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { pub struct ReadableRingBuffer<'a, C: Channel, W: Word> {
@ -708,7 +712,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
} }
/// Read elements from the ring buffer /// Read elements from the ring buffer
@ -717,7 +721,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
/// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf)
} }
/// Read an exact number of elements from the ringbuffer. /// Read an exact number of elements from the ringbuffer.
@ -732,39 +736,18 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
/// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.
/// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.
pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> { pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> {
use core::future::poll_fn; self.ringbuf
use core::sync::atomic::compiler_fence; .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
let mut read_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
self.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.read(&mut buffer[read_data..buffer_len]) {
Ok((len, remaining)) => {
read_data += len;
if read_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
// The capacity of the ringbuffer // The capacity of the ringbuffer
pub fn cap(&self) -> usize { pub const fn cap(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()
} }
pub fn set_waker(&mut self, waker: &Waker) { pub fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.channel.index()].register(waker); DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
} }
fn clear_irqs(&mut self) { fn clear_irqs(&mut self) {
@ -890,50 +873,29 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow()));
} }
/// Write elements from the ring buffer /// Write elements from the ring buffer
/// Return a tuple of the length written and the length remaining in the buffer /// Return a tuple of the length written and the length remaining in the buffer
pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf)
} }
/// Write an exact number of elements to the ringbuffer. /// Write an exact number of elements to the ringbuffer.
pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> { pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> {
use core::future::poll_fn; self.ringbuf
use core::sync::atomic::compiler_fence; .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
let mut written_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
self.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.write(&buffer[written_data..buffer_len]) {
Ok((len, remaining)) => {
written_data += len;
if written_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
// The capacity of the ringbuffer // The capacity of the ringbuffer
pub fn cap(&self) -> usize { pub const fn cap(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()
} }
pub fn set_waker(&mut self, waker: &Waker) { pub fn set_waker(&mut self, waker: &Waker) {
STATE.ch_wakers[self.channel.index()].register(waker); DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);
} }
fn clear_irqs(&mut self) { fn clear_irqs(&mut self) {

View File

@ -1,7 +1,9 @@
#![cfg_attr(gpdma, allow(unused))] #![cfg_attr(gpdma, allow(unused))]
use core::future::poll_fn;
use core::ops::Range; use core::ops::Range;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
use core::task::{Poll, Waker};
use super::word::Word; use super::word::Word;
@ -49,6 +51,9 @@ pub trait DmaCtrl {
/// Reset the transfer completed counter to 0 and return the value just prior to the reset. /// Reset the transfer completed counter to 0 and return the value just prior to the reset.
fn reset_complete_count(&mut self) -> usize; fn reset_complete_count(&mut self) -> usize;
/// Set the waker for a running poll_fn
fn set_waker(&mut self, waker: &Waker);
} }
impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
@ -57,7 +62,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
} }
/// Reset the ring buffer to its initial state /// Reset the ring buffer to its initial state
pub fn clear(&mut self, mut dma: impl DmaCtrl) { pub fn clear(&mut self, dma: &mut impl DmaCtrl) {
self.start = 0; self.start = 0;
dma.reset_complete_count(); dma.reset_complete_count();
} }
@ -68,8 +73,43 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
} }
/// The current position of the ringbuffer /// The current position of the ringbuffer
fn pos(&self, remaining_transfers: usize) -> usize { fn pos(&self, dma: &mut impl DmaCtrl) -> usize {
self.cap() - remaining_transfers self.cap() - dma.get_remaining_transfers()
}
/// Read an exact number of elements from the ringbuffer.
///
/// Returns the remaining number of elements available for immediate reading.
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
///
/// Async/Wake Behavior:
/// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point,
/// and when it wraps around. This means that when called with a buffer of length 'M', when this
/// ring buffer was created with a buffer of size 'N':
/// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.
/// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.
pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result<usize, OverrunError> {
let mut read_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
dma.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.read(dma, &mut buffer[read_data..buffer_len]) {
Ok((len, remaining)) => {
read_data += len;
if read_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
/// Read elements from the ring buffer /// Read elements from the ring buffer
@ -77,7 +117,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
/// If not all of the elements were read, then there will be some elements in the buffer remaining /// If not all of the elements were read, then there will be some elements in the buffer remaining
/// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
/* /*
This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check
after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed
@ -93,7 +133,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
rather than the data we actually copied because it costs nothing and confirms an error condition rather than the data we actually copied because it costs nothing and confirms an error condition
earlier. earlier.
*/ */
let end = self.pos(dma.get_remaining_transfers()); let end = self.pos(dma);
if self.start == end && dma.get_complete_count() == 0 { if self.start == end && dma.get_complete_count() == 0 {
// No elements are available in the buffer // No elements are available in the buffer
Ok((0, self.cap())) Ok((0, self.cap()))
@ -114,8 +154,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
then, get the current position of of the dma write and check then, get the current position of of the dma write and check
if it's inside data we could have copied if it's inside data we could have copied
*/ */
let (pos, complete_count) = let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count()));
critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count()));
if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 { if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -141,7 +180,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
then, get the current position of of the dma write and check then, get the current position of of the dma write and check
if it's inside data we could have copied if it's inside data we could have copied
*/ */
let pos = self.pos(dma.get_remaining_transfers()); let pos = self.pos(dma);
if pos > self.start || pos < end || dma.get_complete_count() > 1 { if pos > self.start || pos < end || dma.get_complete_count() > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -169,7 +208,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
then, get the current position of of the dma write and check then, get the current position of of the dma write and check
if it's inside data we could have copied if it's inside data we could have copied
*/ */
let pos = self.pos(dma.get_remaining_transfers()); let pos = self.pos(dma);
if pos > self.start || pos < end || dma.reset_complete_count() > 1 { if pos > self.start || pos < end || dma.reset_complete_count() > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -209,7 +248,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
} }
/// Reset the ring buffer to its initial state /// Reset the ring buffer to its initial state
pub fn clear(&mut self, mut dma: impl DmaCtrl) { pub fn clear(&mut self, dma: &mut impl DmaCtrl) {
self.end = 0; self.end = 0;
dma.reset_complete_count(); dma.reset_complete_count();
} }
@ -220,14 +259,39 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
} }
/// The current position of the ringbuffer /// The current position of the ringbuffer
fn pos(&self, remaining_transfers: usize) -> usize { fn pos(&self, dma: &mut impl DmaCtrl) -> usize {
self.cap() - remaining_transfers self.cap() - dma.get_remaining_transfers()
}
/// Write an exact number of elements to the ringbuffer.
pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, OverrunError> {
let mut written_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
dma.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.write(dma, &buffer[written_data..buffer_len]) {
Ok((len, remaining)) => {
written_data += len;
if written_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
} }
/// Write elements from the ring buffer /// Write elements from the ring buffer
/// Return a tuple of the length written and the capacity remaining to be written in the buffer /// Return a tuple of the length written and the capacity remaining to be written in the buffer
pub fn write(&mut self, mut dma: impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> {
let start = self.pos(dma.get_remaining_transfers()); let start = self.pos(dma);
if start > self.end { if start > self.end {
// The occupied portion in the ring buffer DOES wrap // The occupied portion in the ring buffer DOES wrap
let len = self.copy_from(buf, self.end..start); let len = self.copy_from(buf, self.end..start);
@ -235,8 +299,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
// Confirm that the DMA is not inside data we could have written // Confirm that the DMA is not inside data we could have written
let (pos, complete_count) = let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count()));
critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count()));
if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 { if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -256,7 +319,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
// Confirm that the DMA is not inside data we could have written // Confirm that the DMA is not inside data we could have written
let pos = self.pos(dma.get_remaining_transfers()); let pos = self.pos(dma);
if pos > self.end || pos < start || dma.get_complete_count() > 1 { if pos > self.end || pos < start || dma.get_complete_count() > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -274,7 +337,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
// Confirm that the DMA is not inside data we could have written // Confirm that the DMA is not inside data we could have written
let pos = self.pos(dma.get_remaining_transfers()); let pos = self.pos(dma);
if pos > self.end || pos < start || dma.reset_complete_count() > 1 { if pos > self.end || pos < start || dma.reset_complete_count() > 1 {
Err(OverrunError) Err(OverrunError)
} else { } else {
@ -323,7 +386,7 @@ mod tests {
requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>, requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>,
} }
impl DmaCtrl for &mut TestCircularTransfer { impl DmaCtrl for TestCircularTransfer {
fn get_remaining_transfers(&self) -> usize { fn get_remaining_transfers(&self) -> usize {
match self.requests.borrow_mut().pop().unwrap() { match self.requests.borrow_mut().pop().unwrap() {
TestCircularTransferRequest::PositionRequest(pos) => { TestCircularTransferRequest::PositionRequest(pos) => {
@ -350,6 +413,8 @@ mod tests {
_ => unreachable!(), _ => unreachable!(),
} }
} }
fn set_waker(&mut self, waker: &Waker) {}
} }
impl TestCircularTransfer { impl TestCircularTransfer {

View File

@ -11,7 +11,7 @@ pub const fn is_default_layout() -> bool {
} }
const fn is_dual_bank() -> bool { const fn is_dual_bank() -> bool {
FLASH_REGIONS.len() == 2 FLASH_REGIONS.len() >= 2
} }
pub fn get_flash_regions() -> &'static [&'static FlashRegion] { pub fn get_flash_regions() -> &'static [&'static FlashRegion] {
@ -49,6 +49,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE])
}; };
bank.cr().write(|w| { bank.cr().write(|w| {
w.set_pg(true); w.set_pg(true);
#[cfg(flash_h7)]
w.set_psize(2); // 32 bits at once w.set_psize(2); // 32 bits at once
}); });
cortex_m::asm::isb(); cortex_m::asm::isb();
@ -85,7 +86,10 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
let bank = pac::FLASH.bank(sector.bank as usize); let bank = pac::FLASH.bank(sector.bank as usize);
bank.cr().modify(|w| { bank.cr().modify(|w| {
w.set_ser(true); w.set_ser(true);
w.set_snb(sector.index_in_bank) #[cfg(flash_h7)]
w.set_snb(sector.index_in_bank);
#[cfg(flash_h7ab)]
w.set_ssn(sector.index_in_bank);
}); });
bank.cr().modify(|w| { bank.cr().modify(|w| {
@ -126,6 +130,10 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
error!("incerr"); error!("incerr");
return Err(Error::Seq); return Err(Error::Seq);
} }
if sr.crcrderr() {
error!("crcrderr");
return Err(Error::Seq);
}
if sr.operr() { if sr.operr() {
return Err(Error::Prog); return Err(Error::Prog);
} }

View File

@ -65,9 +65,11 @@ impl FlashRegion {
#[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_f7, path = "f7.rs")]
#[cfg_attr(flash_g0, path = "g0.rs")] #[cfg_attr(flash_g0, path = "g0.rs")]
#[cfg_attr(flash_h7, path = "h7.rs")] #[cfg_attr(flash_h7, path = "h7.rs")]
#[cfg_attr(flash_h7ab, path = "h7.rs")]
#[cfg_attr( #[cfg_attr(
not(any( not(any(
flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_h7 flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_h7,
flash_h7ab
)), )),
path = "other.rs" path = "other.rs"
)] )]

View File

@ -378,22 +378,6 @@ pub(crate) unsafe fn init(config: Config) {
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(ahb_freq <= Hertz(120_000_000)); assert!(ahb_freq <= Hertz(120_000_000));
let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq));
FLASH.acr().modify(|w| w.set_latency(flash_ws));
RCC.cfgr().modify(|w| {
w.set_sw(sw.into());
w.set_hpre(config.ahb_pre.into());
w.set_ppre1(config.apb1_pre.into());
w.set_ppre2(config.apb2_pre.into());
});
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
// Turn off HSI to save power if we don't need it
if !config.hsi {
RCC.cr().modify(|w| w.set_hsion(false));
}
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
@ -414,6 +398,22 @@ pub(crate) unsafe fn init(config: Config) {
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
assert!(apb2_freq <= Hertz(60_000_000)); assert!(apb2_freq <= Hertz(60_000_000));
let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq));
FLASH.acr().modify(|w| w.set_latency(flash_ws));
RCC.cfgr().modify(|w| {
w.set_sw(sw.into());
w.set_hpre(config.ahb_pre.into());
w.set_ppre1(config.apb1_pre.into());
w.set_ppre2(config.apb2_pre.into());
});
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
// Turn off HSI to save power if we don't need it
if !config.hsi {
RCC.cr().modify(|w| w.set_hsion(false));
}
set_freqs(Clocks { set_freqs(Clocks {
sys: sys_clk, sys: sys_clk,
ahb1: ahb_freq, ahb1: ahb_freq,

View File

@ -200,6 +200,7 @@ fn flash_setup(rcc_aclk: u32, vos: VoltageScale) {
// See RM0433 Rev 7 Table 17. FLASH recommended number of wait // See RM0433 Rev 7 Table 17. FLASH recommended number of wait
// states and programming delay // states and programming delay
#[cfg(flash_h7)]
let (wait_states, progr_delay) = match vos { let (wait_states, progr_delay) = match vos {
// VOS 0 range VCORE 1.26V - 1.40V // VOS 0 range VCORE 1.26V - 1.40V
VoltageScale::Scale0 => match rcc_aclk_mhz { VoltageScale::Scale0 => match rcc_aclk_mhz {
@ -239,6 +240,50 @@ fn flash_setup(rcc_aclk: u32, vos: VoltageScale) {
}, },
}; };
// See RM0455 Rev 10 Table 16. FLASH recommended number of wait
// states and programming delay
#[cfg(flash_h7ab)]
let (wait_states, progr_delay) = match vos {
// VOS 0 range VCORE 1.25V - 1.35V
VoltageScale::Scale0 => match rcc_aclk_mhz {
0..=42 => (0, 0),
43..=84 => (1, 0),
85..=126 => (2, 1),
127..=168 => (3, 1),
169..=210 => (4, 2),
211..=252 => (5, 2),
253..=280 => (6, 3),
_ => (7, 3),
},
// VOS 1 range VCORE 1.15V - 1.25V
VoltageScale::Scale1 => match rcc_aclk_mhz {
0..=38 => (0, 0),
39..=76 => (1, 0),
77..=114 => (2, 1),
115..=152 => (3, 1),
153..=190 => (4, 2),
191..=225 => (5, 2),
_ => (7, 3),
},
// VOS 2 range VCORE 1.05V - 1.15V
VoltageScale::Scale2 => match rcc_aclk_mhz {
0..=34 => (0, 0),
35..=68 => (1, 0),
69..=102 => (2, 1),
103..=136 => (3, 1),
137..=160 => (4, 2),
_ => (7, 3),
},
// VOS 3 range VCORE 0.95V - 1.05V
VoltageScale::Scale3 => match rcc_aclk_mhz {
0..=22 => (0, 0),
23..=44 => (1, 0),
45..=66 => (2, 1),
67..=88 => (3, 1),
_ => (7, 3),
},
};
FLASH.acr().write(|w| { FLASH.acr().write(|w| {
w.set_wrhighfreq(progr_delay); w.set_wrhighfreq(progr_delay);
w.set_latency(wait_states) w.set_latency(wait_states)
@ -538,8 +583,6 @@ pub(crate) unsafe fn init(mut config: Config) {
let requested_pclk4 = config.pclk4.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); let requested_pclk4 = config.pclk4.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
let (rcc_pclk4, ppre4_bits, ppre4, _) = ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None); let (rcc_pclk4, ppre4_bits, ppre4, _) = ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None);
flash_setup(rcc_aclk, pwr_vos);
// Start switching clocks ------------------- // Start switching clocks -------------------
// Ensure CSI is on and stable // Ensure CSI is on and stable
@ -595,6 +638,8 @@ pub(crate) unsafe fn init(mut config: Config) {
// core voltage // core voltage
while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {} while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {}
flash_setup(rcc_aclk, pwr_vos);
// APB1 / APB2 Prescaler // APB1 / APB2 Prescaler
RCC.d2cfgr().modify(|w| { RCC.d2cfgr().modify(|w| {
w.set_d2ppre1(Dppre::from_bits(ppre1_bits)); w.set_d2ppre1(Dppre::from_bits(ppre1_bits));

View File

@ -410,10 +410,11 @@ pub(crate) unsafe fn init(config: Config) {
while RCC.cfgr().read().sws() != Sw::MSI {} while RCC.cfgr().read().sws() != Sw::MSI {}
} }
RCC.apb1enr1().modify(|w| w.set_pwren(true));
match config.rtc_mux { match config.rtc_mux {
RtcClockSource::LSE32 => { RtcClockSource::LSE32 => {
// 1. Unlock the backup domain // 1. Unlock the backup domain
RCC.apb1enr1().modify(|w| w.set_pwren(true));
PWR.cr1().modify(|w| w.set_dbp(true)); PWR.cr1().modify(|w| w.set_dbp(true));
// 2. Setup the LSE // 2. Setup the LSE

View File

@ -9,7 +9,7 @@ use crate::time::Hertz;
#[cfg_attr(rcc_f0, path = "f0.rs")] #[cfg_attr(rcc_f0, path = "f0.rs")]
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")]
#[cfg_attr(rcc_f2, path = "f2.rs")] #[cfg_attr(rcc_f2, path = "f2.rs")]
#[cfg_attr(rcc_f3, path = "f3.rs")] #[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")]
#[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")] #[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")]
#[cfg_attr(rcc_f7, path = "f7.rs")] #[cfg_attr(rcc_f7, path = "f7.rs")]
#[cfg_attr(rcc_c0, path = "c0.rs")] #[cfg_attr(rcc_c0, path = "c0.rs")]

View File

@ -2,6 +2,7 @@ pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale};
use crate::pac::pwr::vals::Dbp; use crate::pac::pwr::vals::Dbp;
use crate::pac::{FLASH, PWR, RCC}; use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::rtc::{Rtc, RtcClockSource as RCS};
use crate::time::Hertz; use crate::time::Hertz;
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
@ -229,6 +230,8 @@ pub(crate) unsafe fn init(config: Config) {
// Wait until LSE is running // Wait until LSE is running
while !RCC.bdcr().read().lserdy() {} while !RCC.bdcr().read().lserdy() {}
Rtc::set_clock_source(RCS::LSE);
} }
RtcClockSource::LSI32 => { RtcClockSource::LSI32 => {
// Turn on the internal 32 kHz LSI oscillator // Turn on the internal 32 kHz LSI oscillator
@ -236,6 +239,8 @@ pub(crate) unsafe fn init(config: Config) {
// Wait until LSI is running // Wait until LSI is running
while !RCC.csr().read().lsirdy() {} while !RCC.csr().read().lsirdy() {}
Rtc::set_clock_source(RCS::LSI);
} }
} }

View File

@ -73,15 +73,20 @@ impl<'d, T: Instance> Rng<'d, T> {
#[cfg(not(rng_v1))] #[cfg(not(rng_v1))]
pub fn reset(&mut self) { pub fn reset(&mut self) {
T::regs().cr().write(|reg| { T::regs().cr().write(|reg| {
reg.set_rngen(false);
reg.set_condrst(true); reg.set_condrst(true);
reg.set_nistc(pac::rng::vals::Nistc::CUSTOM);
// set RNG config "A" according to reference manual // set RNG config "A" according to reference manual
// this has to be written within the same write access as setting the CONDRST bit // this has to be written within the same write access as setting the CONDRST bit
reg.set_nistc(pac::rng::vals::Nistc::DEFAULT);
reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIGA); reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIGA);
reg.set_clkdiv(pac::rng::vals::Clkdiv::NODIV);
reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIGA_B); reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIGA_B);
reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIGA); reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIGA);
reg.set_clkdiv(pac::rng::vals::Clkdiv::NODIV); reg.set_ced(true);
reg.set_ie(false);
reg.set_rngen(true);
});
T::regs().cr().write(|reg| {
reg.set_ced(false);
}); });
// wait for CONDRST to be set // wait for CONDRST to be set
while !T::regs().cr().read().condrst() {} while !T::regs().cr().read().condrst() {}

View File

@ -72,6 +72,7 @@ impl core::ops::Sub for RtcInstant {
} }
} }
#[allow(dead_code)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub(crate) enum WakeupPrescaler { pub(crate) enum WakeupPrescaler {
Div2, Div2,
@ -120,6 +121,7 @@ impl From<WakeupPrescaler> for u32 {
} }
} }
#[allow(dead_code)]
impl WakeupPrescaler { impl WakeupPrescaler {
pub fn compute_min(val: u32) -> Self { pub fn compute_min(val: u32) -> Self {
*[ *[

View File

@ -100,14 +100,19 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
} }
pub fn get_max_duty(&self) -> u16 { pub fn get_max_duty(&self) -> u16 {
self.inner.get_max_compare_value() self.inner.get_max_compare_value() + 1
} }
pub fn set_duty(&mut self, channel: Channel, duty: u16) { pub fn set_duty(&mut self, channel: Channel, duty: u16) {
assert!(duty < self.get_max_duty()); assert!(duty <= self.get_max_duty());
self.inner.set_compare_value(channel, duty) self.inner.set_compare_value(channel, duty)
} }
pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
self.inner.set_output_polarity(channel, polarity);
self.inner.set_complementary_output_polarity(channel, polarity);
}
/// Set the dead time as a proportion of max_duty /// Set the dead time as a proportion of max_duty
pub fn set_dead_time(&mut self, value: u16) { pub fn set_dead_time(&mut self, value: u16) {
let (ckd, value) = compute_dead_time_value(value); let (ckd, value) = compute_dead_time_value(value);

View File

@ -53,6 +53,8 @@ pub(crate) mod sealed {
fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode);
fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity);
fn enable_channel(&mut self, channel: Channel, enable: bool); fn enable_channel(&mut self, channel: Channel, enable: bool);
fn set_compare_value(&mut self, channel: Channel, value: u16); fn set_compare_value(&mut self, channel: Channel, value: u16);
@ -61,6 +63,8 @@ pub(crate) mod sealed {
} }
pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance {
fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity);
fn set_dead_time_clock_division(&mut self, value: vals::Ckd); fn set_dead_time_clock_division(&mut self, value: vals::Ckd);
fn set_dead_time_value(&mut self, value: u8); fn set_dead_time_value(&mut self, value: u8);
@ -71,6 +75,8 @@ pub(crate) mod sealed {
pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance { pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance {
fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode);
fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity);
fn enable_channel(&mut self, channel: Channel, enable: bool); fn enable_channel(&mut self, channel: Channel, enable: bool);
fn set_compare_value(&mut self, channel: Channel, value: u32); fn set_compare_value(&mut self, channel: Channel, value: u32);
@ -125,6 +131,21 @@ impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm {
} }
} }
#[derive(Clone, Copy)]
pub enum OutputPolarity {
ActiveHigh,
ActiveLow,
}
impl From<OutputPolarity> for bool {
fn from(mode: OutputPolarity) -> Self {
match mode {
OutputPolarity::ActiveHigh => false,
OutputPolarity::ActiveLow => true,
}
}
}
pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {}
pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {}
@ -265,6 +286,13 @@ macro_rules! impl_compare_capable_16bit {
.modify(|w| w.set_ocm(raw_channel % 2, mode.into())); .modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
} }
fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
use sealed::GeneralPurpose16bitInstance;
Self::regs_gp16()
.ccer()
.modify(|w| w.set_ccp(channel.raw(), polarity.into()));
}
fn enable_channel(&mut self, channel: Channel, enable: bool) { fn enable_channel(&mut self, channel: Channel, enable: bool) {
use sealed::GeneralPurpose16bitInstance; use sealed::GeneralPurpose16bitInstance;
Self::regs_gp16() Self::regs_gp16()
@ -325,6 +353,13 @@ foreach_interrupt! {
Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
} }
fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
use crate::timer::sealed::GeneralPurpose32bitInstance;
Self::regs_gp32()
.ccer()
.modify(|w| w.set_ccp(channel.raw(), polarity.into()));
}
fn enable_channel(&mut self, channel: Channel, enable: bool) { fn enable_channel(&mut self, channel: Channel, enable: bool) {
use crate::timer::sealed::GeneralPurpose32bitInstance; use crate::timer::sealed::GeneralPurpose32bitInstance;
Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable)); Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable));
@ -388,6 +423,13 @@ foreach_interrupt! {
.modify(|w| w.set_ocm(raw_channel % 2, mode.into())); .modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
} }
fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
use crate::timer::sealed::AdvancedControlInstance;
Self::regs_advanced()
.ccer()
.modify(|w| w.set_ccp(channel.raw(), polarity.into()));
}
fn enable_channel(&mut self, channel: Channel, enable: bool) { fn enable_channel(&mut self, channel: Channel, enable: bool) {
use crate::timer::sealed::AdvancedControlInstance; use crate::timer::sealed::AdvancedControlInstance;
Self::regs_advanced() Self::regs_advanced()
@ -409,6 +451,13 @@ foreach_interrupt! {
} }
impl sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { impl sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {
fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
use crate::timer::sealed::AdvancedControlInstance;
Self::regs_advanced()
.ccer()
.modify(|w| w.set_ccnp(channel.raw(), polarity.into()));
}
fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { fn set_dead_time_clock_division(&mut self, value: vals::Ckd) {
use crate::timer::sealed::AdvancedControlInstance; use crate::timer::sealed::AdvancedControlInstance;
Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); Self::regs_advanced().cr1().modify(|w| w.set_ckd(value));

View File

@ -97,11 +97,15 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
} }
pub fn get_max_duty(&self) -> u16 { pub fn get_max_duty(&self) -> u16 {
self.inner.get_max_compare_value() self.inner.get_max_compare_value() + 1
} }
pub fn set_duty(&mut self, channel: Channel, duty: u16) { pub fn set_duty(&mut self, channel: Channel, duty: u16) {
assert!(duty < self.get_max_duty()); assert!(duty <= self.get_max_duty());
self.inner.set_compare_value(channel, duty) self.inner.set_compare_value(channel, duty)
} }
pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
self.inner.set_output_polarity(channel, polarity);
}
} }

View File

@ -584,15 +584,15 @@ mod eh02 {
mod eh1 { mod eh1 {
use super::*; use super::*;
impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> {
type Error = Error; type Error = Error;
} }
impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> {
type Error = Error; type Error = Error;
} }
impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> {
type Error = Error; type Error = Error;
} }
@ -602,16 +602,6 @@ mod eh1 {
} }
} }
impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer).map(drop)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
}
}
impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)
@ -628,16 +618,6 @@ mod eh1 {
} }
} }
impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUart<'d, T> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.tx.blocking_write(buffer).map(drop)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.tx.blocking_flush()
}
}
impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)

View File

@ -809,45 +809,57 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx:
Kind::Uart => (1, 0x10, 0x1_0000), Kind::Uart => (1, 0x10, 0x1_0000),
}; };
fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 {
// The calculation to be done to get the BRR is `mul * pclk / presc / baud`
// To do this in 32-bit only we can't multiply `mul` and `pclk`
let clock = pclk / presc;
// The mul is applied as the last operation to prevent overflow
let brr = clock / baud * mul;
// The BRR calculation will be a bit off because of integer rounding.
// Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul.
let rounding = ((clock % baud) * mul + (baud / 2)) / baud;
brr + rounding
}
#[cfg(not(usart_v1))] #[cfg(not(usart_v1))]
let mut over8 = false; let mut over8 = false;
let mut found = None; let mut found_brr = None;
for &(presc, _presc_val) in &DIVS { for &(presc, _presc_val) in &DIVS {
let denom = (config.baudrate * presc as u32) as u64; let brr = calculate_brr(config.baudrate, pclk_freq.0, presc as u32, mul);
let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom;
trace!( trace!(
"USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})",
presc, presc,
div, brr,
div >> 4, brr >> 4,
div & 0x0F brr & 0x0F
); );
if div < brr_min { if brr < brr_min {
#[cfg(not(usart_v1))] #[cfg(not(usart_v1))]
if div * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) {
over8 = true; over8 = true;
let div = div as u32; r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07)));
r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07)));
#[cfg(usart_v4)] #[cfg(usart_v4)]
r.presc().write(|w| w.set_prescaler(_presc_val)); r.presc().write(|w| w.set_prescaler(_presc_val));
found = Some(div); found_brr = Some(brr);
break; break;
} }
panic!("USART: baudrate too high"); panic!("USART: baudrate too high");
} }
if div < brr_max { if brr < brr_max {
let div = div as u32; r.brr().write_value(regs::Brr(brr));
r.brr().write_value(regs::Brr(div));
#[cfg(usart_v4)] #[cfg(usart_v4)]
r.presc().write(|w| w.set_prescaler(_presc_val)); r.presc().write(|w| w.set_prescaler(_presc_val));
found = Some(div); found_brr = Some(brr);
break; break;
} }
} }
let div = found.expect("USART: baudrate too low"); let brr = found_brr.expect("USART: baudrate too low");
#[cfg(not(usart_v1))] #[cfg(not(usart_v1))]
let oversampling = if over8 { "8 bit" } else { "16 bit" }; let oversampling = if over8 { "8 bit" } else { "16 bit" };
@ -857,7 +869,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx:
"Using {} oversampling, desired baudrate: {}, actual baudrate: {}", "Using {} oversampling, desired baudrate: {}, actual baudrate: {}",
oversampling, oversampling,
config.baudrate, config.baudrate,
(pclk_freq.0 * mul as u32) / div pclk_freq.0 / brr * mul
); );
r.cr2().write(|w| { r.cr2().write(|w| {
@ -943,27 +955,27 @@ mod eh02 {
mod eh1 { mod eh1 {
use super::*; use super::*;
impl embedded_hal_1::serial::Error for Error { impl embedded_hal_nb::serial::Error for Error {
fn kind(&self) -> embedded_hal_1::serial::ErrorKind { fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match *self { match *self {
Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat, Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
Self::Noise => embedded_hal_1::serial::ErrorKind::Noise, Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,
Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, Self::BufferTooLong => embedded_hal_nb::serial::ErrorKind::Other,
} }
} }
} }
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::ErrorType for Uart<'d, T, TxDma, RxDma> { impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::ErrorType for Uart<'d, T, TxDma, RxDma> {
type Error = Error; type Error = Error;
} }
impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::ErrorType for UartTx<'d, T, TxDma> { impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, TxDma> {
type Error = Error; type Error = Error;
} }
impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::ErrorType for UartRx<'d, T, RxDma> { impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, RxDma> {
type Error = Error; type Error = Error;
} }
@ -973,16 +985,6 @@ mod eh1 {
} }
} }
impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::Write for UartTx<'d, T, TxDma> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
}
}
impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> { impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map_err(nb::Error::Other) self.blocking_write(&[char]).map_err(nb::Error::Other)
@ -999,16 +1001,6 @@ mod eh1 {
} }
} }
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::Write for Uart<'d, T, TxDma, RxDma> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
}
}
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> { impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map_err(nb::Error::Other) self.blocking_write(&[char]).map_err(nb::Error::Other)

View File

@ -11,7 +11,7 @@ use core::marker::PhantomData;
/// ///
/// Note that, unlike other mutexes, implementations only guarantee no /// Note that, unlike other mutexes, implementations only guarantee no
/// concurrent access from other threads: concurrent access from the current /// concurrent access from other threads: concurrent access from the current
/// thread is allwed. For example, it's possible to lock the same mutex multiple times reentrantly. /// thread is allowed. For example, it's possible to lock the same mutex multiple times reentrantly.
/// ///
/// Therefore, locking a `RawMutex` is only enough to guarantee safe shared (`&`) access /// Therefore, locking a `RawMutex` is only enough to guarantee safe shared (`&`) access
/// to the data, it is not enough to guarantee exclusive (`&mut`) access. /// to the data, it is not enough to guarantee exclusive (`&mut`) access.

View File

@ -65,6 +65,13 @@ where
pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> { pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {
self.channel.try_send(message) self.channel.try_send(message)
} }
/// Allows a poll_fn to poll until the channel is ready to send
///
/// See [`Channel::poll_ready_to_send()`]
pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
self.channel.poll_ready_to_send(cx)
}
} }
/// Send-only access to a [`Channel`] without knowing channel size. /// Send-only access to a [`Channel`] without knowing channel size.
@ -106,6 +113,13 @@ impl<'ch, T> DynamicSender<'ch, T> {
pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> { pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {
self.channel.try_send_with_context(message, None) self.channel.try_send_with_context(message, None)
} }
/// Allows a poll_fn to poll until the channel is ready to send
///
/// See [`Channel::poll_ready_to_send()`]
pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
self.channel.poll_ready_to_send(cx)
}
} }
/// Receive-only access to a [`Channel`]. /// Receive-only access to a [`Channel`].
@ -133,16 +147,30 @@ where
{ {
/// Receive the next value. /// Receive the next value.
/// ///
/// See [`Channel::recv()`]. /// See [`Channel::receive()`].
pub fn recv(&self) -> RecvFuture<'_, M, T, N> { pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> {
self.channel.recv() self.channel.receive()
} }
/// Attempt to immediately receive the next value. /// Attempt to immediately receive the next value.
/// ///
/// See [`Channel::try_recv()`] /// See [`Channel::try_receive()`]
pub fn try_recv(&self) -> Result<T, TryRecvError> { pub fn try_receive(&self) -> Result<T, TryReceiveError> {
self.channel.try_recv() self.channel.try_receive()
}
/// Allows a poll_fn to poll until the channel is ready to receive
///
/// See [`Channel::poll_ready_to_receive()`]
pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
self.channel.poll_ready_to_receive(cx)
}
/// Poll the channel for the next item
///
/// See [`Channel::poll_receive()`]
pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
self.channel.poll_receive(cx)
} }
} }
@ -162,16 +190,30 @@ impl<'ch, T> Copy for DynamicReceiver<'ch, T> {}
impl<'ch, T> DynamicReceiver<'ch, T> { impl<'ch, T> DynamicReceiver<'ch, T> {
/// Receive the next value. /// Receive the next value.
/// ///
/// See [`Channel::recv()`]. /// See [`Channel::receive()`].
pub fn recv(&self) -> DynamicRecvFuture<'_, T> { pub fn receive(&self) -> DynamicReceiveFuture<'_, T> {
DynamicRecvFuture { channel: self.channel } DynamicReceiveFuture { channel: self.channel }
} }
/// Attempt to immediately receive the next value. /// Attempt to immediately receive the next value.
/// ///
/// See [`Channel::try_recv()`] /// See [`Channel::try_receive()`]
pub fn try_recv(&self) -> Result<T, TryRecvError> { pub fn try_receive(&self) -> Result<T, TryReceiveError> {
self.channel.try_recv_with_context(None) self.channel.try_receive_with_context(None)
}
/// Allows a poll_fn to poll until the channel is ready to receive
///
/// See [`Channel::poll_ready_to_receive()`]
pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
self.channel.poll_ready_to_receive(cx)
}
/// Poll the channel for the next item
///
/// See [`Channel::poll_receive()`]
pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
self.channel.poll_receive(cx)
} }
} }
@ -184,42 +226,39 @@ where
} }
} }
/// Future returned by [`Channel::recv`] and [`Receiver::recv`]. /// Future returned by [`Channel::receive`] and [`Receiver::receive`].
#[must_use = "futures do nothing unless you `.await` or poll them"] #[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct RecvFuture<'ch, M, T, const N: usize> pub struct ReceiveFuture<'ch, M, T, const N: usize>
where where
M: RawMutex, M: RawMutex,
{ {
channel: &'ch Channel<M, T, N>, channel: &'ch Channel<M, T, N>,
} }
impl<'ch, M, T, const N: usize> Future for RecvFuture<'ch, M, T, N> impl<'ch, M, T, const N: usize> Future for ReceiveFuture<'ch, M, T, N>
where where
M: RawMutex, M: RawMutex,
{ {
type Output = T; type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
match self.channel.try_recv_with_context(Some(cx)) { self.channel.poll_receive(cx)
Ok(v) => Poll::Ready(v),
Err(TryRecvError::Empty) => Poll::Pending,
}
} }
} }
/// Future returned by [`DynamicReceiver::recv`]. /// Future returned by [`DynamicReceiver::receive`].
#[must_use = "futures do nothing unless you `.await` or poll them"] #[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct DynamicRecvFuture<'ch, T> { pub struct DynamicReceiveFuture<'ch, T> {
channel: &'ch dyn DynamicChannel<T>, channel: &'ch dyn DynamicChannel<T>,
} }
impl<'ch, T> Future for DynamicRecvFuture<'ch, T> { impl<'ch, T> Future for DynamicReceiveFuture<'ch, T> {
type Output = T; type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
match self.channel.try_recv_with_context(Some(cx)) { match self.channel.try_receive_with_context(Some(cx)) {
Ok(v) => Poll::Ready(v), Ok(v) => Poll::Ready(v),
Err(TryRecvError::Empty) => Poll::Pending, Err(TryReceiveError::Empty) => Poll::Pending,
} }
} }
} }
@ -285,13 +324,18 @@ impl<'ch, T> Unpin for DynamicSendFuture<'ch, T> {}
trait DynamicChannel<T> { trait DynamicChannel<T> {
fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>>; fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>>;
fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryRecvError>; fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>;
fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()>;
fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()>;
fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T>;
} }
/// Error returned by [`try_recv`](Channel::try_recv). /// Error returned by [`try_receive`](Channel::try_receive).
#[derive(PartialEq, Eq, Clone, Copy, Debug)] #[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TryRecvError { pub enum TryReceiveError {
/// A message could not be received because the channel is empty. /// A message could not be received because the channel is empty.
Empty, Empty,
} }
@ -320,11 +364,11 @@ impl<T, const N: usize> ChannelState<T, N> {
} }
} }
fn try_recv(&mut self) -> Result<T, TryRecvError> { fn try_receive(&mut self) -> Result<T, TryReceiveError> {
self.try_recv_with_context(None) self.try_receive_with_context(None)
} }
fn try_recv_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result<T, TryRecvError> { fn try_receive_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
if self.queue.is_full() { if self.queue.is_full() {
self.senders_waker.wake(); self.senders_waker.wake();
} }
@ -335,14 +379,31 @@ impl<T, const N: usize> ChannelState<T, N> {
if let Some(cx) = cx { if let Some(cx) = cx {
self.receiver_waker.register(cx.waker()); self.receiver_waker.register(cx.waker());
} }
Err(TryRecvError::Empty) Err(TryReceiveError::Empty)
} }
} }
fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> bool { fn poll_receive(&mut self, cx: &mut Context<'_>) -> Poll<T> {
if self.queue.is_full() {
self.senders_waker.wake();
}
if let Some(message) = self.queue.pop_front() {
Poll::Ready(message)
} else {
self.receiver_waker.register(cx.waker());
Poll::Pending
}
}
fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> Poll<()> {
self.receiver_waker.register(cx.waker()); self.receiver_waker.register(cx.waker());
!self.queue.is_empty() if !self.queue.is_empty() {
Poll::Ready(())
} else {
Poll::Pending
}
} }
fn try_send(&mut self, message: T) -> Result<(), TrySendError<T>> { fn try_send(&mut self, message: T) -> Result<(), TrySendError<T>> {
@ -364,10 +425,14 @@ impl<T, const N: usize> ChannelState<T, N> {
} }
} }
fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> bool { fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> Poll<()> {
self.senders_waker.register(cx.waker()); self.senders_waker.register(cx.waker());
!self.queue.is_full() if !self.queue.is_full() {
Poll::Ready(())
} else {
Poll::Pending
}
} }
} }
@ -409,8 +474,13 @@ where
self.inner.lock(|rc| f(&mut *rc.borrow_mut())) self.inner.lock(|rc| f(&mut *rc.borrow_mut()))
} }
fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryRecvError> { fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
self.lock(|c| c.try_recv_with_context(cx)) self.lock(|c| c.try_receive_with_context(cx))
}
/// Poll the channel for the next message
pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
self.lock(|c| c.poll_receive(cx))
} }
fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> { fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {
@ -418,12 +488,12 @@ where
} }
/// Allows a poll_fn to poll until the channel is ready to receive /// Allows a poll_fn to poll until the channel is ready to receive
pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
self.lock(|c| c.poll_ready_to_receive(cx)) self.lock(|c| c.poll_ready_to_receive(cx))
} }
/// Allows a poll_fn to poll until the channel is ready to send /// Allows a poll_fn to poll until the channel is ready to send
pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
self.lock(|c| c.poll_ready_to_send(cx)) self.lock(|c| c.poll_ready_to_send(cx))
} }
@ -466,16 +536,16 @@ where
/// ///
/// If there are no messages in the channel's buffer, this method will /// If there are no messages in the channel's buffer, this method will
/// wait until a message is sent. /// wait until a message is sent.
pub fn recv(&self) -> RecvFuture<'_, M, T, N> { pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> {
RecvFuture { channel: self } ReceiveFuture { channel: self }
} }
/// Attempt to immediately receive a message. /// Attempt to immediately receive a message.
/// ///
/// This method will either receive a message from the channel immediately or return an error /// This method will either receive a message from the channel immediately or return an error
/// if the channel is empty. /// if the channel is empty.
pub fn try_recv(&self) -> Result<T, TryRecvError> { pub fn try_receive(&self) -> Result<T, TryReceiveError> {
self.lock(|c| c.try_recv()) self.lock(|c| c.try_receive())
} }
} }
@ -489,8 +559,20 @@ where
Channel::try_send_with_context(self, m, cx) Channel::try_send_with_context(self, m, cx)
} }
fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryRecvError> { fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
Channel::try_recv_with_context(self, cx) Channel::try_receive_with_context(self, cx)
}
fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {
Channel::poll_ready_to_send(self, cx)
}
fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {
Channel::poll_ready_to_receive(self, cx)
}
fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {
Channel::poll_receive(self, cx)
} }
} }
@ -534,15 +616,15 @@ mod tests {
fn receiving_once_with_one_send() { fn receiving_once_with_one_send() {
let mut c = ChannelState::<u32, 3>::new(); let mut c = ChannelState::<u32, 3>::new();
assert!(c.try_send(1).is_ok()); assert!(c.try_send(1).is_ok());
assert_eq!(c.try_recv().unwrap(), 1); assert_eq!(c.try_receive().unwrap(), 1);
assert_eq!(capacity(&c), 3); assert_eq!(capacity(&c), 3);
} }
#[test] #[test]
fn receiving_when_empty() { fn receiving_when_empty() {
let mut c = ChannelState::<u32, 3>::new(); let mut c = ChannelState::<u32, 3>::new();
match c.try_recv() { match c.try_receive() {
Err(TryRecvError::Empty) => assert!(true), Err(TryReceiveError::Empty) => assert!(true),
_ => assert!(false), _ => assert!(false),
} }
assert_eq!(capacity(&c), 3); assert_eq!(capacity(&c), 3);
@ -552,7 +634,7 @@ mod tests {
fn simple_send_and_receive() { fn simple_send_and_receive() {
let c = Channel::<NoopRawMutex, u32, 3>::new(); let c = Channel::<NoopRawMutex, u32, 3>::new();
assert!(c.try_send(1).is_ok()); assert!(c.try_send(1).is_ok());
assert_eq!(c.try_recv().unwrap(), 1); assert_eq!(c.try_receive().unwrap(), 1);
} }
#[test] #[test]
@ -572,7 +654,7 @@ mod tests {
let r: DynamicReceiver<'_, u32> = c.receiver().into(); let r: DynamicReceiver<'_, u32> = c.receiver().into();
assert!(s.try_send(1).is_ok()); assert!(s.try_send(1).is_ok());
assert_eq!(r.try_recv().unwrap(), 1); assert_eq!(r.try_receive().unwrap(), 1);
} }
#[futures_test::test] #[futures_test::test]
@ -587,14 +669,14 @@ mod tests {
assert!(c2.try_send(1).is_ok()); assert!(c2.try_send(1).is_ok());
}) })
.is_ok()); .is_ok());
assert_eq!(c.recv().await, 1); assert_eq!(c.receive().await, 1);
} }
#[futures_test::test] #[futures_test::test]
async fn sender_send_completes_if_capacity() { async fn sender_send_completes_if_capacity() {
let c = Channel::<CriticalSectionRawMutex, u32, 1>::new(); let c = Channel::<CriticalSectionRawMutex, u32, 1>::new();
c.send(1).await; c.send(1).await;
assert_eq!(c.recv().await, 1); assert_eq!(c.receive().await, 1);
} }
#[futures_test::test] #[futures_test::test]
@ -612,11 +694,11 @@ mod tests {
// Wish I could think of a means of determining that the async send is waiting instead. // Wish I could think of a means of determining that the async send is waiting instead.
// However, I've used the debugger to observe that the send does indeed wait. // However, I've used the debugger to observe that the send does indeed wait.
Delay::new(Duration::from_millis(500)).await; Delay::new(Duration::from_millis(500)).await;
assert_eq!(c.recv().await, 1); assert_eq!(c.receive().await, 1);
assert!(executor assert!(executor
.spawn(async move { .spawn(async move {
loop { loop {
c.recv().await; c.receive().await;
} }
}) })
.is_ok()); .is_ok());

View File

@ -152,8 +152,8 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true } log = { version = "0.4.14", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
futures-util = { version = "0.3.17", default-features = false } futures-util = { version = "0.3.17", default-features = false }
atomic-polyfill = "1.0.1" atomic-polyfill = "1.0.1"

View File

@ -248,6 +248,8 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> {
write_ep: D::EndpointIn, write_ep: D::EndpointIn,
_control: &'d ControlShared, _control: &'d ControlShared,
max_packet_size: usize,
} }
impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
@ -338,6 +340,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
read_ep, read_ep,
write_ep, write_ep,
_control: &state.shared, _control: &state.shared,
max_packet_size: max_packet_size as usize,
} }
} }
@ -349,6 +352,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
Sender { Sender {
write_ep: self.write_ep, write_ep: self.write_ep,
seq: 0, seq: 0,
max_packet_size: self.max_packet_size,
}, },
Receiver { Receiver {
data_if: self.data_if, data_if: self.data_if,
@ -365,6 +369,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
pub struct Sender<'d, D: Driver<'d>> { pub struct Sender<'d, D: Driver<'d>> {
write_ep: D::EndpointIn, write_ep: D::EndpointIn,
seq: u16, seq: u16,
max_packet_size: usize,
} }
impl<'d, D: Driver<'d>> Sender<'d, D> { impl<'d, D: Driver<'d>> Sender<'d, D> {
@ -375,8 +380,8 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
let seq = self.seq; let seq = self.seq;
self.seq = self.seq.wrapping_add(1); self.seq = self.seq.wrapping_add(1);
const MAX_PACKET_SIZE: usize = 64; // TODO unhardcode
const OUT_HEADER_LEN: usize = 28; const OUT_HEADER_LEN: usize = 28;
const ABS_MAX_PACKET_SIZE: usize = 512;
let header = NtbOutHeader { let header = NtbOutHeader {
nth_sig: SIG_NTH, nth_sig: SIG_NTH,
@ -395,27 +400,27 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
}; };
// Build first packet on a buffer, send next packets straight from `data`. // Build first packet on a buffer, send next packets straight from `data`.
let mut buf = [0; MAX_PACKET_SIZE]; let mut buf = [0; ABS_MAX_PACKET_SIZE];
let n = byteify(&mut buf, header); let n = byteify(&mut buf, header);
assert_eq!(n.len(), OUT_HEADER_LEN); assert_eq!(n.len(), OUT_HEADER_LEN);
if OUT_HEADER_LEN + data.len() < MAX_PACKET_SIZE { if OUT_HEADER_LEN + data.len() < self.max_packet_size {
// First packet is not full, just send it. // First packet is not full, just send it.
// No need to send ZLP because it's short for sure. // No need to send ZLP because it's short for sure.
buf[OUT_HEADER_LEN..][..data.len()].copy_from_slice(data); buf[OUT_HEADER_LEN..][..data.len()].copy_from_slice(data);
self.write_ep.write(&buf[..OUT_HEADER_LEN + data.len()]).await?; self.write_ep.write(&buf[..OUT_HEADER_LEN + data.len()]).await?;
} else { } else {
let (d1, d2) = data.split_at(MAX_PACKET_SIZE - OUT_HEADER_LEN); let (d1, d2) = data.split_at(self.max_packet_size - OUT_HEADER_LEN);
buf[OUT_HEADER_LEN..].copy_from_slice(d1); buf[OUT_HEADER_LEN..self.max_packet_size].copy_from_slice(d1);
self.write_ep.write(&buf).await?; self.write_ep.write(&buf[..self.max_packet_size]).await?;
for chunk in d2.chunks(MAX_PACKET_SIZE) { for chunk in d2.chunks(self.max_packet_size) {
self.write_ep.write(&chunk).await?; self.write_ep.write(&chunk).await?;
} }
// Send ZLP if needed. // Send ZLP if needed.
if d2.len() % MAX_PACKET_SIZE == 0 { if d2.len() % self.max_packet_size == 0 {
self.write_ep.write(&[]).await?; self.write_ep.write(&[]).await?;
} }
} }

View File

@ -526,7 +526,7 @@ impl<'a> PropertyData<'a> {
PropertyData::Binary(val) => val.len(), PropertyData::Binary(val) => val.len(),
PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val), PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val),
PropertyData::RegMultiSz(val) => { PropertyData::RegMultiSz(val) => {
core::mem::size_of::<u16>() * val.iter().map(|x| x.encode_utf16().count() + 1).sum::<usize>() + 1 core::mem::size_of::<u16>() * (val.iter().map(|x| x.encode_utf16().count() + 1).sum::<usize>() + 1)
} }
} }
} }

View File

@ -7,7 +7,7 @@ use core::cell::RefCell;
use defmt_rtt as _; use defmt_rtt as _;
use embassy_boot_rp::*; use embassy_boot_rp::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_rp::flash::{self, Flash}; use embassy_rp::flash::Flash;
use embassy_rp::gpio::{Level, Output}; use embassy_rp::gpio::{Level, Output};
use embassy_rp::watchdog::Watchdog; use embassy_rp::watchdog::Watchdog;
use embassy_sync::blocking_mutex::Mutex; use embassy_sync::blocking_mutex::Mutex;
@ -34,7 +34,7 @@ async fn main(_s: Spawner) {
let mut watchdog = Watchdog::new(p.WATCHDOG); let mut watchdog = Watchdog::new(p.WATCHDOG);
watchdog.start(Duration::from_secs(8)); watchdog.start(Duration::from_secs(8));
let flash = Flash::<_, flash::Blocking, FLASH_SIZE>::new(p.FLASH); let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH);
let flash = Mutex::new(RefCell::new(flash)); let flash = Mutex::new(RefCell::new(flash));
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);

View File

@ -33,9 +33,7 @@ fn main() -> ! {
let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
let active_offset = config.active.offset(); let active_offset = config.active.offset();
let mut bl: BootLoader<_, _, _> = BootLoader::new(config); let bl: BootLoader = BootLoader::prepare(config);
bl.prepare();
unsafe { bl.load(active_offset) } unsafe { bl.load(active_offset) }
} }

View File

@ -29,9 +29,7 @@ fn main() -> ! {
let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
let active_offset = config.active.offset(); let active_offset = config.active.offset();
let mut bl: BootLoader<_, _, _> = BootLoader::new(config); let bl: BootLoader = BootLoader::prepare(config);
bl.prepare();
unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) } unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) }
} }

View File

@ -27,9 +27,7 @@ fn main() -> ! {
let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
let active_offset = config.active.offset(); let active_offset = config.active.offset();
let mut bl: BootLoader<_, _, _, 2048> = BootLoader::new(config); let bl = BootLoader::prepare::<_, _, _, 2048>(config);
bl.prepare();
unsafe { bl.load(BANK1_REGION.base + active_offset) } unsafe { bl.load(BANK1_REGION.base + active_offset) }
} }

View File

@ -12,12 +12,14 @@ nightly = [
"embassy-nrf/nightly", "embassy-nrf/nightly",
"embassy-net/nightly", "embassy-net/nightly",
"embassy-net-esp-hosted", "embassy-net-esp-hosted",
"embassy-net-enc28j60",
"embassy-nrf/unstable-traits", "embassy-nrf/unstable-traits",
"embassy-time/nightly", "embassy-time/nightly",
"embassy-time/unstable-traits", "embassy-time/unstable-traits",
"static_cell/nightly", "static_cell/nightly",
"embassy-usb", "embassy-usb",
"embedded-io-async", "embedded-io-async",
"embedded-hal-bus/async",
"embassy-net", "embassy-net",
"embassy-lora", "embassy-lora",
"lora-phy", "lora-phy",
@ -40,6 +42,7 @@ lora-phy = { version = "1", optional = true }
lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }
embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true }
embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"], optional = true }
defmt = "0.3" defmt = "0.3"
defmt-rtt = "0.4" defmt-rtt = "0.4"
@ -54,9 +57,14 @@ rand = { version = "0.8.4", default-features = false }
embedded-storage = "0.3.0" embedded-storage = "0.3.0"
usbd-hid = "0.6.0" usbd-hid = "0.6.0"
serde = { version = "1.0.136", default-features = false } serde = { version = "1.0.136", default-features = false }
embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "1.0.0-rc.1", optional = true }
embedded-hal-bus = { version = "0.1.0-rc.1" }
num-integer = { version = "0.1.45", default-features = false } num-integer = { version = "0.1.45", default-features = false }
microfft = "0.5.0" microfft = "0.5.0"
[profile.release] [profile.release]
debug = 2 debug = 2
[patch.crates-io]
lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"}

View File

@ -35,7 +35,7 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(my_task())); unwrap!(spawner.spawn(my_task()));
loop { loop {
match CHANNEL.recv().await { match CHANNEL.receive().await {
LedState::On => led.set_high(), LedState::On => led.set_high(),
LedState::Off => led.set_low(), LedState::Off => led.set_low(),
} }

View File

@ -33,7 +33,7 @@ async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedSta
let mut led = Output::new(led, Level::Low, OutputDrive::Standard); let mut led = Output::new(led, Level::Low, OutputDrive::Standard);
loop { loop {
match receiver.recv().await { match receiver.receive().await {
LedState::On => led.set_high(), LedState::On => led.set_high(),
LedState::Off => led.set_low(), LedState::Off => led.set_low(),
} }

View File

@ -0,0 +1,124 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_net::tcp::TcpSocket;
use embassy_net::{Stack, StackResources};
use embassy_net_enc28j60::Enc28j60;
use embassy_nrf::gpio::{Level, Output, OutputDrive};
use embassy_nrf::rng::Rng;
use embassy_nrf::spim::Spim;
use embassy_nrf::{bind_interrupts, peripherals, spim};
use embassy_time::Delay;
use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_io_async::Write;
use static_cell::make_static;
use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs {
SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;
});
#[embassy_executor::task]
async fn net_task(
stack: &'static Stack<
Enc28j60<
ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static, peripherals::P0_15>, Delay>,
Output<'static, peripherals::P0_13>,
>,
>,
) -> ! {
stack.run().await
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = embassy_nrf::init(Default::default());
info!("running!");
let eth_sck = p.P0_20;
let eth_mosi = p.P0_22;
let eth_miso = p.P0_24;
let eth_cs = p.P0_15;
let eth_rst = p.P0_13;
let _eth_irq = p.P0_12;
let mut config = spim::Config::default();
config.frequency = spim::Frequency::M16;
let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config);
let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard);
let spi = ExclusiveDevice::new(spi, cs, Delay);
let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard);
let mac_addr = [2, 3, 4, 5, 6, 7];
let device = Enc28j60::new(spi, Some(rst), mac_addr);
let config = embassy_net::Config::dhcpv4(Default::default());
// let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
// address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
// dns_servers: Vec::new(),
// gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
// });
// Generate random seed
let mut rng = Rng::new(p.RNG, Irqs);
let mut seed = [0; 8];
rng.blocking_fill_bytes(&mut seed);
let seed = u64::from_le_bytes(seed);
// Init network stack
let stack = &*make_static!(Stack::new(
device,
config,
make_static!(StackResources::<2>::new()),
seed
));
unwrap!(spawner.spawn(net_task(stack)));
// And now we can use it!
let mut rx_buffer = [0; 4096];
let mut tx_buffer = [0; 4096];
let mut buf = [0; 4096];
loop {
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));
info!("Listening on TCP:1234...");
if let Err(e) = socket.accept(1234).await {
warn!("accept error: {:?}", e);
continue;
}
info!("Received connection from {:?}", socket.remote_endpoint());
loop {
let n = match socket.read(&mut buf).await {
Ok(0) => {
warn!("read EOF");
break;
}
Ok(n) => n,
Err(e) => {
warn!("read error: {:?}", e);
break;
}
};
info!("rxd {:02x}", &buf[..n]);
match socket.write_all(&buf[..n]).await {
Ok(()) => {}
Err(e) => {
warn!("write error: {:?}", e);
break;
}
};
}
}
}

View File

@ -46,7 +46,7 @@ async fn main(spawner: Spawner) {
// back out the buffer we receive from the read // back out the buffer we receive from the read
// task. // task.
loop { loop {
let buf = CHANNEL.recv().await; let buf = CHANNEL.receive().await;
info!("writing..."); info!("writing...");
unwrap!(tx.write(&buf).await); unwrap!(tx.write(&buf).await);
} }

View File

@ -11,7 +11,7 @@ use embassy_nrf::rng::Rng;
use embassy_nrf::spim::{self, Spim}; use embassy_nrf::spim::{self, Spim};
use embassy_nrf::{bind_interrupts, peripherals}; use embassy_nrf::{bind_interrupts, peripherals};
use embassy_time::Delay; use embassy_time::Delay;
use embedded_hal_async::spi::ExclusiveDevice; use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_io_async::Write; use embedded_io_async::Write;
use static_cell::make_static; use static_cell::make_static;
use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _};
@ -72,8 +72,8 @@ async fn main(spawner: Spawner) {
unwrap!(spawner.spawn(wifi_task(runner))); unwrap!(spawner.spawn(wifi_task(runner)));
control.init().await; unwrap!(control.init().await);
control.join(WIFI_NETWORK, WIFI_PASSWORD).await; unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await);
let config = embassy_net::Config::dhcpv4(Default::default()); let config = embassy_net::Config::dhcpv4(Default::default());
// let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {

View File

@ -13,7 +13,7 @@ embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["ni
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
embassy-net-w5500 = { version = "0.1.0", path = "../../embassy-net-w5500", features = ["defmt"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] }
@ -42,8 +42,9 @@ smart-leds = "0.3.0"
heapless = "0.7.15" heapless = "0.7.15"
usbd-hid = "0.6.1" usbd-hid = "0.6.1"
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
embedded-hal-async = "0.2.0-alpha.2" embedded-hal-async = "1.0.0-rc.1"
embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] }
embedded-io-async = { version = "0.5.0", features = ["defmt-03"] } embedded-io-async = { version = "0.5.0", features = ["defmt-03"] }
embedded-storage = { version = "0.3" } embedded-storage = { version = "0.3" }
static_cell = { version = "1.1", features = ["nightly"]} static_cell = { version = "1.1", features = ["nightly"]}
@ -54,3 +55,6 @@ rand = { version = "0.8.5", default-features = false }
[profile.release] [profile.release]
debug = 2 debug = 2
[patch.crates-io]
lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"}

View File

@ -10,13 +10,14 @@ use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_futures::yield_now; use embassy_futures::yield_now;
use embassy_net::{Stack, StackResources}; use embassy_net::{Stack, StackResources};
use embassy_net_w5500::*; use embassy_net_wiznet::chip::W5500;
use embassy_net_wiznet::*;
use embassy_rp::clocks::RoscRng; use embassy_rp::clocks::RoscRng;
use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::gpio::{Input, Level, Output, Pull};
use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0};
use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
use embassy_time::{Delay, Duration}; use embassy_time::{Delay, Duration};
use embedded_hal_async::spi::ExclusiveDevice; use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_io_async::Write; use embedded_io_async::Write;
use rand::RngCore; use rand::RngCore;
use static_cell::make_static; use static_cell::make_static;
@ -26,6 +27,7 @@ use {defmt_rtt as _, panic_probe as _};
async fn ethernet_task( async fn ethernet_task(
runner: Runner< runner: Runner<
'static, 'static,
W5500,
ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>, ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>,
Input<'static, PIN_21>, Input<'static, PIN_21>,
Output<'static, PIN_20>, Output<'static, PIN_20>,
@ -54,7 +56,7 @@ async fn main(spawner: Spawner) {
let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
let state = make_static!(State::<8, 8>::new()); let state = make_static!(State::<8, 8>::new());
let (device, runner) = embassy_net_w5500::new( let (device, runner) = embassy_net_wiznet::new(
mac_addr, mac_addr,
state, state,
ExclusiveDevice::new(spi, cs, Delay), ExclusiveDevice::new(spi, cs, Delay),

Some files were not shown because too many files have changed in this diff Show More