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
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/nrf -o webroot/crates/embassy-boot-nrf/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 ./cyw43 -o webroot/crates/cyw43/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-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})
kubectl cp webroot/crates $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.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.features": [
///"nightly",
// Uncomment if the example has a "nightly" feature.
"nightly",
],
"rust-analyzer.linkedProjects": [
// Declare for the target you wish to develop
// "embassy-executor/Cargo.toml",
// "embassy-sync/Cargo.toml",
"examples/stm32wl/Cargo.toml",
// Uncomment ONE line for the chip you want to work on.
// This makes rust-analyzer work on the example crate and all its dependencies.
"examples/nrf52840/Cargo.toml",
// "examples/nrf52840-rtic/Cargo.toml",
// "examples/nrf5340/Cargo.toml",
// "examples/nrf-rtos-trace/Cargo.toml",
// "examples/rp/Cargo.toml",

3
ci.sh
View File

@ -3,7 +3,7 @@
set -euo pipefail
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')
@ -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 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,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 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 \

View File

@ -24,7 +24,7 @@ cortex-m = "0.7.6"
cortex-m-rt = "0.7.0"
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 }
[package.metadata.embassy_docs]

View File

@ -102,7 +102,7 @@ where
cmd_buf[0] = cmd;
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)]

View File

@ -14,28 +14,17 @@ use embassy_nrf::wdt;
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
/// A bootloader for nRF devices.
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = PAGE_SIZE> {
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
}
pub struct BootLoader<const BUFFER_SIZE: usize = PAGE_SIZE>;
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE>
{
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
Self {
boot: embassy_boot::BootLoader::new(config),
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
}
}
/// 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");
impl<const BUFFER_SIZE: usize> BootLoader<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>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Self {
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
let mut boot = embassy_boot::BootLoader::new(config);
boot.prepare_boot(&mut aligned_buf.0).expect("Boot prepare error");
Self
}
/// 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.
#[cfg(not(feature = "softdevice"))]
pub unsafe fn load(self, start: u32) -> ! {
core::mem::drop(self.boot);
let mut p = cortex_m::Peripherals::steal();
p.SCB.invalidate_icache();
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.
#[cfg(feature = "softdevice")]
pub unsafe fn load(&mut self, _app: u32) -> ! {
pub unsafe fn load(self, _app: u32) -> ! {
use nrf_softdevice_mbr as mbr;
const NRF_SUCCESS: u32 = 0;

View File

@ -15,28 +15,17 @@ use embassy_time::Duration;
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
/// A bootloader for RP2040 devices.
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = ERASE_SIZE> {
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
}
pub struct BootLoader<const BUFFER_SIZE: usize = ERASE_SIZE>;
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE>
{
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
Self {
boot: embassy_boot::BootLoader::new(config),
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
}
}
/// 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");
impl<const BUFFER_SIZE: usize> BootLoader<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>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Self {
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
let mut boot = embassy_boot::BootLoader::new(config);
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
Self
}
/// 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.
pub unsafe fn load(self, start: u32) -> ! {
core::mem::drop(self.boot);
trace!("Loading app at 0x{:x}", start);
#[allow(unused_mut)]
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> {
/// Start a new watchdog with a given flash and watchdog peripheral and a timeout
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);
watchdog.start(timeout);
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> {
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> {
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;
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
self.watchdog.feed();
self.flash.read(offset, data)
self.flash.blocking_read(offset, data)
}
fn capacity(&self) -> usize {
self.flash.capacity()

View File

@ -11,28 +11,17 @@ pub use embassy_boot::{FirmwareState, FirmwareUpdater};
use embedded_storage::nor_flash::NorFlash;
/// A bootloader for STM32 devices.
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> {
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
}
pub struct BootLoader;
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE>
{
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
Self {
boot: embassy_boot::BootLoader::new(config),
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
}
}
/// 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");
impl BootLoader {
/// 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>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Self {
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
let mut boot = embassy_boot::BootLoader::new(config);
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
Self
}
/// 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.
pub unsafe fn load(self, start: u32) -> ! {
core::mem::drop(self.boot);
trace!("Loading app at 0x{:x}", start);
#[allow(unused_mut)]
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 = [
"unproven",
] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" }
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true }
embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.4.0", optional = true }
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.
///
@ -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
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
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/),
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
- Replace unnecessary atomics in runqueue

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-executor"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "async/await executor designed for embedded usage"
@ -14,7 +14,7 @@ categories = [
[package.metadata.embassy_docs]
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/"
features = ["nightly", "defmt", "pender-callback"]
features = ["nightly", "defmt"]
flavors = [
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
@ -25,7 +25,7 @@ flavors = [
[package.metadata.docs.rs]
default-target = "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]
@ -37,9 +37,6 @@ arch-xtensa = ["_arch"]
arch-riscv32 = ["_arch"]
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)
executor-thread = []
# 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")]
pub use thread::*;
#[cfg(feature = "executor-thread")]
@ -8,18 +54,9 @@ mod thread {
#[cfg(feature = "nightly")]
pub use embassy_macros::main_cortex_m as main;
use crate::raw::{Pender, PenderInner};
use crate::arch::THREAD_PENDER;
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.
///
/// This is the simplest and most common kind of executor. It runs on
@ -39,7 +76,7 @@ mod thread {
/// Create a new Executor.
pub fn new() -> Self {
Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
inner: raw::Executor::new(THREAD_PENDER as *mut ()),
not_send: PhantomData,
}
}
@ -86,30 +123,7 @@ mod interrupt {
use cortex_m::interrupt::InterruptNumber;
use cortex_m::peripheral::NVIC;
use crate::raw::{self, Pender, PenderInner};
#[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
}
}
use crate::raw;
/// Interrupt mode executor.
///
@ -194,9 +208,7 @@ mod interrupt {
unsafe {
(&mut *self.executor.get())
.as_mut_ptr()
.write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender(
irq.number(),
)))))
.write(raw::Executor::new(irq.number() as *mut ()))
}
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };

View File

@ -11,22 +11,16 @@ mod thread {
#[cfg(feature = "nightly")]
pub use embassy_macros::main_riscv as main;
use crate::raw::{Pender, PenderInner};
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
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
pub struct Executor {
inner: raw::Executor,
@ -37,7 +31,7 @@ mod thread {
/// Create a new Executor.
pub fn new() -> Self {
Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
inner: raw::Executor::new(core::ptr::null_mut()),
not_send: PhantomData,
}
}

View File

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

View File

@ -14,14 +14,12 @@ mod thread {
use wasm_bindgen::prelude::*;
use crate::raw::util::UninitCell;
use crate::raw::{Pender, PenderInner};
use crate::{raw, Spawner};
/// 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 ()>,
#[export_name = "__pender"]
fn __pender(context: *mut ()) {
let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) };
let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() });
}
pub(crate) struct WasmContext {
@ -29,16 +27,6 @@ mod thread {
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 {
pub fn new() -> 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 {
/// Create a new Executor.
pub fn new() -> Self {
let ctx = &*Box::leak(Box::new(WasmContext::new()));
let ctx = Box::leak(Box::new(WasmContext::new()));
Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))),
not_send: PhantomData,
inner: raw::Executor::new(ctx as *mut WasmContext as *mut ()),
ctx,
not_send: PhantomData,
}
}

View File

@ -8,22 +8,16 @@ mod thread {
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
use crate::raw::{Pender, PenderInner};
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
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
pub struct Executor {
inner: raw::Executor,
@ -34,7 +28,7 @@ mod thread {
/// Create a new Executor.
pub fn new() -> Self {
Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
inner: raw::Executor::new(core::ptr::null_mut()),
not_send: PhantomData,
}
}

View File

@ -147,10 +147,7 @@ impl<F: Future + 'static> TaskStorage<F> {
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
let task = AvailableTask::claim(self);
match task {
Some(task) => {
let task = task.initialize(future);
unsafe { SpawnToken::<F>::new(task) }
}
Some(task) => task.initialize(future),
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>,
}
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
.state
.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 })
}
fn initialize(self, future: impl FnOnce() -> F) -> TaskRef {
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
unsafe {
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
self.task.future.write(future());
}
TaskRef::new(self.task)
let task = TaskRef::new(self.task);
SpawnToken::new(task)
}
}
/// Raw storage that can hold up to N tasks of the same type.
///
/// This is essentially a `[TaskStorage<F>; N]`.
pub struct TaskPool<F: Future + 'static, const N: usize> {
pool: [TaskStorage<F>; N],
/// Initialize the [`TaskStorage`] to run the given future.
pub fn initialize(self, future: impl FnOnce() -> F) -> SpawnToken<F> {
self.initialize_impl::<F>(future)
}
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],
}
}
/// Try to spawn a task in the pool.
/// Initialize the [`TaskStorage`] to run the given future.
///
/// See [`TaskStorage::spawn()`] for details.
/// # Safety
///
/// 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
/// 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`
/// `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,
{
pub unsafe fn __initialize_async_fn<FutFn>(self, future: impl FnOnce() -> F) -> SpawnToken<FutFn> {
// 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-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
// by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken<F>`.
let task = self.pool.iter().find_map(AvailableTask::claim);
match task {
Some(task) => {
let task = task.initialize(future);
unsafe { SpawnToken::<FutFn>::new(task) }
self.initialize_impl::<FutFn>(future)
}
}
/// Raw storage that can hold up to N tasks of the same type.
///
/// This is essentially a `[TaskStorage<F>; N]`.
pub struct TaskPool<F: Future + 'static, const N: usize> {
pool: [TaskStorage<F>; N],
}
impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
/// Create a new TaskPool, with all tasks in non-spawned state.
pub const fn new() -> Self {
Self {
pool: [TaskStorage::NEW; N],
}
}
fn spawn_impl<T>(&'static self, future: impl FnOnce() -> F) -> SpawnToken<T> {
match self.pool.iter().find_map(AvailableTask::claim) {
Some(task) => task.initialize_impl::<T>(future),
None => SpawnToken::new_failed(),
}
}
/// 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)]
pub(crate) enum PenderInner {
#[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 () },
}
pub(crate) struct Pender(*mut ());
unsafe impl Send for PenderInner {}
unsafe impl Sync for PenderInner {}
/// 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);
unsafe impl Send for Pender {}
unsafe impl Sync for Pender {}
impl Pender {
/// Create a `Pender` that will call an arbitrary function pointer.
///
/// # Arguments
///
/// - `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),
pub(crate) fn pend(self) {
extern "Rust" {
fn __pender(context: *mut ());
}
unsafe { __pender(self.0) };
}
}
@ -409,7 +386,7 @@ impl SyncExecutor {
#[allow(clippy::never_loop)]
loop {
#[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| {
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
/// that "want to run").
/// - You must supply a [`Pender`]. The executor will call it to notify you it has work
/// to do. You must arrange for `poll()` to be called as soon as possible.
/// - You must supply a pender function, as shown below. The executor will call it to notify you
/// 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.
/// You must deal with this correctly.
///
/// 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 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)]
pub struct Executor {
pub(crate) inner: SyncExecutor,
@ -495,12 +488,12 @@ impl 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`.
pub fn new(pender: Pender) -> Self {
/// See [`Executor`] docs for details on the pender.
pub fn new(context: *mut ()) -> Self {
Self {
inner: SyncExecutor::new(pender),
inner: SyncExecutor::new(Pender(context)),
_not_sync: PhantomData,
}
}
@ -523,16 +516,16 @@ impl Executor {
/// 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.
///
/// 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
/// 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
/// energy.
///
/// # Safety
///
/// You must NOT call `poll` reentrantly on the same executor.
///
/// 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
/// 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
/// somehow schedule for `poll()` to be called later, at a time you know for sure there's
/// no `poll()` already running.
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")]
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 {
raw_task: None,
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-sync = { version = "0.2.0", path = "../embassy-sync" }
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"] }
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
lora-phy = { version = "1" }
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
- [`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.

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-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embedded-hal = { version = "1.0.0-alpha.11" }
embedded-hal-async = { version = "=0.2.0-alpha.2" }
embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
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"] }

View File

@ -5,9 +5,12 @@ use heapless::String;
use crate::ioctl::Shared;
use crate::proto::{self, CtrlMsg};
#[derive(Debug)]
pub struct Error {
pub status: u32,
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
Failed(u32),
Timeout,
Internal,
}
pub struct Control<'a> {
@ -23,58 +26,78 @@ enum WifiMode {
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> {
pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self {
Self { state_ch, shared }
}
pub async fn init(&mut self) {
pub async fn init(&mut self) -> Result<(), Error> {
debug!("wait for init event...");
self.shared.init_wait().await;
debug!("set wifi mode");
self.set_wifi_mode(WifiMode::Sta as _).await;
debug!("set heartbeat");
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);
self.state_ch.set_ethernet_address(mac_addr);
Ok(())
}
pub async fn join(&mut self, ssid: &str, password: &str) {
let req = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::ReqConnectAp as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp {
pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
let req = proto::CtrlMsgReqConnectAp {
ssid: String::from(ssid),
pwd: String::from(password),
bssid: String::new(),
listen_interval: 3,
is_wpa3_supported: false,
})),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
assert_eq!(resp.resp, 0);
ioctl!(self, ReqConnectAp, RespConnectAp, req, resp);
self.state_ch.set_link_state(LinkState::Up);
Ok(())
}
async fn get_mac_addr(&mut self) -> [u8; 6] {
let req = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::ReqGetMacAddress as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress(
proto::CtrlMsgReqGetMacAddress {
pub async fn disconnect(&mut self) -> Result<(), Error> {
let req = proto::CtrlMsgReqGetStatus {};
ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp);
self.state_ch.set_link_state(LinkState::Up);
Ok(())
}
/// 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;
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
assert_eq!(resp.resp, 0);
ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp);
// WHY IS THIS A STRING? WHYYYY
fn nibble_from_hex(b: u8) -> u8 {
@ -88,32 +111,32 @@ impl<'a> Control<'a> {
let mac = resp.mac.as_bytes();
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() {
*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) {
let req = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::ReqSetWifiMode as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
};
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 set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> {
let req = proto::CtrlMsgReqSetMode { mode };
ioctl!(self, ReqSetWifiMode, RespSetWifiMode, req, resp);
Ok(())
}
async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg {
debug!("ioctl req: {:?}", &req);
async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> {
debug!("ioctl req: {:?}", &msg);
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);
@ -135,9 +158,12 @@ impl<'a> Control<'a> {
ioctl.defuse();
let res = noproto::read(&buf[..resp_len]).unwrap();
debug!("ioctl resp: {:?}", &res);
*msg = noproto::read(&buf[..resp_len]).map_err(|_| {
warn!("failed to serialize control request");
Error::Internal
})?;
debug!("ioctl resp: {:?}", msg);
res
Ok(())
}
}

View File

@ -1,17 +1,15 @@
#![no_std]
use control::Control;
use embassy_futures::select::{select3, Either3};
use embassy_futures::select::{select4, Either4};
use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::LinkState;
use embassy_time::{Duration, Instant, Timer};
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiDevice;
use ioctl::Shared;
use proto::CtrlMsg;
use crate::ioctl::PendingIoctl;
use crate::proto::CtrlMsgPayload;
use crate::ioctl::{PendingIoctl, Shared};
use crate::proto::{CtrlMsg, CtrlMsgPayload};
mod proto;
@ -21,6 +19,8 @@ mod fmt;
mod control;
mod ioctl;
pub use control::*;
const MTU: usize = 1514;
macro_rules! impl_bytes {
@ -95,6 +95,7 @@ enum InterfaceType {
}
const MAX_SPI_BUFFER_SIZE: usize = 1600;
const HEARTBEAT_MAX_GAP: Duration = Duration::from_secs(20);
pub struct State {
shared: Shared,
@ -129,12 +130,14 @@ where
let mut runner = Runner {
ch: ch_runner,
state_ch,
shared: &state.shared,
next_seq: 1,
handshake,
ready,
reset,
spi,
heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP,
};
runner.init().await;
@ -143,9 +146,11 @@ where
pub struct Runner<'a, SPI, IN, OUT> {
ch: ch::Runner<'a, MTU>,
state_ch: ch::StateRunner<'a>,
shared: &'a Shared,
next_seq: u16,
heartbeat_deadline: Instant,
spi: SPI,
handshake: IN,
@ -177,9 +182,10 @@ where
let ioctl = self.shared.ioctl_wait_pending();
let tx = self.ch.tx_buf();
let ev = async { self.ready.wait_for_high().await.unwrap() };
let hb = Timer::at(self.heartbeat_deadline);
match select3(ioctl, tx, ev).await {
Either3::First(PendingIoctl { buf, req_len }) => {
match select4(ioctl, tx, ev, hb).await {
Either4::First(PendingIoctl { buf, req_len }) => {
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[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]);
@ -198,7 +204,7 @@ where
header.checksum = checksum(&tx_buf[..26 + req_len]);
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);
let mut header = PayloadHeader {
@ -217,9 +223,12 @@ where
self.ch.tx_done();
}
Either3::Third(()) => {
Either4::Third(()) => {
tx_buf[..PayloadHeader::SIZE].fill(0);
}
Either4::Fourth(()) => {
panic!("heartbeat from esp32 stopped")
}
}
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 {
warn!("failed to parse event");
return;
@ -323,6 +332,11 @@ where
match payload {
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]
name = "embassy-net-w5500"
name = "embassy-net-wiznet"
version = "0.1.0"
description = "embassy-net driver for the W5500 ethernet chip"
keywords = ["embedded", "w5500", "embassy-net", "embedded-hal-async", "ethernet", "async"]
description = "embassy-net driver for WIZnet SPI Ethernet chips"
keywords = ["embedded", "wiznet", "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-alpha.11" }
embedded-hal-async = { version = "=0.2.0-alpha.2" }
embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
defmt = { version = "0.3", optional = true }
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-w5500-v$VERSION/embassy-net-w5500/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/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-wiznet/src/"
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]
#![feature(async_fn_in_trait)]
pub mod chip;
mod device;
mod socket;
mod spi;
use embassy_futures::select::{select, Either};
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::spi::SpiDevice;
use crate::device::W5500;
use crate::chip::Chip;
use crate::device::WiznetDevice;
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>;
/// 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.
pub struct Runner<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> {
mac: W5500<SPI>,
/// You must call `.run()` in a background task for the driver to operate.
pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> {
mac: WiznetDevice<C, SPI>,
ch: ch::Runner<'d, MTU>,
int: INT,
_reset: RST,
}
/// You must call this in a background task for the W5500 to operate.
impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {
/// You must call this in a background task for the driver to operate.
impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> {
pub async fn run(mut self) -> ! {
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
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).
pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
/// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net).
///
/// 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],
state: &'a mut State<N_RX, N_TX>,
spi_dev: SPI,
int: INT,
mut reset: RST,
) -> (Device<'a>, Runner<'a, SPI, INT, RST>) {
// Reset the W5500.
) -> (Device<'a>, Runner<'a, C, SPI, INT, RST>) {
// Reset the chip.
reset.set_low().ok();
// Ensure the reset is registered.
Timer::after(Duration::from_millis(1)).await;
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));
(

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
- [`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-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.
## Examples

View File

@ -82,6 +82,22 @@ impl<'a> TcpReader<'a> {
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
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> {
@ -100,6 +116,22 @@ impl<'a> TcpWriter<'a> {
pub async fn flush(&mut self) -> Result<(), Error> {
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> {
@ -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.
pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) {
(TcpReader { io: self.io }, TcpWriter { io: self.io })
@ -359,6 +423,64 @@ impl<'d> TcpIo<'d> {
.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> {
poll_fn(move |cx| {
self.with_mut(|s, _| {
@ -376,6 +498,14 @@ impl<'d> TcpIo<'d> {
})
.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")]
@ -384,13 +514,20 @@ mod embedded_io_impls {
impl embedded_io_async::Error for ConnectError {
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 {
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 {
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<'_> {

View File

@ -32,7 +32,7 @@ rt = [
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
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 }
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-async = { version = "=0.2.0-alpha.2", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
embedded-io = { version = "0.5.0" }
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.mosi.read().bits());
// Disable all events interrupts
T::Interrupt::disable();
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" }
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-async = { version = "=0.2.0-alpha.2", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true}
paste = "1.0"
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>(
ch: impl Peripheral<P = C> + 'a,
@ -86,7 +87,7 @@ pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
) -> Transfer<'a, C> {
copy_inner(
ch,
&DUMMY as *const u32,
&mut DUMMY as *const u32,
to as *mut u32,
len,
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> {
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!(
"Reading from 0x{:x} to 0x{:x}",
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
}
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)?;
trace!(
@ -136,7 +136,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
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())?;
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
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))? };
Ok(())
}
/// 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;
unsafe {
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> {
pub fn new(_flash: impl Peripheral<P = T> + 'd) -> Self {
pub fn new_blocking(_flash: impl Peripheral<P = T> + 'd) -> Self {
Self {
dma: None,
phantom: PhantomData,
@ -310,47 +310,8 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> {
transfer,
})
}
}
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.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> {
pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
use core::mem::MaybeUninit;
// 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(())
}
}
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 {
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;
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> {
self.write(offset, bytes)
self.blocking_write(offset, bytes)
}
}

View File

@ -749,15 +749,15 @@ mod eh02 {
mod eh1 {
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;
}
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;
}
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;
}
@ -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> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
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> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)

View File

@ -807,26 +807,26 @@ mod eh02 {
mod eh1 {
use super::*;
impl embedded_hal_1::serial::Error for Error {
fn kind(&self) -> embedded_hal_1::serial::ErrorKind {
impl embedded_hal_nb::serial::Error for Error {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match *self {
Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat,
Self::Break => embedded_hal_1::serial::ErrorKind::Other,
Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun,
Self::Parity => embedded_hal_1::serial::ErrorKind::Parity,
Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
Self::Break => embedded_hal_nb::serial::ErrorKind::Other,
Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
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;
}
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;
}
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;
}
@ -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> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
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> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
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;
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((
RxToken {
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<'_>> {
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 {
tx: &self.runner.tx_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
let data_event = match self.rx.try_recv().unwrap() {
let data_event = match self.rx.try_receive().unwrap() {
MacEvent::McpsDataInd(data_event) => data_event,
_ => unreachable!(),
};
@ -111,7 +113,7 @@ impl<'d> embassy_net_driver::TxToken for TxToken<'d> {
F: FnOnce(&mut [u8]) -> R,
{
// 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]);
// 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;
loop {
let (buf, len) = self.tx_channel.recv().await;
let (buf, len) = self.tx_channel.receive().await;
let _wm = self.write_mutex.lock().await;
// 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 }
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-async = { version = "=0.2.0-alpha.2", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true}
embedded-storage = "0.3.0"
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 }
critical-section = "1.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"
bxcan = "0.7.0"
nb = "1.0.0"
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
[build-dependencies]
proc-macro2 = "1.0.36"
quote = "1.0.15"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-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]
default = ["rt"]

View File

@ -310,7 +310,11 @@ fn main() {
for p in METADATA.peripherals {
// generating RccPeripheral impl for H7 ADC3 would result in bad frequency
if !singletons.contains(&p.name.to_string()) || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7")) {
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;
}

View File

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

View File

@ -478,7 +478,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
pub async fn read(&mut self) -> Result<Envelope, BusError> {
poll_fn(|cx| {
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));
} else if let Some(err) = self.curr_error() {
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.
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);
}
@ -506,14 +506,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
/// Waits while receive queue is empty.
pub async fn wait_not_empty(&mut self) {
poll_fn(|cx| {
if T::state().rx_queue.poll_ready_to_receive(cx) {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await
poll_fn(|cx| T::state().rx_queue.poll_ready_to_receive(cx)).await
}
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 {
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> {
@ -463,7 +467,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
}
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
@ -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
/// 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> {
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.
@ -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.
/// - 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> {
use core::future::poll_fn;
use core::sync::atomic::compiler_fence;
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)),
}
})
self.ringbuf
.read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
}
/// The capacity of the ringbuffer.
pub fn cap(&self) -> usize {
pub const fn cap(&self) -> usize {
self.ringbuf.cap()
}
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) {
@ -628,50 +611,29 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
}
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
/// 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> {
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.
pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> {
use core::future::poll_fn;
use core::sync::atomic::compiler_fence;
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)),
}
})
self.ringbuf
.write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
}
/// The capacity of the ringbuffer.
pub fn cap(&self) -> usize {
pub const fn cap(&self) -> usize {
self.ringbuf.cap()
}
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) {

View File

@ -623,6 +623,10 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
fn reset_complete_count(&mut self) -> usize {
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> {
@ -708,7 +712,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
}
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
@ -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
/// 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> {
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.
@ -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.
/// - 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> {
use core::future::poll_fn;
use core::sync::atomic::compiler_fence;
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)),
}
})
self.ringbuf
.read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
}
// The capacity of the ringbuffer
pub fn cap(&self) -> usize {
pub const fn cap(&self) -> usize {
self.ringbuf.cap()
}
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) {
@ -890,50 +873,29 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
}
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
/// 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> {
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.
pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> {
use core::future::poll_fn;
use core::sync::atomic::compiler_fence;
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)),
}
})
self.ringbuf
.write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)
.await
}
// The capacity of the ringbuffer
pub fn cap(&self) -> usize {
pub const fn cap(&self) -> usize {
self.ringbuf.cap()
}
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) {

View File

@ -1,7 +1,9 @@
#![cfg_attr(gpdma, allow(unused))]
use core::future::poll_fn;
use core::ops::Range;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::{Poll, Waker};
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.
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> {
@ -57,7 +62,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
}
/// 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;
dma.reset_complete_count();
}
@ -68,8 +73,43 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
}
/// The current position of the ringbuffer
fn pos(&self, remaining_transfers: usize) -> usize {
self.cap() - remaining_transfers
fn pos(&self, dma: &mut impl DmaCtrl) -> usize {
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
@ -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
/// 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.
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
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
earlier.
*/
let end = self.pos(dma.get_remaining_transfers());
let end = self.pos(dma);
if self.start == end && dma.get_complete_count() == 0 {
// No elements are available in the buffer
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
if it's inside data we could have copied
*/
let (pos, complete_count) =
critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count()));
let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count()));
if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 {
Err(OverrunError)
} else {
@ -141,7 +180,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
then, get the current position of of the dma write and check
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 {
Err(OverrunError)
} else {
@ -169,7 +208,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {
then, get the current position of of the dma write and check
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 {
Err(OverrunError)
} else {
@ -209,7 +248,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
}
/// 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;
dma.reset_complete_count();
}
@ -220,14 +259,39 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
}
/// The current position of the ringbuffer
fn pos(&self, remaining_transfers: usize) -> usize {
self.cap() - remaining_transfers
fn pos(&self, dma: &mut impl DmaCtrl) -> usize {
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
/// 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> {
let start = self.pos(dma.get_remaining_transfers());
pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> {
let start = self.pos(dma);
if start > self.end {
// The occupied portion in the ring buffer DOES wrap
let len = self.copy_from(buf, self.end..start);
@ -235,8 +299,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
compiler_fence(Ordering::SeqCst);
// Confirm that the DMA is not inside data we could have written
let (pos, complete_count) =
critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count()));
let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count()));
if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 {
Err(OverrunError)
} else {
@ -256,7 +319,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
compiler_fence(Ordering::SeqCst);
// 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 {
Err(OverrunError)
} else {
@ -274,7 +337,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> {
compiler_fence(Ordering::SeqCst);
// 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 {
Err(OverrunError)
} else {
@ -323,7 +386,7 @@ mod tests {
requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>,
}
impl DmaCtrl for &mut TestCircularTransfer {
impl DmaCtrl for TestCircularTransfer {
fn get_remaining_transfers(&self) -> usize {
match self.requests.borrow_mut().pop().unwrap() {
TestCircularTransferRequest::PositionRequest(pos) => {
@ -350,6 +413,8 @@ mod tests {
_ => unreachable!(),
}
}
fn set_waker(&mut self, waker: &Waker) {}
}
impl TestCircularTransfer {

View File

@ -11,7 +11,7 @@ pub const fn is_default_layout() -> bool {
}
const fn is_dual_bank() -> bool {
FLASH_REGIONS.len() == 2
FLASH_REGIONS.len() >= 2
}
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| {
w.set_pg(true);
#[cfg(flash_h7)]
w.set_psize(2); // 32 bits at once
});
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);
bank.cr().modify(|w| {
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| {
@ -126,6 +130,10 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {
error!("incerr");
return Err(Error::Seq);
}
if sr.crcrderr() {
error!("crcrderr");
return Err(Error::Seq);
}
if sr.operr() {
return Err(Error::Prog);
}

View File

@ -65,9 +65,11 @@ impl FlashRegion {
#[cfg_attr(flash_f7, path = "f7.rs")]
#[cfg_attr(flash_g0, path = "g0.rs")]
#[cfg_attr(flash_h7, path = "h7.rs")]
#[cfg_attr(flash_h7ab, path = "h7.rs")]
#[cfg_attr(
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"
)]

View File

@ -378,22 +378,6 @@ pub(crate) unsafe fn init(config: Config) {
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
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 {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => {
@ -414,6 +398,22 @@ pub(crate) unsafe fn init(config: Config) {
// Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
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 {
sys: sys_clk,
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
// states and programming delay
#[cfg(flash_h7)]
let (wait_states, progr_delay) = match vos {
// VOS 0 range VCORE 1.26V - 1.40V
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| {
w.set_wrhighfreq(progr_delay);
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 (rcc_pclk4, ppre4_bits, ppre4, _) = ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None);
flash_setup(rcc_aclk, pwr_vos);
// Start switching clocks -------------------
// Ensure CSI is on and stable
@ -595,6 +638,8 @@ pub(crate) unsafe fn init(mut config: Config) {
// core voltage
while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {}
flash_setup(rcc_aclk, pwr_vos);
// APB1 / APB2 Prescaler
RCC.d2cfgr().modify(|w| {
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 {}
}
RCC.apb1enr1().modify(|w| w.set_pwren(true));
match config.rtc_mux {
RtcClockSource::LSE32 => {
// 1. Unlock the backup domain
RCC.apb1enr1().modify(|w| w.set_pwren(true));
PWR.cr1().modify(|w| w.set_dbp(true));
// 2. Setup the LSE

View File

@ -9,7 +9,7 @@ use crate::time::Hertz;
#[cfg_attr(rcc_f0, path = "f0.rs")]
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.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(rcc_f7, path = "f7.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::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::rtc::{Rtc, RtcClockSource as RCS};
use crate::time::Hertz;
/// 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
while !RCC.bdcr().read().lserdy() {}
Rtc::set_clock_source(RCS::LSE);
}
RtcClockSource::LSI32 => {
// Turn on the internal 32 kHz LSI oscillator
@ -236,6 +239,8 @@ pub(crate) unsafe fn init(config: Config) {
// Wait until LSI is running
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))]
pub fn reset(&mut self) {
T::regs().cr().write(|reg| {
reg.set_rngen(false);
reg.set_condrst(true);
reg.set_nistc(pac::rng::vals::Nistc::CUSTOM);
// set RNG config "A" according to reference manual
// 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_clkdiv(pac::rng::vals::Clkdiv::NODIV);
reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIGA_B);
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
while !T::regs().cr().read().condrst() {}

View File

@ -72,6 +72,7 @@ impl core::ops::Sub for RtcInstant {
}
}
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub(crate) enum WakeupPrescaler {
Div2,
@ -120,6 +121,7 @@ impl From<WakeupPrescaler> for u32 {
}
}
#[allow(dead_code)]
impl WakeupPrescaler {
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 {
self.inner.get_max_compare_value()
self.inner.get_max_compare_value() + 1
}
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)
}
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
pub fn set_dead_time(&mut self, value: u16) {
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_polarity(&mut self, channel: Channel, polarity: OutputPolarity);
fn enable_channel(&mut self, channel: Channel, enable: bool);
fn set_compare_value(&mut self, channel: Channel, value: u16);
@ -61,6 +63,8 @@ pub(crate) mod sealed {
}
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_value(&mut self, value: u8);
@ -71,6 +75,8 @@ pub(crate) mod sealed {
pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance {
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 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 GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {}
@ -265,6 +286,13 @@ macro_rules! impl_compare_capable_16bit {
.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) {
use sealed::GeneralPurpose16bitInstance;
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()));
}
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) {
use crate::timer::sealed::GeneralPurpose32bitInstance;
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()));
}
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) {
use crate::timer::sealed::AdvancedControlInstance;
Self::regs_advanced()
@ -409,6 +451,13 @@ foreach_interrupt! {
}
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) {
use crate::timer::sealed::AdvancedControlInstance;
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 {
self.inner.get_max_compare_value()
self.inner.get_max_compare_value() + 1
}
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)
}
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 {
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;
}
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;
}
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;
}
@ -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> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
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> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
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),
};
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))]
let mut over8 = false;
let mut found = None;
let mut found_brr = None;
for &(presc, _presc_val) in &DIVS {
let denom = (config.baudrate * presc as u32) as u64;
let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom;
let brr = calculate_brr(config.baudrate, pclk_freq.0, presc as u32, mul);
trace!(
"USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})",
presc,
div,
div >> 4,
div & 0x0F
brr,
brr >> 4,
brr & 0x0F
);
if div < brr_min {
if brr < brr_min {
#[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;
let div = div as u32;
r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07)));
r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07)));
#[cfg(usart_v4)]
r.presc().write(|w| w.set_prescaler(_presc_val));
found = Some(div);
found_brr = Some(brr);
break;
}
panic!("USART: baudrate too high");
}
if div < brr_max {
let div = div as u32;
r.brr().write_value(regs::Brr(div));
if brr < brr_max {
r.brr().write_value(regs::Brr(brr));
#[cfg(usart_v4)]
r.presc().write(|w| w.set_prescaler(_presc_val));
found = Some(div);
found_brr = Some(brr);
break;
}
}
let div = found.expect("USART: baudrate too low");
let brr = found_brr.expect("USART: baudrate too low");
#[cfg(not(usart_v1))]
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: {}",
oversampling,
config.baudrate,
(pclk_freq.0 * mul as u32) / div
pclk_freq.0 / brr * mul
);
r.cr2().write(|w| {
@ -943,27 +955,27 @@ mod eh02 {
mod eh1 {
use super::*;
impl embedded_hal_1::serial::Error for Error {
fn kind(&self) -> embedded_hal_1::serial::ErrorKind {
impl embedded_hal_nb::serial::Error for Error {
fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {
match *self {
Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat,
Self::Noise => embedded_hal_1::serial::ErrorKind::Noise,
Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun,
Self::Parity => embedded_hal_1::serial::ErrorKind::Parity,
Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other,
Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,
Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,
Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,
Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,
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;
}
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;
}
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;
}
@ -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> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
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> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
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
/// 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
/// 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>> {
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.
@ -106,6 +113,13 @@ impl<'ch, T> DynamicSender<'ch, T> {
pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {
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`].
@ -133,16 +147,30 @@ where
{
/// Receive the next value.
///
/// See [`Channel::recv()`].
pub fn recv(&self) -> RecvFuture<'_, M, T, N> {
self.channel.recv()
/// See [`Channel::receive()`].
pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> {
self.channel.receive()
}
/// Attempt to immediately receive the next value.
///
/// See [`Channel::try_recv()`]
pub fn try_recv(&self) -> Result<T, TryRecvError> {
self.channel.try_recv()
/// See [`Channel::try_receive()`]
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
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> {
/// Receive the next value.
///
/// See [`Channel::recv()`].
pub fn recv(&self) -> DynamicRecvFuture<'_, T> {
DynamicRecvFuture { channel: self.channel }
/// See [`Channel::receive()`].
pub fn receive(&self) -> DynamicReceiveFuture<'_, T> {
DynamicReceiveFuture { channel: self.channel }
}
/// Attempt to immediately receive the next value.
///
/// See [`Channel::try_recv()`]
pub fn try_recv(&self) -> Result<T, TryRecvError> {
self.channel.try_recv_with_context(None)
/// See [`Channel::try_receive()`]
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
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"]
pub struct RecvFuture<'ch, M, T, const N: usize>
pub struct ReceiveFuture<'ch, M, T, const N: usize>
where
M: RawMutex,
{
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
M: RawMutex,
{
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
match self.channel.try_recv_with_context(Some(cx)) {
Ok(v) => Poll::Ready(v),
Err(TryRecvError::Empty) => Poll::Pending,
}
self.channel.poll_receive(cx)
}
}
/// Future returned by [`DynamicReceiver::recv`].
/// Future returned by [`DynamicReceiver::receive`].
#[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>,
}
impl<'ch, T> Future for DynamicRecvFuture<'ch, T> {
impl<'ch, T> Future for DynamicReceiveFuture<'ch, T> {
type Output = 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),
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> {
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)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TryRecvError {
pub enum TryReceiveError {
/// A message could not be received because the channel is empty.
Empty,
}
@ -320,11 +364,11 @@ impl<T, const N: usize> ChannelState<T, N> {
}
}
fn try_recv(&mut self) -> Result<T, TryRecvError> {
self.try_recv_with_context(None)
fn try_receive(&mut self) -> Result<T, TryReceiveError> {
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() {
self.senders_waker.wake();
}
@ -335,14 +379,31 @@ impl<T, const N: usize> ChannelState<T, N> {
if let Some(cx) = cx {
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.queue.is_empty()
if !self.queue.is_empty() {
Poll::Ready(())
} else {
Poll::Pending
}
}
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.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()))
}
fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryRecvError> {
self.lock(|c| c.try_recv_with_context(cx))
fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
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>> {
@ -418,12 +488,12 @@ where
}
/// 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))
}
/// 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))
}
@ -466,16 +536,16 @@ where
///
/// If there are no messages in the channel's buffer, this method will
/// wait until a message is sent.
pub fn recv(&self) -> RecvFuture<'_, M, T, N> {
RecvFuture { channel: self }
pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> {
ReceiveFuture { channel: self }
}
/// Attempt to immediately receive a message.
///
/// This method will either receive a message from the channel immediately or return an error
/// if the channel is empty.
pub fn try_recv(&self) -> Result<T, TryRecvError> {
self.lock(|c| c.try_recv())
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
self.lock(|c| c.try_receive())
}
}
@ -489,8 +559,20 @@ where
Channel::try_send_with_context(self, m, cx)
}
fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryRecvError> {
Channel::try_recv_with_context(self, cx)
fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {
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() {
let mut c = ChannelState::<u32, 3>::new();
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);
}
#[test]
fn receiving_when_empty() {
let mut c = ChannelState::<u32, 3>::new();
match c.try_recv() {
Err(TryRecvError::Empty) => assert!(true),
match c.try_receive() {
Err(TryReceiveError::Empty) => assert!(true),
_ => assert!(false),
}
assert_eq!(capacity(&c), 3);
@ -552,7 +634,7 @@ mod tests {
fn simple_send_and_receive() {
let c = Channel::<NoopRawMutex, u32, 3>::new();
assert!(c.try_send(1).is_ok());
assert_eq!(c.try_recv().unwrap(), 1);
assert_eq!(c.try_receive().unwrap(), 1);
}
#[test]
@ -572,7 +654,7 @@ mod tests {
let r: DynamicReceiver<'_, u32> = c.receiver().into();
assert!(s.try_send(1).is_ok());
assert_eq!(r.try_recv().unwrap(), 1);
assert_eq!(r.try_receive().unwrap(), 1);
}
#[futures_test::test]
@ -587,14 +669,14 @@ mod tests {
assert!(c2.try_send(1).is_ok());
})
.is_ok());
assert_eq!(c.recv().await, 1);
assert_eq!(c.receive().await, 1);
}
#[futures_test::test]
async fn sender_send_completes_if_capacity() {
let c = Channel::<CriticalSectionRawMutex, u32, 1>::new();
c.send(1).await;
assert_eq!(c.recv().await, 1);
assert_eq!(c.receive().await, 1);
}
#[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.
// However, I've used the debugger to observe that the send does indeed wait.
Delay::new(Duration::from_millis(500)).await;
assert_eq!(c.recv().await, 1);
assert_eq!(c.receive().await, 1);
assert!(executor
.spawn(async move {
loop {
c.recv().await;
c.receive().await;
}
})
.is_ok());

View File

@ -152,8 +152,8 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
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-async = { version = "=0.2.0-alpha.2", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
futures-util = { version = "0.3.17", default-features = false }
atomic-polyfill = "1.0.1"

View File

@ -248,6 +248,8 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> {
write_ep: D::EndpointIn,
_control: &'d ControlShared,
max_packet_size: usize,
}
impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
@ -338,6 +340,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
read_ep,
write_ep,
_control: &state.shared,
max_packet_size: max_packet_size as usize,
}
}
@ -349,6 +352,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
Sender {
write_ep: self.write_ep,
seq: 0,
max_packet_size: self.max_packet_size,
},
Receiver {
data_if: self.data_if,
@ -365,6 +369,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
pub struct Sender<'d, D: Driver<'d>> {
write_ep: D::EndpointIn,
seq: u16,
max_packet_size: usize,
}
impl<'d, D: Driver<'d>> Sender<'d, D> {
@ -375,8 +380,8 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
let seq = self.seq;
self.seq = self.seq.wrapping_add(1);
const MAX_PACKET_SIZE: usize = 64; // TODO unhardcode
const OUT_HEADER_LEN: usize = 28;
const ABS_MAX_PACKET_SIZE: usize = 512;
let header = NtbOutHeader {
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`.
let mut buf = [0; MAX_PACKET_SIZE];
let mut buf = [0; ABS_MAX_PACKET_SIZE];
let n = byteify(&mut buf, header);
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.
// No need to send ZLP because it's short for sure.
buf[OUT_HEADER_LEN..][..data.len()].copy_from_slice(data);
self.write_ep.write(&buf[..OUT_HEADER_LEN + data.len()]).await?;
} 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);
self.write_ep.write(&buf).await?;
buf[OUT_HEADER_LEN..self.max_packet_size].copy_from_slice(d1);
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?;
}
// Send ZLP if needed.
if d2.len() % MAX_PACKET_SIZE == 0 {
if d2.len() % self.max_packet_size == 0 {
self.write_ep.write(&[]).await?;
}
}

View File

@ -526,7 +526,7 @@ impl<'a> PropertyData<'a> {
PropertyData::Binary(val) => val.len(),
PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(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 embassy_boot_rp::*;
use embassy_executor::Spawner;
use embassy_rp::flash::{self, Flash};
use embassy_rp::flash::Flash;
use embassy_rp::gpio::{Level, Output};
use embassy_rp::watchdog::Watchdog;
use embassy_sync::blocking_mutex::Mutex;
@ -34,7 +34,7 @@ async fn main(_s: Spawner) {
let mut watchdog = Watchdog::new(p.WATCHDOG);
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 config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);

View File

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

View File

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

View File

@ -12,12 +12,14 @@ nightly = [
"embassy-nrf/nightly",
"embassy-net/nightly",
"embassy-net-esp-hosted",
"embassy-net-enc28j60",
"embassy-nrf/unstable-traits",
"embassy-time/nightly",
"embassy-time/unstable-traits",
"static_cell/nightly",
"embassy-usb",
"embedded-io-async",
"embedded-hal-bus/async",
"embassy-net",
"embassy-lora",
"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 = { 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-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"], optional = true }
defmt = "0.3"
defmt-rtt = "0.4"
@ -54,9 +57,14 @@ rand = { version = "0.8.4", default-features = false }
embedded-storage = "0.3.0"
usbd-hid = "0.6.0"
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 }
microfft = "0.5.0"
[profile.release]
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()));
loop {
match CHANNEL.recv().await {
match CHANNEL.receive().await {
LedState::On => led.set_high(),
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);
loop {
match receiver.recv().await {
match receiver.receive().await {
LedState::On => led.set_high(),
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
// task.
loop {
let buf = CHANNEL.recv().await;
let buf = CHANNEL.receive().await;
info!("writing...");
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::{bind_interrupts, peripherals};
use embassy_time::Delay;
use embedded_hal_async::spi::ExclusiveDevice;
use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_io_async::Write;
use static_cell::make_static;
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)));
control.init().await;
control.join(WIFI_NETWORK, WIFI_PASSWORD).await;
unwrap!(control.init().await);
unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await);
let config = embassy_net::Config::dhcpv4(Default::default());
// 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-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-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-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
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"
usbd-hid = "0.6.1"
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" }
embedded-hal-async = "0.2.0-alpha.2"
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
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-storage = { version = "0.3" }
static_cell = { version = "1.1", features = ["nightly"]}
@ -54,3 +55,6 @@ rand = { version = "0.8.5", default-features = false }
[profile.release]
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_futures::yield_now;
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::gpio::{Input, Level, Output, Pull};
use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0};
use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
use embassy_time::{Delay, Duration};
use embedded_hal_async::spi::ExclusiveDevice;
use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_io_async::Write;
use rand::RngCore;
use static_cell::make_static;
@ -26,6 +27,7 @@ use {defmt_rtt as _, panic_probe as _};
async fn ethernet_task(
runner: Runner<
'static,
W5500,
ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>,
Input<'static, PIN_21>,
Output<'static, PIN_20>,
@ -54,7 +56,7 @@ async fn main(spawner: Spawner) {
let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
let state = make_static!(State::<8, 8>::new());
let (device, runner) = embassy_net_w5500::new(
let (device, runner) = embassy_net_wiznet::new(
mac_addr,
state,
ExclusiveDevice::new(spi, cs, Delay),

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