diff --git a/ci.sh b/ci.sh index cd1c0786..9f79c87b 100755 --- a/ci.sh +++ b/ci.sh @@ -36,6 +36,8 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ + --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ + --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,nightly \ @@ -79,6 +81,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ + --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ --- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \ @@ -104,6 +107,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \ --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \ --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/nrf --bin b \ + --- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/boot/rp --bin b \ --- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32f3 --bin b \ --- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32f7 --bin b \ --- build --release --manifest-path examples/boot/application/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32h7 --bin b \ @@ -112,6 +116,7 @@ cargo batch \ --- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32l4 --bin b \ --- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/boot/stm32wl --bin b \ --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ + --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/bluepill-stm32f103c8 \ diff --git a/docs/modules/ROOT/pages/basic_application.adoc b/docs/modules/ROOT/pages/basic_application.adoc index 4dc4a635..3f4f16e2 100644 --- a/docs/modules/ROOT/pages/basic_application.adoc +++ b/docs/modules/ROOT/pages/basic_application.adoc @@ -21,7 +21,7 @@ Then, what follows are some declarations on how to deal with panics and faults. [source,rust] ---- -include::example$basic/src/main.rs[lines="11..12"] +include::example$basic/src/main.rs[lines="10"] ---- === Task declaration @@ -30,7 +30,7 @@ After a bit of import declaration, the tasks run by the application should be de [source,rust] ---- -include::example$basic/src/main.rs[lines="13..22"] +include::example$basic/src/main.rs[lines="12..20"] ---- An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking. @@ -45,23 +45,10 @@ The `Spawner` is the way the main application spawns other tasks. The `Periphera [source,rust] ---- -include::example$basic/src/main.rs[lines="23..-1"] +include::example$basic/src/main.rs[lines="22..-1"] ---- -`#[embassy_executor::main]` takes an optional `config` parameter specifying a function that returns an instance of HAL's `Config` struct. For example: - -```rust -fn embassy_config() -> embassy_nrf::config::Config { - embassy_nrf::config::Config::default() -} - -#[embassy_executor::main(config = "embassy_config()")] -async fn main(_spawner: Spawner, p: embassy_nrf::Peripherals) { - // ... -} -``` - -What happens when the `blinker` task have been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy::main]` macro. The macro does the following: +What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy::main]` macro. The macro does the following: . Creates an Embassy Executor . Initializes the microcontroller HAL to get the `Peripherals` @@ -76,7 +63,7 @@ The project definition needs to contain the embassy dependencies: [source,toml] ---- -include::example$basic/Cargo.toml[lines="8..9"] +include::example$basic/Cargo.toml[lines="9..11"] ---- Depending on your microcontroller, you may need to replace `embassy-nrf` with something else (`embassy-stm32` for STM32. Remember to update feature flags as well). diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc index 7dbfeb3e..b50de5ab 100644 --- a/docs/modules/ROOT/pages/bootloader.adoc +++ b/docs/modules/ROOT/pages/bootloader.adoc @@ -15,6 +15,7 @@ The bootloader supports * nRF52 with and without softdevice * STM32 L4, WB, WL, L1, L0, F3, F7 and H7 +* Raspberry Pi: RP2040 In general, the bootloader works on any platform that implements the `embedded-storage` traits for its internal flash, but may require custom initialization code to work. diff --git a/docs/modules/ROOT/pages/layer_by_layer.adoc b/docs/modules/ROOT/pages/layer_by_layer.adoc index a96dd9fe..a78a64a9 100644 --- a/docs/modules/ROOT/pages/layer_by_layer.adoc +++ b/docs/modules/ROOT/pages/layer_by_layer.adoc @@ -8,7 +8,7 @@ The application we'll write is a simple 'push button, blink led' application, wh == PAC version -The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provide distinct types +The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code. Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use. @@ -20,13 +20,13 @@ The blinky app using PAC is shown below: include::example$layer-by-layer/blinky-pac/src/main.rs[] ---- -As you can see, there are a lot of code needed to enable the peripheral clocks, configuring the input pins and the output pins of the application. +As you can see, a lot of code is needed to enable the peripheral clocks and to configure the input pins and the output pins of the application. Another downside of this application is that it is busy-looping while polling the button state. This prevents the microcontroller from utilizing any sleep mode to save power. == HAL version -To simplify our application, we can use the HAL instead. The HAL exposes higher level APIs that handle details such +To simplify our application, we can use the HAL instead. The HAL exposes higher level APIs that handle details such as: * Automatically enabling the peripheral clock when you're using the peripheral * Deriving and applying register configuration from higher level types @@ -39,7 +39,7 @@ The HAL example is shown below: include::example$layer-by-layer/blinky-hal/src/main.rs[] ---- -As you can see, the application becomes a lot simpler, even without using any async code. The `Input` and `Output` hides all the details accessing the GPIO registers, and allow you to use a much simpler API to query the state of the button and toggle the LED output accordingly. +As you can see, the application becomes a lot simpler, even without using any async code. The `Input` and `Output` types hide all the details of accessing the GPIO registers and allow you to use a much simpler API for querying the state of the button and toggling the LED output. The same downside from the PAC example still applies though: the application is busy looping and consuming more power than necessary. diff --git a/docs/modules/ROOT/pages/stm32.adoc b/docs/modules/ROOT/pages/stm32.adoc index 8ed9ab04..7bfc0592 100644 --- a/docs/modules/ROOT/pages/stm32.adoc +++ b/docs/modules/ROOT/pages/stm32.adoc @@ -4,9 +4,9 @@ The link:https://github.com/embassy-rs/embassy/tree/master/embassy-stm32[Embassy == The infinite variant problem -STM32 microcontrollers comes in many families and flavors, and supporting all of them is a big undertaking. Embassy has taken advantage of the fact +STM32 microcontrollers come in many families, and flavors and supporting all of them is a big undertaking. Embassy has taken advantage of the fact that the STM32 peripheral versions are shared across chip families. Instead of re-implementing the SPI -peripheral for every STM32 chip family, embassy have a single SPI implementation that depends on +peripheral for every STM32 chip family, embassy has a single SPI implementation that depends on code-generated register types that are identical for STM32 families with the same version of a given peripheral. === The metapac diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 54c67a37..ae4efbd2 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -1,14 +1,24 @@ [package] edition = "2021" name = "embassy-boot" -version = "0.1.0" -description = "Bootloader using Embassy" +version = "0.1.1" +description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks." license = "MIT OR Apache-2.0" +repository = "https://github.com/embassy-rs/embassy" +categories = [ + "embedded", + "no-std", + "asynchronous", +] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/boot/src/" target = "thumbv7em-none-eabi" +features = ["defmt"] + +[package.metadata.docs.rs] +features = ["defmt"] [lib] diff --git a/embassy-boot/README.md b/embassy-boot/boot/README.md similarity index 100% rename from embassy-boot/README.md rename to embassy-boot/boot/README.md diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 429323ec..76b14bc8 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -1,7 +1,7 @@ #![feature(type_alias_impl_trait)] #![no_std] #![warn(missing_docs)] -#![doc = include_str!("../../README.md")] +#![doc = include_str!("../README.md")] mod fmt; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; diff --git a/embassy-boot/nrf/README.md b/embassy-boot/nrf/README.md new file mode 100644 index 00000000..02f35c0a --- /dev/null +++ b/embassy-boot/nrf/README.md @@ -0,0 +1,26 @@ +# embassy-boot-nrf + +An [Embassy](https://embassy.dev) project. + +An adaptation of `embassy-boot` for nRF. + +## Features + +* Load applications with our without the softdevice. +* Configure bootloader partitions based on linker script. +* Using watchdog timer to detect application failure. + + +## Minimum supported Rust version (MSRV) + +`embassy-boot-nrf` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 82475d1e..205bbd6d 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] -#![doc = include_str!("../../README.md")] +#![doc = include_str!("../README.md")] mod fmt; pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig}; diff --git a/embassy-boot/rp/Cargo.toml b/embassy-boot/rp/Cargo.toml new file mode 100644 index 00000000..93099b23 --- /dev/null +++ b/embassy-boot/rp/Cargo.toml @@ -0,0 +1,71 @@ +[package] +edition = "2021" +name = "embassy-boot-rp" +version = "0.1.0" +description = "Bootloader lib for RP2040 chips" +license = "MIT OR Apache-2.0" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-rp-v$VERSION/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/rp/src/" +target = "thumbv6m-none-eabi" + +[lib] + +[dependencies] +defmt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } +log = { version = "0.4", optional = true } + +embassy-sync = { path = "../../embassy-sync" } +embassy-rp = { path = "../../embassy-rp", default-features = false, features = ["nightly"] } +embassy-boot = { path = "../boot", default-features = false } +cortex-m = { version = "0.7.6" } +cortex-m-rt = { version = "0.7" } +embedded-storage = "0.3.0" +embedded-storage-async = "0.3.0" +cfg-if = "1.0.0" + +[features] +defmt = [ + "dep:defmt", + "embassy-boot/defmt", + "embassy-rp/defmt", +] +log = [ + "dep:log", + "embassy-boot/log", + "embassy-rp/log", +] +debug = ["defmt-rtt"] + +[profile.dev] +debug = 2 +debug-assertions = true +incremental = false +opt-level = 'z' +overflow-checks = true + +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false +incremental = false +lto = 'fat' +opt-level = 'z' +overflow-checks = false + +# do not optimize proc-macro crates = faster builds from scratch +[profile.dev.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false + +[profile.release.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false diff --git a/embassy-boot/rp/README.md b/embassy-boot/rp/README.md new file mode 100644 index 00000000..c0c2d85f --- /dev/null +++ b/embassy-boot/rp/README.md @@ -0,0 +1,26 @@ +# embassy-boot-rp + +An [Embassy](https://embassy.dev) project. + +An adaptation of `embassy-boot` for RP2040. + +NOTE: The applications using this bootloader should not link with the `link-rp.x` linker script. + +## Features + +* Configure bootloader partitions based on linker script. +* Load applications from active partition. + +## Minimum supported Rust version (MSRV) + +`embassy-boot-rp` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-boot/rp/build.rs b/embassy-boot/rp/build.rs new file mode 100644 index 00000000..2cbc7ef5 --- /dev/null +++ b/embassy-boot/rp/build.rs @@ -0,0 +1,8 @@ +use std::env; + +fn main() { + let target = env::var("TARGET").unwrap(); + if target.starts_with("thumbv6m-") { + println!("cargo:rustc-cfg=armv6m"); + } +} diff --git a/embassy-boot/rp/src/fmt.rs b/embassy-boot/rp/src/fmt.rs new file mode 100644 index 00000000..06697081 --- /dev/null +++ b/embassy-boot/rp/src/fmt.rs @@ -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; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs new file mode 100644 index 00000000..85fc8182 --- /dev/null +++ b/embassy-boot/rp/src/lib.rs @@ -0,0 +1,90 @@ +#![no_std] +#![feature(type_alias_impl_trait)] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] +mod fmt; + +pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; +use embassy_rp::flash::{ERASE_SIZE, WRITE_SIZE}; + +/// A bootloader for RP2040 devices. +pub struct BootLoader { + boot: embassy_boot::BootLoader, + magic: AlignedBuffer, + page: AlignedBuffer, +} + +impl BootLoader { + /// Create a new bootloader instance using the supplied partitions for active, dfu and state. + pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { + Self { + boot: embassy_boot::BootLoader::new(active, dfu, state), + magic: AlignedBuffer([0; WRITE_SIZE]), + page: AlignedBuffer([0; ERASE_SIZE]), + } + } + + /// Inspect the bootloader state and perform actions required before booting, such as swapping + /// firmware. + pub fn prepare(&mut self, flash: &mut F) -> usize { + match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) { + Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(), + Err(_) => panic!("boot prepare error!"), + } + } + + /// Boots the application. + /// + /// # Safety + /// + /// This modifies the stack pointer and reset vector and will run code placed in the active partition. + pub unsafe fn load(&mut self, start: usize) -> ! { + trace!("Loading app at 0x{:x}", start); + #[allow(unused_mut)] + let mut p = cortex_m::Peripherals::steal(); + #[cfg(not(armv6m))] + p.SCB.invalidate_icache(); + p.SCB.vtor.write(start as u32); + + cortex_m::asm::bootload(start as *const u32) + } +} + +impl Default for BootLoader { + /// Create a new bootloader instance using parameters from linker script + fn default() -> Self { + extern "C" { + static __bootloader_state_start: u32; + static __bootloader_state_end: u32; + static __bootloader_active_start: u32; + static __bootloader_active_end: u32; + static __bootloader_dfu_start: u32; + static __bootloader_dfu_end: u32; + } + + let active = unsafe { + Partition::new( + &__bootloader_active_start as *const u32 as usize, + &__bootloader_active_end as *const u32 as usize, + ) + }; + let dfu = unsafe { + Partition::new( + &__bootloader_dfu_start as *const u32 as usize, + &__bootloader_dfu_end as *const u32 as usize, + ) + }; + let state = unsafe { + Partition::new( + &__bootloader_state_start as *const u32 as usize, + &__bootloader_state_end as *const u32 as usize, + ) + }; + + trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to); + trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); + trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); + + Self::new(active, dfu, state) + } +} diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml index 9d12c6cf..2fc169b3 100644 --- a/embassy-boot/stm32/Cargo.toml +++ b/embassy-boot/stm32/Cargo.toml @@ -15,7 +15,7 @@ target = "thumbv7em-none-eabi" [dependencies] defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } log = { version = "0.4", optional = true } embassy-sync = { path = "../../embassy-sync" } diff --git a/embassy-boot/stm32/README.md b/embassy-boot/stm32/README.md index a82b730b..cb134b53 100644 --- a/embassy-boot/stm32/README.md +++ b/embassy-boot/stm32/README.md @@ -1,11 +1,24 @@ -# Bootloader for STM32 +# embassy-boot-stm32 -The bootloader uses `embassy-boot` to interact with the flash. +An [Embassy](https://embassy.dev) project. -# Usage +An adaptation of `embassy-boot` for STM32. -Flash the bootloader +## Features -``` -cargo flash --features embassy-stm32/stm32wl55jc-cm4 --release --chip STM32WLE5JCIx -``` +* Configure bootloader partitions based on linker script. +* Load applications from active partition. + +## Minimum supported Rust version (MSRV) + +`embassy-boot-stm32` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index d549eccc..82f712c4 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] -#![doc = include_str!("../../README.md")] +#![doc = include_str!("../README.md")] mod fmt; pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 85ee856a..fa74be8c 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -20,7 +20,7 @@ nightly = ["embedded-hal-async", "embedded-storage-async"] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true } +embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } nb = "1.0.0" diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs index 1c43f015..3680984f 100644 --- a/embassy-embedded-hal/src/adapter.rs +++ b/embassy-embedded-hal/src/adapter.rs @@ -38,32 +38,31 @@ where E: embedded_hal_1::i2c::Error + 'static, T: blocking::i2c::WriteRead + blocking::i2c::Read + blocking::i2c::Write, { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { self.wrapped.read(address, buffer) } + async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(address, buffer) } - fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { - async move { self.wrapped.write(address, bytes) } + async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), Self::Error> { + self.wrapped.write(address, bytes) } - fn write_read<'a>(&'a mut self, address: u8, bytes: &'a [u8], buffer: &'a mut [u8]) -> Self::WriteReadFuture<'a> { - async move { self.wrapped.write_read(address, bytes, buffer) } + async fn write_read<'a>( + &'a mut self, + address: u8, + bytes: &'a [u8], + buffer: &'a mut [u8], + ) -> Result<(), Self::Error> { + self.wrapped.write_read(address, bytes, buffer) } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: u8, operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + ) -> Result<(), Self::Error> { let _ = address; let _ = operations; - async move { todo!() } + todo!() } } @@ -84,23 +83,17 @@ where E: embedded_hal_1::spi::Error + 'static, T: blocking::spi::Transfer + blocking::spi::Write, { - type TransferFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Self::TransferFuture<'a> { - async move { - // Ensure we write the expected bytes - for i in 0..core::cmp::min(read.len(), write.len()) { - read[i] = write[i].clone(); - } - self.wrapped.transfer(read)?; - Ok(()) + async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + // Ensure we write the expected bytes + for i in 0..core::cmp::min(read.len(), write.len()) { + read[i] = write[i].clone(); } + self.wrapped.transfer(read)?; + Ok(()) } - type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> { - async move { todo!() } + async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> { + todo!() } } @@ -109,10 +102,8 @@ where E: embedded_hal_1::spi::Error + 'static, T: blocking::spi::Transfer + blocking::spi::Write, { - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } @@ -121,13 +112,9 @@ where E: embedded_hal_1::spi::Error + 'static, T: blocking::spi::Transfer + blocking::spi::Write, { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - self.wrapped.write(data)?; - Ok(()) - } + async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(data)?; + Ok(()) } } @@ -136,13 +123,9 @@ where E: embedded_hal_1::spi::Error + 'static, T: blocking::spi::Transfer + blocking::spi::Write, { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - self.wrapped.transfer(data)?; - Ok(()) - } + async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.transfer(data)?; + Ok(()) } } @@ -192,7 +175,7 @@ where } type FlushFuture<'a> = impl Future> + 'a where T: 'a; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + fn flush(&mut self) -> Result<(), Self::Error> { async move { self.wrapped.bflush() } } } diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index a12a3a3a..8da04222 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -1,5 +1,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr( + feature = "nightly", + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) +)] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] #![warn(missing_docs)] //! Utilities to use `embedded-hal` traits with Embassy. diff --git a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs index 0bc6afd9..c5e1fd41 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs @@ -22,7 +22,6 @@ //! let i2c_dev2 = I2cDevice::new(i2c_bus); //! let mpu = Mpu6050::new(i2c_dev2); //! ``` -use core::future::Future; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; @@ -55,53 +54,39 @@ where M: RawMutex + 'static, BUS: i2c::I2c + 'static, { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; - Ok(()) - } + async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; + Ok(()) } - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; - Ok(()) - } + async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; + Ok(()) } - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write_read<'a>( + async fn write_read<'a>( &'a mut self, address: u8, wr_buffer: &'a [u8], rd_buffer: &'a mut [u8], - ) -> Self::WriteReadFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.write_read(address, wr_buffer, rd_buffer) - .await - .map_err(I2cDeviceError::I2c)?; - Ok(()) - } + ) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.write_read(address, wr_buffer, rd_buffer) + .await + .map_err(I2cDeviceError::I2c)?; + Ok(()) } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: u8, operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + ) -> Result<(), I2cDeviceError> { let _ = address; let _ = operations; - async move { todo!() } + todo!() } } @@ -136,55 +121,41 @@ where M: RawMutex + 'static, BUS: i2c::I2c + SetConfig + 'static, { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.set_config(&self.config); - bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; - Ok(()) - } + async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; + Ok(()) } - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.set_config(&self.config); - bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; - Ok(()) - } + async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; + Ok(()) } - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write_read<'a>( + async fn write_read<'a>( &'a mut self, address: u8, wr_buffer: &'a [u8], rd_buffer: &'a mut [u8], - ) -> Self::WriteReadFuture<'a> { - async move { - let mut bus = self.bus.lock().await; - bus.set_config(&self.config); - bus.write_read(address, wr_buffer, rd_buffer) - .await - .map_err(I2cDeviceError::I2c)?; - Ok(()) - } + ) -> Result<(), I2cDeviceError> { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + bus.write_read(address, wr_buffer, rd_buffer) + .await + .map_err(I2cDeviceError::I2c)?; + Ok(()) } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: u8, operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + ) -> Result<(), I2cDeviceError> { let _ = address; let _ = operations; - async move { todo!() } + todo!() } } diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index a3814d6d..d2571665 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -65,33 +65,25 @@ where { type Bus = BUS; - type TransactionFuture<'a, R, F, Fut> = impl Future> + 'a + async fn transaction(&mut self, f: F) -> Result where - Self: 'a, R: 'a, F: FnOnce(*mut Self::Bus) -> Fut + 'a, - Fut: Future::Error>> + 'a; - - fn transaction<'a, R, F, Fut>(&'a mut self, f: F) -> Self::TransactionFuture<'a, R, F, Fut> - where - R: 'a, - F: FnOnce(*mut Self::Bus) -> Fut + 'a, - Fut: Future::Error>> + 'a, + F: FnOnce(*mut Self::Bus) -> Fut, + Fut: Future::Error>>, { - async move { - let mut bus = self.bus.lock().await; - self.cs.set_low().map_err(SpiDeviceError::Cs)?; + let mut bus = self.bus.lock().await; + self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut *bus).await; + let f_res = f(&mut *bus).await; - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush().await; - let cs_res = self.cs.set_high(); + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush().await; + let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; + let f_res = f_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) - } + Ok(f_res) } } @@ -130,33 +122,25 @@ where { type Bus = BUS; - type TransactionFuture<'a, R, F, Fut> = impl Future> + 'a + async fn transaction(&mut self, f: F) -> Result where - Self: 'a, R: 'a, F: FnOnce(*mut Self::Bus) -> Fut + 'a, - Fut: Future::Error>> + 'a; - - fn transaction<'a, R, F, Fut>(&'a mut self, f: F) -> Self::TransactionFuture<'a, R, F, Fut> - where - R: 'a, - F: FnOnce(*mut Self::Bus) -> Fut + 'a, - Fut: Future::Error>> + 'a, + F: FnOnce(*mut Self::Bus) -> Fut, + Fut: Future::Error>>, { - async move { - let mut bus = self.bus.lock().await; - bus.set_config(&self.config); - self.cs.set_low().map_err(SpiDeviceError::Cs)?; + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut *bus).await; + let f_res = f(&mut *bus).await; - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush().await; - let cs_res = self.cs.set_high(); + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush().await; + let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; + let f_res = f_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) - } + Ok(f_res) } } diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index d0f51646..c2868eb9 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,9 +1,15 @@ [package] name = "embassy-executor" -version = "0.1.0" +version = "0.1.1" edition = "2021" license = "MIT OR Apache-2.0" - +description = "async/await executor designed for embedded usage" +repository = "https://github.com/embassy-rs/embassy" +categories = [ + "embedded", + "no-std", + "asynchronous", +] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" @@ -21,10 +27,13 @@ flavors = [ { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] }, ] +[package.metadata.docs.rs] +features = ["std", "nightly", "defmt"] + [features] default = [] -std = ["embassy-macros/std", "critical-section/std"] -wasm = ["dep:wasm-bindgen", "dep:js-sys", "embassy-macros/wasm"] +std = ["critical-section/std"] +wasm = ["dep:wasm-bindgen", "dep:js-sys"] # Enable nightly-only features nightly = [] diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index e4cbd04b..4c7e2f4c 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -8,18 +8,22 @@ pub(crate) mod fmt; #[cfg(feature = "nightly")] -pub use embassy_macros::{main, task}; +pub use embassy_macros::task; cfg_if::cfg_if! { if #[cfg(cortex_m)] { #[path="arch/cortex_m.rs"] mod arch; pub use arch::*; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_cortex_m as main; } else if #[cfg(target_arch="riscv32")] { #[path="arch/riscv32.rs"] mod arch; pub use arch::*; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_riscv as main; } else if #[cfg(all(target_arch="xtensa", feature = "nightly"))] { #[path="arch/xtensa.rs"] @@ -30,11 +34,15 @@ cfg_if::cfg_if! { #[path="arch/wasm.rs"] mod arch; pub use arch::*; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_wasm as main; } else if #[cfg(feature="std")] { #[path="arch/std.rs"] mod arch; pub use arch::*; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_std as main; } } diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs new file mode 100644 index 00000000..c5e44430 --- /dev/null +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -0,0 +1,331 @@ +use core::slice; +use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; + +/// Atomic reusable ringbuffer +/// +/// This ringbuffer implementation is designed to be stored in a `static`, +/// therefore all methods take `&self` and not `&mut self`. +/// +/// It is "reusable": when created it has no backing buffer, you can give it +/// one with `init` and take it back with `deinit`, and init it again in the +/// future if needed. This is very non-idiomatic, but helps a lot when storing +/// it in a `static`. +/// +/// One concurrent writer and one concurrent reader are supported, even at +/// different execution priorities (like main and irq). +pub struct RingBuffer { + buf: AtomicPtr, + len: AtomicUsize, + start: AtomicUsize, + end: AtomicUsize, +} + +pub struct Reader<'a>(&'a RingBuffer); +pub struct Writer<'a>(&'a RingBuffer); + +impl RingBuffer { + /// Create a new empty ringbuffer. + pub const fn new() -> Self { + Self { + buf: AtomicPtr::new(core::ptr::null_mut()), + len: AtomicUsize::new(0), + start: AtomicUsize::new(0), + end: AtomicUsize::new(0), + } + } + + /// Initialize the ring buffer with a buffer. + /// + /// # Safety + /// - The buffer (`buf .. buf+len`) must be valid memory until `deinit` is called. + /// - Must not be called concurrently with any other methods. + pub unsafe fn init(&self, buf: *mut u8, len: usize) { + // Ordering: it's OK to use `Relaxed` because this is not called + // concurrently with other methods. + self.buf.store(buf, Ordering::Relaxed); + self.len.store(len, Ordering::Relaxed); + self.start.store(0, Ordering::Relaxed); + self.end.store(0, Ordering::Relaxed); + } + + /// Deinitialize the ringbuffer. + /// + /// After calling this, the ringbuffer becomes empty, as if it was + /// just created with `new()`. + /// + /// # Safety + /// - Must not be called concurrently with any other methods. + pub unsafe fn deinit(&self) { + // Ordering: it's OK to use `Relaxed` because this is not called + // concurrently with other methods. + self.len.store(0, Ordering::Relaxed); + self.start.store(0, Ordering::Relaxed); + self.end.store(0, Ordering::Relaxed); + } + + /// Create a reader. + /// + /// # Safety + /// + /// Only one reader can exist at a time. + pub unsafe fn reader(&self) -> Reader<'_> { + Reader(self) + } + + /// Create a writer. + /// + /// # Safety + /// + /// Only one writer can exist at a time. + pub unsafe fn writer(&self) -> Writer<'_> { + Writer(self) + } + + pub fn is_full(&self) -> bool { + let start = self.start.load(Ordering::Relaxed); + let end = self.end.load(Ordering::Relaxed); + + self.wrap(end + 1) == start + } + + pub fn is_empty(&self) -> bool { + let start = self.start.load(Ordering::Relaxed); + let end = self.end.load(Ordering::Relaxed); + + start == end + } + + fn wrap(&self, n: usize) -> usize { + let len = self.len.load(Ordering::Relaxed); + + assert!(n <= len); + if n == len { + 0 + } else { + n + } + } +} + +impl<'a> Writer<'a> { + /// Push data into the buffer in-place. + /// + /// The closure `f` is called with a free part of the buffer, it must write + /// some data to it and return the amount of bytes written. + pub fn push(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> usize { + let (p, n) = self.push_buf(); + let buf = unsafe { slice::from_raw_parts_mut(p, n) }; + let n = f(buf); + self.push_done(n); + n + } + + /// Push one data byte. + /// + /// Returns true if pushed succesfully. + pub fn push_one(&mut self, val: u8) -> bool { + let n = self.push(|f| match f { + [] => 0, + [x, ..] => { + *x = val; + 1 + } + }); + n != 0 + } + + /// Get a buffer where data can be pushed to. + /// + /// Write data to the start of the buffer, then call `push_done` with + /// however many bytes you've pushed. + /// + /// The buffer is suitable to DMA to. + /// + /// If the ringbuf is full, size=0 will be returned. + /// + /// The buffer stays valid as long as no other `Writer` method is called + /// and `init`/`deinit` aren't called on the ringbuf. + pub fn push_buf(&mut self) -> (*mut u8, usize) { + // Ordering: popping writes `start` last, so we read `start` first. + // Read it with Acquire ordering, so that the next accesses can't be reordered up past it. + let start = self.0.start.load(Ordering::Acquire); + let buf = self.0.buf.load(Ordering::Relaxed); + let len = self.0.len.load(Ordering::Relaxed); + let end = self.0.end.load(Ordering::Relaxed); + + let n = if start <= end { + len - end - (start == 0) as usize + } else { + start - end - 1 + }; + + trace!(" ringbuf: push_buf {:?}..{:?}", end, end + n); + (unsafe { buf.add(end) }, n) + } + + pub fn push_done(&mut self, n: usize) { + trace!(" ringbuf: push {:?}", n); + let end = self.0.end.load(Ordering::Relaxed); + + // Ordering: write `end` last, with Release ordering. + // The ordering ensures no preceding memory accesses (such as writing + // the actual data in the buffer) can be reordered down past it, which + // will guarantee the reader sees them after reading from `end`. + self.0.end.store(self.0.wrap(end + n), Ordering::Release); + } +} + +impl<'a> Reader<'a> { + /// Pop data from the buffer in-place. + /// + /// The closure `f` is called with the next data, it must process + /// some data from it and return the amount of bytes processed. + pub fn pop(&mut self, f: impl FnOnce(&[u8]) -> usize) -> usize { + let (p, n) = self.pop_buf(); + let buf = unsafe { slice::from_raw_parts(p, n) }; + let n = f(buf); + self.pop_done(n); + n + } + + /// Pop one data byte. + /// + /// Returns true if popped succesfully. + pub fn pop_one(&mut self) -> Option { + let mut res = None; + self.pop(|f| match f { + &[] => 0, + &[x, ..] => { + res = Some(x); + 1 + } + }); + res + } + + /// Get a buffer where data can be popped from. + /// + /// Read data from the start of the buffer, then call `pop_done` with + /// however many bytes you've processed. + /// + /// The buffer is suitable to DMA from. + /// + /// If the ringbuf is empty, size=0 will be returned. + /// + /// The buffer stays valid as long as no other `Reader` method is called + /// and `init`/`deinit` aren't called on the ringbuf. + pub fn pop_buf(&mut self) -> (*mut u8, usize) { + // Ordering: pushing writes `end` last, so we read `end` first. + // Read it with Acquire ordering, so that the next accesses can't be reordered up past it. + // This is needed to guarantee we "see" the data written by the writer. + let end = self.0.end.load(Ordering::Acquire); + let buf = self.0.buf.load(Ordering::Relaxed); + let len = self.0.len.load(Ordering::Relaxed); + let start = self.0.start.load(Ordering::Relaxed); + + let n = if end < start { len - start } else { end - start }; + + trace!(" ringbuf: pop_buf {:?}..{:?}", start, start + n); + (unsafe { buf.add(start) }, n) + } + + pub fn pop_done(&mut self, n: usize) { + trace!(" ringbuf: pop {:?}", n); + + let start = self.0.start.load(Ordering::Relaxed); + + // Ordering: write `start` last, with Release ordering. + // The ordering ensures no preceding memory accesses (such as reading + // the actual data) can be reordered down past it. This is necessary + // because writing to `start` is effectively freeing the read part of the + // buffer, which "gives permission" to the writer to write to it again. + // Therefore, all buffer accesses must be completed before this. + self.0.start.store(self.0.wrap(start + n), Ordering::Release); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn push_pop() { + let mut b = [0; 4]; + let rb = RingBuffer::new(); + unsafe { + rb.init(b.as_mut_ptr(), 4); + + assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_full(), false); + + rb.writer().push(|buf| { + // If capacity is 4, we can fill it up to 3. + assert_eq!(3, buf.len()); + buf[0] = 1; + buf[1] = 2; + buf[2] = 3; + 3 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), true); + + rb.writer().push(|buf| { + // If it's full, we can push 0 bytes. + assert_eq!(0, buf.len()); + 0 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), true); + + rb.reader().pop(|buf| { + assert_eq!(3, buf.len()); + assert_eq!(1, buf[0]); + 1 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), false); + + rb.reader().pop(|buf| { + assert_eq!(2, buf.len()); + 0 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), false); + + rb.reader().pop(|buf| { + assert_eq!(2, buf.len()); + assert_eq!(2, buf[0]); + assert_eq!(3, buf[1]); + 2 + }); + + assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_full(), false); + + rb.reader().pop(|buf| { + assert_eq!(0, buf.len()); + 0 + }); + + rb.writer().push(|buf| { + assert_eq!(1, buf.len()); + buf[0] = 10; + 1 + }); + + rb.writer().push(|buf| { + assert_eq!(2, buf.len()); + buf[0] = 11; + buf[1] = 12; + 2 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), true); + } + } +} diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index 5d2649d0..b2a35cd3 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs @@ -4,6 +4,7 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +pub mod atomic_ring_buffer; pub mod drop; mod macros; mod peripheral; diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index dc200417..cbe78e59 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -32,7 +32,7 @@ embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3" } +embedded-hal-async = { version = "=0.2.0-alpha.0" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } embedded-hal = { version = "0.2", features = ["unproven"] } diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index ed8cb405..b14d422a 100644 --- a/embassy-lora/src/sx126x/mod.rs +++ b/embassy-lora/src/sx126x/mod.rs @@ -87,7 +87,7 @@ where config.rf.spreading_factor.into(), config.rf.bandwidth.into(), config.rf.coding_rate.into(), - 4, + 8, false, true, false, @@ -119,14 +119,14 @@ where config.spreading_factor.into(), config.bandwidth.into(), config.coding_rate.into(), - 4, + 8, 4, false, 0u8, true, false, 0, - false, + true, true, ) .await?; diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 91d5ec8a..5c612c99 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -3,6 +3,13 @@ name = "embassy-macros" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" +description = "macros for creating the entry point and tasks for embassy-executor" +repository = "https://github.com/embassy-rs/embassy" +categories = [ + "embedded", + "no-std", + "asynchronous", +] [dependencies] syn = { version = "1.0.76", features = ["full", "extra-traits"] } @@ -14,8 +21,5 @@ proc-macro2 = "1.0.29" proc-macro = true [features] -std = [] -wasm = [] - # Enabling this cause interrupt::take! to require embassy-executor rtos-trace-interrupt = [] diff --git a/embassy-macros/README.md b/embassy-macros/README.md new file mode 100644 index 00000000..d1d6f4cc --- /dev/null +++ b/embassy-macros/README.md @@ -0,0 +1,21 @@ +# embassy-macros + +An [Embassy](https://embassy.dev) project. + +Macros for creating the main entry point and tasks that can be spawned by `embassy-executor`. + +NOTE: The macros are re-exported by the `embassy-executor` crate which should be used instead of adding a direct dependency on the `embassy-macros` crate. + +## Minimum supported Rust version (MSRV) + +The `task` and `main` macros require the type alias impl trait (TAIT) nightly feature in order to compile. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index ec8498f9..d2c696c7 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -1,3 +1,4 @@ +#![doc = include_str!("../README.md")] extern crate proc_macro; use proc_macro::TokenStream; @@ -6,6 +7,36 @@ mod macros; mod util; use macros::*; +/// Declares an async task that can be run by `embassy-executor`. The optional `pool_size` parameter can be used to specify how +/// many concurrent tasks can be spawned (default is 1) for the function. +/// +/// +/// The following restrictions apply: +/// +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * The optional `pool_size` attribute must be 1 or greater. +/// +/// +/// ## Examples +/// +/// Declaring a task taking no arguments: +/// +/// ``` rust +/// #[embassy_executor::task] +/// async fn mytask() { +/// // Function body +/// } +/// ``` +/// +/// Declaring a task with a given pool size: +/// +/// ``` rust +/// #[embassy_executor::task(pool_size = 4)] +/// async fn mytask() { +/// // Function body +/// } +/// ``` #[proc_macro_attribute] pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(args as syn::AttributeArgs); @@ -14,11 +45,104 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { task::run(args, f).unwrap_or_else(|x| x).into() } +/// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// ## Examples +/// Spawning a task: +/// +/// ``` rust +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` #[proc_macro_attribute] -pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { +pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(args as syn::AttributeArgs); let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(args, f).unwrap_or_else(|x| x).into() + main::run(args, f, main::cortex_m()).unwrap_or_else(|x| x).into() +} + +/// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// ## Examples +/// Spawning a task: +/// +/// ``` rust +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` +#[proc_macro_attribute] +pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream { + let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let f = syn::parse_macro_input!(item as syn::ItemFn); + main::run(args, f, main::riscv()).unwrap_or_else(|x| x).into() +} + +/// Creates a new `executor` instance and declares an application entry point for STD spawning the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// ## Examples +/// Spawning a task: +/// +/// ``` rust +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` +#[proc_macro_attribute] +pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream { + let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let f = syn::parse_macro_input!(item as syn::ItemFn); + main::run(args, f, main::std()).unwrap_or_else(|x| x).into() +} + +/// Creates a new `executor` instance and declares an application entry point for WASM spawning the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// ## Examples +/// Spawning a task: +/// +/// ``` rust +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` +#[proc_macro_attribute] +pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream { + let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let f = syn::parse_macro_input!(item as syn::ItemFn); + main::run(args, f, main::wasm()).unwrap_or_else(|x| x).into() } #[proc_macro_attribute] diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs index afe9bd3e..18f7c36c 100644 --- a/embassy-macros/src/macros/main.rs +++ b/embassy-macros/src/macros/main.rs @@ -7,7 +7,62 @@ use crate::util::ctxt::Ctxt; #[derive(Debug, FromMeta)] struct Args {} -pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result { +pub fn riscv() -> TokenStream { + quote! { + #[riscv_rt::entry] + fn main() -> ! { + let mut executor = ::embassy_executor::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; + executor.run(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }) + } + } +} + +pub fn cortex_m() -> TokenStream { + quote! { + #[cortex_m_rt::entry] + fn main() -> ! { + let mut executor = ::embassy_executor::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; + executor.run(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }) + } + } +} + +pub fn wasm() -> TokenStream { + quote! { + #[wasm_bindgen::prelude::wasm_bindgen(start)] + pub fn main() -> Result<(), wasm_bindgen::JsValue> { + static EXECUTOR: ::embassy_executor::_export::StaticCell<::embassy_executor::Executor> = ::embassy_executor::_export::StaticCell::new(); + let executor = EXECUTOR.init(::embassy_executor::Executor::new()); + + executor.start(|spawner| { + spawner.spawn(__embassy_main(spawner)).unwrap(); + }); + + Ok(()) + } + } +} + +pub fn std() -> TokenStream { + quote! { + fn main() -> ! { + let mut executor = ::embassy_executor::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; + + executor.run(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }) + } + } +} + +pub fn run(args: syn::AttributeArgs, f: syn::ItemFn, main: TokenStream) -> Result { #[allow(unused_variables)] let args = Args::from_list(&args).map_err(|e| e.write_errors())?; @@ -30,46 +85,6 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result Result<(), wasm_bindgen::JsValue> { - static EXECUTOR: ::embassy_executor::_export::StaticCell<::embassy_executor::Executor> = ::embassy_executor::_export::StaticCell::new(); - let executor = EXECUTOR.init(::embassy_executor::Executor::new()); - - executor.start(|spawner| { - spawner.spawn(__embassy_main(spawner)).unwrap(); - }); - - Ok(()) - } - }; - - #[cfg(all(feature = "std", not(feature = "wasm")))] - let main = quote! { - fn main() -> ! { - let mut executor = ::embassy_executor::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - - executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }) - } - }; - - #[cfg(all(not(feature = "std"), not(feature = "wasm")))] - let main = quote! { - #[cortex_m_rt::entry] - fn main() -> ! { - let mut executor = ::embassy_executor::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - - executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }) - } - }; - let result = quote! { #[::embassy_executor::task()] async fn __embassy_main(#fargs) { diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 76217075..ac338843 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -42,7 +42,7 @@ log = { version = "0.4.14", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embedded-io = { version = "0.3.1", optional = true } +embedded-io = { version = "0.4.0", optional = true } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } heapless = { version = "0.7.5", default-features = false } @@ -52,12 +52,12 @@ stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } atomic-pool = "1.0" atomic-polyfill = "1.0.1" -embedded-nal-async = { version = "0.2.0", optional = true } +embedded-nal-async = { version = "0.3.0", optional = true } [dependencies.smoltcp] version = "0.8.0" git = "https://github.com/smoltcp-rs/smoltcp" -rev = "ed0cf16750a42f30e31fcaf5347915592924b1e3" +rev = "b7a7c4b1c56e8d4c2524c1e3a056c745a13cc09f" default-features = false features = [ "proto-ipv4", diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index c183bd58..4bdfd772 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -12,8 +12,6 @@ pub enum LinkState { Up, } -// 'static required due to the "fake GAT" in smoltcp::phy::Device. -// https://github.com/smoltcp-rs/smoltcp/pull/572 pub trait Device { fn is_transmit_ready(&mut self) -> bool; fn transmit(&mut self, pkt: PacketBuf); @@ -25,7 +23,7 @@ pub trait Device { fn ethernet_address(&self) -> [u8; 6]; } -impl Device for &'static mut T { +impl Device for &mut T { fn is_transmit_ready(&mut self) -> bool { T::is_transmit_ready(self) } @@ -63,11 +61,11 @@ impl DeviceAdapter { } } -impl<'a, D: Device + 'static> SmolDevice<'a> for DeviceAdapter { - type RxToken = RxToken; - type TxToken = TxToken<'a, D>; +impl SmolDevice for DeviceAdapter { + type RxToken<'a> = RxToken where Self: 'a; + type TxToken<'a> = TxToken<'a, D> where Self: 'a; - fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { + fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let tx_pkt = PacketBox::new(Packet::new())?; let rx_pkt = self.device.receive()?; let rx_token = RxToken { pkt: rx_pkt }; @@ -80,7 +78,7 @@ impl<'a, D: Device + 'static> SmolDevice<'a> for DeviceAdapter { } /// Construct a transmit token. - fn transmit(&'a mut self) -> Option { + fn transmit(&mut self) -> Option> { if !self.device.is_transmit_ready() { return None; } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 4d30550d..edb96984 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,5 +1,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr( + feature = "nightly", + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) +)] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 3a761075..5c4fb044 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -1,4 +1,4 @@ -use core::cell::UnsafeCell; +use core::cell::RefCell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; @@ -62,8 +62,8 @@ pub enum ConfigStrategy { } pub struct Stack { - pub(crate) socket: UnsafeCell, - inner: UnsafeCell>, + pub(crate) socket: RefCell, + inner: RefCell>, } struct Inner { @@ -81,8 +81,6 @@ pub(crate) struct SocketStack { next_local_port: u16, } -unsafe impl Send for Stack {} - impl Stack { pub fn new( device: D, @@ -143,40 +141,38 @@ impl Stack { } Self { - socket: UnsafeCell::new(socket), - inner: UnsafeCell::new(inner), + socket: RefCell::new(socket), + inner: RefCell::new(inner), } } - /// SAFETY: must not call reentrantly. - unsafe fn with(&self, f: impl FnOnce(&SocketStack, &Inner) -> R) -> R { - f(&*self.socket.get(), &*self.inner.get()) + fn with(&self, f: impl FnOnce(&SocketStack, &Inner) -> R) -> R { + f(&*self.socket.borrow(), &*self.inner.borrow()) } - /// SAFETY: must not call reentrantly. - unsafe fn with_mut(&self, f: impl FnOnce(&mut SocketStack, &mut Inner) -> R) -> R { - f(&mut *self.socket.get(), &mut *self.inner.get()) + fn with_mut(&self, f: impl FnOnce(&mut SocketStack, &mut Inner) -> R) -> R { + f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) } pub fn ethernet_address(&self) -> [u8; 6] { - unsafe { self.with(|_s, i| i.device.device.ethernet_address()) } + self.with(|_s, i| i.device.device.ethernet_address()) } pub fn is_link_up(&self) -> bool { - unsafe { self.with(|_s, i| i.link_up) } + self.with(|_s, i| i.link_up) } pub fn is_config_up(&self) -> bool { - unsafe { self.with(|_s, i| i.config.is_some()) } + self.with(|_s, i| i.config.is_some()) } pub fn config(&self) -> Option { - unsafe { self.with(|_s, i| i.config.clone()) } + self.with(|_s, i| i.config.clone()) } pub async fn run(&self) -> ! { poll_fn(|cx| { - unsafe { self.with_mut(|s, i| i.poll(cx, s)) } + self.with_mut(|s, i| i.poll(cx, s)); Poll::<()>::Pending }) .await; @@ -270,21 +266,12 @@ impl Inner { None => {} Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), Some(dhcpv4::Event::Configured(config)) => { - let mut dns_servers = Vec::new(); - for s in &config.dns_servers { - if let Some(addr) = s { - dns_servers.push(addr.clone()).unwrap(); - } - } - - self.apply_config( - s, - Config { - address: config.address, - gateway: config.router, - dns_servers, - }, - ) + let config = Config { + address: config.address, + gateway: config.router, + dns_servers: config.dns_servers, + }; + self.apply_config(s, config) } } } else if old_link_up { diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index f3bd2361..73cf2d4e 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -1,4 +1,4 @@ -use core::cell::UnsafeCell; +use core::cell::RefCell; use core::future::poll_fn; use core::mem; use core::task::Poll; @@ -68,8 +68,7 @@ impl<'a> TcpWriter<'a> { impl<'a> TcpSocket<'a> { pub fn new(stack: &'a Stack, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { - // safety: not accessed reentrantly. - let s = unsafe { &mut *stack.socket.get() }; + let s = &mut *stack.socket.borrow_mut(); let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; let handle = s.sockets.add(tcp::Socket::new( @@ -93,17 +92,18 @@ impl<'a> TcpSocket<'a> { where T: Into, { - // safety: not accessed reentrantly. - let local_port = unsafe { &mut *self.io.stack.get() }.get_local_port(); + let local_port = self.io.stack.borrow_mut().get_local_port(); - // safety: not accessed reentrantly. - match unsafe { self.io.with_mut(|s, i| s.connect(i, remote_endpoint, local_port)) } { + match { + self.io + .with_mut(|s, i| s.connect(i.context(), remote_endpoint, local_port)) + } { Ok(()) => {} Err(tcp::ConnectError::InvalidState) => return Err(ConnectError::InvalidState), Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute), } - poll_fn(|cx| unsafe { + poll_fn(|cx| { self.io.with_mut(|s, _| match s.state() { tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(ConnectError::ConnectionReset)), tcp::State::Listen => unreachable!(), @@ -121,14 +121,13 @@ impl<'a> TcpSocket<'a> { where T: Into, { - // safety: not accessed reentrantly. - match unsafe { self.io.with_mut(|s, _| s.listen(local_endpoint)) } { + match self.io.with_mut(|s, _| s.listen(local_endpoint)) { Ok(()) => {} Err(tcp::ListenError::InvalidState) => return Err(AcceptError::InvalidState), Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort), } - poll_fn(|cx| unsafe { + poll_fn(|cx| { self.io.with_mut(|s, _| match s.state() { tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => { s.register_send_waker(cx.waker()); @@ -149,51 +148,49 @@ impl<'a> TcpSocket<'a> { } pub fn set_timeout(&mut self, duration: Option) { - unsafe { self.io.with_mut(|s, _| s.set_timeout(duration)) } + self.io.with_mut(|s, _| s.set_timeout(duration)) } pub fn set_keep_alive(&mut self, interval: Option) { - unsafe { self.io.with_mut(|s, _| s.set_keep_alive(interval)) } + self.io.with_mut(|s, _| s.set_keep_alive(interval)) } pub fn set_hop_limit(&mut self, hop_limit: Option) { - unsafe { self.io.with_mut(|s, _| s.set_hop_limit(hop_limit)) } + self.io.with_mut(|s, _| s.set_hop_limit(hop_limit)) } pub fn local_endpoint(&self) -> Option { - unsafe { self.io.with(|s, _| s.local_endpoint()) } + self.io.with(|s, _| s.local_endpoint()) } pub fn remote_endpoint(&self) -> Option { - unsafe { self.io.with(|s, _| s.remote_endpoint()) } + self.io.with(|s, _| s.remote_endpoint()) } pub fn state(&self) -> tcp::State { - unsafe { self.io.with(|s, _| s.state()) } + self.io.with(|s, _| s.state()) } pub fn close(&mut self) { - unsafe { self.io.with_mut(|s, _| s.close()) } + self.io.with_mut(|s, _| s.close()) } pub fn abort(&mut self) { - unsafe { self.io.with_mut(|s, _| s.abort()) } + self.io.with_mut(|s, _| s.abort()) } pub fn may_send(&self) -> bool { - unsafe { self.io.with(|s, _| s.may_send()) } + self.io.with(|s, _| s.may_send()) } pub fn may_recv(&self) -> bool { - unsafe { self.io.with(|s, _| s.may_recv()) } + self.io.with(|s, _| s.may_recv()) } } impl<'a> Drop for TcpSocket<'a> { fn drop(&mut self) { - // safety: not accessed reentrantly. - let s = unsafe { &mut *self.io.stack.get() }; - s.sockets.remove(self.io.handle); + self.io.stack.borrow_mut().sockets.remove(self.io.handle); } } @@ -201,21 +198,19 @@ impl<'a> Drop for TcpSocket<'a> { #[derive(Copy, Clone)] struct TcpIo<'a> { - stack: &'a UnsafeCell, + stack: &'a RefCell, handle: SocketHandle, } impl<'d> TcpIo<'d> { - /// SAFETY: must not call reentrantly. - unsafe fn with(&self, f: impl FnOnce(&tcp::Socket, &Interface) -> R) -> R { - let s = &*self.stack.get(); + fn with(&self, f: impl FnOnce(&tcp::Socket, &Interface) -> R) -> R { + let s = &*self.stack.borrow(); let socket = s.sockets.get::(self.handle); f(socket, &s.iface) } - /// SAFETY: must not call reentrantly. - unsafe fn with_mut(&mut self, f: impl FnOnce(&mut tcp::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.get(); + fn with_mut(&mut self, f: impl FnOnce(&mut tcp::Socket, &mut Interface) -> R) -> R { + let s = &mut *self.stack.borrow_mut(); let socket = s.sockets.get_mut::(self.handle); let res = f(socket, &mut s.iface); s.waker.wake(); @@ -223,7 +218,7 @@ impl<'d> TcpIo<'d> { } async fn read(&mut self, buf: &mut [u8]) -> Result { - poll_fn(move |cx| unsafe { + poll_fn(move |cx| { // CAUTION: smoltcp semantics around EOF are different to what you'd expect // from posix-like IO, so we have to tweak things here. self.with_mut(|s, _| match s.recv_slice(buf) { @@ -244,7 +239,7 @@ impl<'d> TcpIo<'d> { } async fn write(&mut self, buf: &[u8]) -> Result { - poll_fn(move |cx| unsafe { + poll_fn(move |cx| { self.with_mut(|s, _| match s.send_slice(buf) { // Not ready to send (no space in the tx buffer) Ok(0) => { @@ -271,8 +266,6 @@ impl<'d> TcpIo<'d> { #[cfg(feature = "nightly")] mod embedded_io_impls { - use core::future::Future; - use super::*; impl embedded_io::Error for ConnectError { @@ -292,30 +285,18 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.io.read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.io.read(buf).await } } impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.io.write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.io.write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.io.flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.io.flush().await } } @@ -324,12 +305,8 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Read for TcpReader<'d> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.io.read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.io.read(buf).await } } @@ -338,27 +315,19 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.io.write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.io.write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.io.flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.io.flush().await } } } #[cfg(all(feature = "unstable-traits", feature = "nightly"))] pub mod client { - use core::future::Future; + use core::cell::UnsafeCell; use core::mem::MaybeUninit; use core::ptr::NonNull; @@ -385,28 +354,29 @@ pub mod client { { type Error = Error; type Connection<'m> = TcpConnection<'m, N, TX_SZ, RX_SZ> where Self: 'm; - type ConnectFuture<'m> = impl Future, Self::Error>> + 'm - where - Self: 'm; - fn connect<'m>(&'m self, remote: embedded_nal_async::SocketAddr) -> Self::ConnectFuture<'m> { - async move { - let addr: crate::IpAddress = match remote.ip() { - IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())), - #[cfg(feature = "proto-ipv6")] - IpAddr::V6(addr) => crate::IpAddress::Ipv6(crate::Ipv6Address::from_bytes(&addr.octets())), - #[cfg(not(feature = "proto-ipv6"))] - IpAddr::V6(_) => panic!("ipv6 support not enabled"), - }; - let remote_endpoint = (addr, remote.port()); - let mut socket = TcpConnection::new(&self.stack, self.state)?; - socket - .socket - .connect(remote_endpoint) - .await - .map_err(|_| Error::ConnectionReset)?; - Ok(socket) - } + async fn connect<'a>( + &'a self, + remote: embedded_nal_async::SocketAddr, + ) -> Result, Self::Error> + where + Self: 'a, + { + let addr: crate::IpAddress = match remote.ip() { + IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())), + #[cfg(feature = "proto-ipv6")] + IpAddr::V6(addr) => crate::IpAddress::Ipv6(crate::Ipv6Address::from_bytes(&addr.octets())), + #[cfg(not(feature = "proto-ipv6"))] + IpAddr::V6(_) => panic!("ipv6 support not enabled"), + }; + let remote_endpoint = (addr, remote.port()); + let mut socket = TcpConnection::new(&self.stack, self.state)?; + socket + .socket + .connect(remote_endpoint) + .await + .map_err(|_| Error::ConnectionReset)?; + Ok(socket) } } @@ -445,32 +415,20 @@ pub mod client { impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read for TcpConnection<'d, N, TX_SZ, RX_SZ> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.socket.read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.socket.read(buf).await } } impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write for TcpConnection<'d, N, TX_SZ, RX_SZ> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.socket.write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.socket.write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.socket.flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.socket.flush().await } } diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index f2e33493..4ddad77d 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,4 +1,4 @@ -use core::cell::UnsafeCell; +use core::cell::RefCell; use core::future::poll_fn; use core::mem; use core::task::Poll; @@ -27,7 +27,7 @@ pub enum Error { } pub struct UdpSocket<'a> { - stack: &'a UnsafeCell, + stack: &'a RefCell, handle: SocketHandle, } @@ -39,8 +39,7 @@ impl<'a> UdpSocket<'a> { tx_meta: &'a mut [PacketMetadata], tx_buffer: &'a mut [u8], ) -> Self { - // safety: not accessed reentrantly. - let s = unsafe { &mut *stack.socket.get() }; + let s = &mut *stack.socket.borrow_mut(); let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) }; let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; @@ -63,30 +62,26 @@ impl<'a> UdpSocket<'a> { { let mut endpoint = endpoint.into(); - // safety: not accessed reentrantly. if endpoint.port == 0 { // If user didn't specify port allocate a dynamic port. - endpoint.port = unsafe { &mut *self.stack.get() }.get_local_port(); + endpoint.port = self.stack.borrow_mut().get_local_port(); } - // safety: not accessed reentrantly. - match unsafe { self.with_mut(|s, _| s.bind(endpoint)) } { + match self.with_mut(|s, _| s.bind(endpoint)) { Ok(()) => Ok(()), Err(udp::BindError::InvalidState) => Err(BindError::InvalidState), Err(udp::BindError::Unaddressable) => Err(BindError::NoRoute), } } - /// SAFETY: must not call reentrantly. - unsafe fn with(&self, f: impl FnOnce(&udp::Socket, &Interface) -> R) -> R { - let s = &*self.stack.get(); + fn with(&self, f: impl FnOnce(&udp::Socket, &Interface) -> R) -> R { + let s = &*self.stack.borrow(); let socket = s.sockets.get::(self.handle); f(socket, &s.iface) } - /// SAFETY: must not call reentrantly. - unsafe fn with_mut(&self, f: impl FnOnce(&mut udp::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.get(); + fn with_mut(&self, f: impl FnOnce(&mut udp::Socket, &mut Interface) -> R) -> R { + let s = &mut *self.stack.borrow_mut(); let socket = s.sockets.get_mut::(self.handle); let res = f(socket, &mut s.iface); s.waker.wake(); @@ -94,7 +89,7 @@ impl<'a> UdpSocket<'a> { } pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { - poll_fn(move |cx| unsafe { + poll_fn(move |cx| { self.with_mut(|s, _| match s.recv_slice(buf) { Ok(x) => Poll::Ready(Ok(x)), // No data ready @@ -113,7 +108,7 @@ impl<'a> UdpSocket<'a> { T: Into, { let remote_endpoint = remote_endpoint.into(); - poll_fn(move |cx| unsafe { + poll_fn(move |cx| { self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) { // Entire datagram has been sent Ok(()) => Poll::Ready(Ok(())), @@ -128,30 +123,28 @@ impl<'a> UdpSocket<'a> { } pub fn endpoint(&self) -> IpListenEndpoint { - unsafe { self.with(|s, _| s.endpoint()) } + self.with(|s, _| s.endpoint()) } pub fn is_open(&self) -> bool { - unsafe { self.with(|s, _| s.is_open()) } + self.with(|s, _| s.is_open()) } pub fn close(&mut self) { - unsafe { self.with_mut(|s, _| s.close()) } + self.with_mut(|s, _| s.close()) } pub fn may_send(&self) -> bool { - unsafe { self.with(|s, _| s.can_send()) } + self.with(|s, _| s.can_send()) } pub fn may_recv(&self) -> bool { - unsafe { self.with(|s, _| s.can_recv()) } + self.with(|s, _| s.can_recv()) } } impl Drop for UdpSocket<'_> { fn drop(&mut self) { - // safety: not accessed reentrantly. - let s = unsafe { &mut *self.stack.get() }; - s.sockets.remove(self.handle); + self.stack.borrow_mut().sockets.remove(self.handle); } } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 67b6bec4..6b06d5d0 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -75,8 +75,8 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} -embedded-io = { version = "0.3.1", features = ["async"], optional = true } +embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} +embedded-io = { version = "0.4.0", features = ["async"], optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 9c8fe65f..ea25236f 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -15,7 +15,7 @@ use core::cell::RefCell; use core::cmp::min; -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -341,32 +341,20 @@ impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUar } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.inner_read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).await } } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.inner.inner_read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.inner_read(buf).await } } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - self.inner_fill_buf() + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner_fill_buf().await } fn consume(&mut self, amt: usize) { @@ -375,12 +363,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for Bu } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - self.inner.inner_fill_buf() + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.inner_fill_buf().await } fn consume(&mut self, amt: usize) { @@ -389,38 +373,22 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRea } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.inner_write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner_write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.inner_flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner_flush().await } } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.inner.inner_write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner.inner_write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.inner.inner_flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.inner_flush().await } } diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index dec31a84..bf4019c1 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -131,8 +131,12 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0); +impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0); + impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); +impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index e57a4a38..6c28a3be 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -137,8 +137,12 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0); +impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0); + impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); +impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); + impl_pwm!(PWM0, PWM0, PWM0); impl_timer!(TIMER0, TIMER0, TIMER0); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 918404cf..e7214cf5 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -138,8 +138,13 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_spim!(TWISPI0, SPIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); impl_spim!(SPI1, SPIM1, SPIM1_SPIS1_SPI1); +impl_spis!(TWISPI0, SPIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); +impl_spis!(SPI1, SPIS1, SPIM1_SPIS1_SPI1); + impl_twim!(TWISPI0, TWIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); +impl_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); + impl_pwm!(PWM0, PWM0, PWM0); impl_timer!(TIMER0, TIMER0, TIMER0); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index dba033b0..21d1d16c 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -136,9 +136,15 @@ impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 81e66c19..152dad4e 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -138,6 +138,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // I2S + I2S, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -146,9 +149,16 @@ impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); +impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2); + impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); @@ -234,6 +244,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); +impl_i2s!(I2S, I2S, I2S); + pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; @@ -274,6 +286,6 @@ pub mod irqs { declare!(PWM2); declare!(SPIM2_SPIS2_SPI2); declare!(RTC2); - declare!(I2S); declare!(FPU); + declare!(I2S); } diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 92499e3c..a99ca634 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -161,6 +161,9 @@ embassy_hal_common::peripherals! { // PDM PDM, + + // I2S + I2S, } #[cfg(feature = "nightly")] @@ -174,9 +177,16 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); impl_spim!(SPI3, SPIM3, SPIM3); +impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2); + impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); @@ -280,6 +290,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); +impl_i2s!(I2S, I2S, I2S); + pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; @@ -320,10 +332,10 @@ pub mod irqs { declare!(PWM2); declare!(SPIM2_SPIS2_SPI2); declare!(RTC2); - declare!(I2S); declare!(FPU); declare!(USBD); declare!(UARTE1); declare!(PWM3); declare!(SPIM3); + declare!(I2S); } diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4beadfba..4f7463be 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -164,6 +164,9 @@ embassy_hal_common::peripherals! { // PDM PDM, + + // I2S + I2S, } #[cfg(feature = "nightly")] @@ -177,9 +180,16 @@ impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); impl_spim!(SPI3, SPIM3, SPIM3); +impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2); + impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); +impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); @@ -285,6 +295,8 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); +impl_i2s!(I2S, I2S, I2S); + pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; @@ -325,7 +337,6 @@ pub mod irqs { declare!(PWM2); declare!(SPIM2_SPIS2_SPI2); declare!(RTC2); - declare!(I2S); declare!(FPU); declare!(USBD); declare!(UARTE1); @@ -333,4 +344,5 @@ pub mod irqs { declare!(CRYPTOCELL); declare!(PWM3); declare!(SPIM3); + declare!(I2S); } diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index e20edcdf..4575f09f 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -366,11 +366,21 @@ impl_spim!(UARTETWISPI1, SPIM1, SERIAL1); impl_spim!(UARTETWISPI2, SPIM2, SERIAL2); impl_spim!(UARTETWISPI3, SPIM3, SERIAL3); +impl_spis!(UARTETWISPI0, SPIS0, SERIAL0); +impl_spis!(UARTETWISPI1, SPIS1, SERIAL1); +impl_spis!(UARTETWISPI2, SPIS2, SERIAL2); +impl_spis!(UARTETWISPI3, SPIS3, SERIAL3); + impl_twim!(UARTETWISPI0, TWIM0, SERIAL0); impl_twim!(UARTETWISPI1, TWIM1, SERIAL1); impl_twim!(UARTETWISPI2, TWIM2, SERIAL2); impl_twim!(UARTETWISPI3, TWIM3, SERIAL3); +impl_twis!(UARTETWISPI0, TWIS0, SERIAL0); +impl_twis!(UARTETWISPI1, TWIS1, SERIAL1); +impl_twis!(UARTETWISPI2, TWIS2, SERIAL2); +impl_twis!(UARTETWISPI3, TWIS3, SERIAL3); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 8c292e52..54827238 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -243,7 +243,9 @@ embassy_hal_common::peripherals! { impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0); impl_spim!(UARTETWISPI0, SPIM0, SERIAL0); +impl_spis!(UARTETWISPI0, SPIS0, SERIAL0); impl_twim!(UARTETWISPI0, TWIM0, SERIAL0); +impl_twis!(UARTETWISPI0, TWIS0, SERIAL0); impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 5c00b65a..472ee677 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -280,11 +280,21 @@ impl_spim!(UARTETWISPI1, SPIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); impl_spim!(UARTETWISPI2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); impl_spim!(UARTETWISPI3, SPIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); +impl_spis!(UARTETWISPI0, SPIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_spis!(UARTETWISPI1, SPIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_spis!(UARTETWISPI2, SPIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_spis!(UARTETWISPI3, SPIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); + impl_twim!(UARTETWISPI0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); impl_twim!(UARTETWISPI1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); impl_twim!(UARTETWISPI2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); impl_twim!(UARTETWISPI3, TWIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); +impl_twis!(UARTETWISPI0, TWIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_twis!(UARTETWISPI1, TWIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_twis!(UARTETWISPI2, TWIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_twis!(UARTETWISPI3, TWIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 25ad9049..7467dbc6 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -2,7 +2,7 @@ use core::convert::Infallible; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef}; +use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; @@ -148,7 +148,7 @@ impl Iterator for BitIter { /// GPIOTE channel driver in input mode pub struct InputChannel<'d, C: Channel, T: GpioPin> { - ch: C, + ch: PeripheralRef<'d, C>, pin: Input<'d, T>, } @@ -162,7 +162,9 @@ impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { } impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { - pub fn new(ch: C, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self { + pub fn new(ch: impl Peripheral

+ 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self { + into_ref!(ch); + let g = regs(); let num = ch.number(); @@ -215,7 +217,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { /// GPIOTE channel driver in output mode pub struct OutputChannel<'d, C: Channel, T: GpioPin> { - ch: C, + ch: PeripheralRef<'d, C>, _pin: Output<'d, T>, } @@ -229,7 +231,8 @@ impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> { } impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { - pub fn new(ch: C, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self { + pub fn new(ch: impl Peripheral

+ 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self { + into_ref!(ch); let g = regs(); let num = ch.number(); @@ -470,71 +473,49 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use futures::FutureExt; - use super::*; impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_high().await) } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_low().await) } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_rising_edge().await) } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_falling_edge().await) } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_any_edge().await) } } impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Flex<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_high().await) } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_low().await) } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_rising_edge().await) } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_falling_edge().await) } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + Ok(self.wait_for_any_edge().await) } } } diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs new file mode 100644 index 00000000..7e950775 --- /dev/null +++ b/embassy-nrf/src/i2s.rs @@ -0,0 +1,1141 @@ +#![macro_use] + +//! Support for I2S audio + +use core::future::poll_fn; +use core::marker::PhantomData; +use core::mem::size_of; +use core::ops::{Deref, DerefMut}; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_hal_common::drop::OnDrop; +use embassy_hal_common::{into_ref, PeripheralRef}; + +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::Interrupt; +use crate::pac::i2s::RegisterBlock; +use crate::util::{slice_in_ram_or, slice_ptr_parts}; +use crate::{Peripheral, EASY_DMA_SIZE}; + +pub type DoubleBuffering = MultiBuffering; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + BufferTooLong, + BufferZeroLength, + BufferNotInDataMemory, + BufferMisaligned, + BufferLengthMisaligned, +} + +/// I2S configuration. +#[derive(Clone)] +#[non_exhaustive] +pub struct Config { + pub sample_width: SampleWidth, + pub align: Align, + pub format: Format, + pub channels: Channels, +} + +impl Config { + pub fn sample_width(mut self, sample_width: SampleWidth) -> Self { + self.sample_width = sample_width; + self + } + + pub fn align(mut self, align: Align) -> Self { + self.align = align; + self + } + + pub fn format(mut self, format: Format) -> Self { + self.format = format; + self + } + + pub fn channels(mut self, channels: Channels) -> Self { + self.channels = channels; + self + } +} + +impl Default for Config { + fn default() -> Self { + Self { + sample_width: SampleWidth::_16bit, + align: Align::Left, + format: Format::I2S, + channels: Channels::Stereo, + } + } +} + +/// I2S Mode +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub struct MasterClock { + freq: MckFreq, + ratio: Ratio, +} + +impl MasterClock { + pub fn new(freq: MckFreq, ratio: Ratio) -> Self { + Self { freq, ratio } + } +} + +impl MasterClock { + pub fn sample_rate(&self) -> u32 { + self.freq.to_frequency() / self.ratio.to_divisor() + } +} + +/// Master clock generator frequency. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum MckFreq { + _32MDiv8, + _32MDiv10, + _32MDiv11, + _32MDiv15, + _32MDiv16, + _32MDiv21, + _32MDiv23, + _32MDiv30, + _32MDiv31, + _32MDiv32, + _32MDiv42, + _32MDiv63, + _32MDiv125, +} + +impl MckFreq { + const REGISTER_VALUES: &'static [u32] = &[ + 0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000, + 0x08000000, 0x06000000, 0x04100000, 0x020C0000, + ]; + + const FREQUENCIES: &'static [u32] = &[ + 4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936, + 256000, + ]; + + /// Return the value that needs to be written to the register. + pub fn to_register_value(&self) -> u32 { + Self::REGISTER_VALUES[usize::from(*self)] + } + + /// Return the master clock frequency. + pub fn to_frequency(&self) -> u32 { + Self::FREQUENCIES[usize::from(*self)] + } +} + +impl From for usize { + fn from(variant: MckFreq) -> Self { + variant as _ + } +} + +/// Master clock frequency ratio +/// +/// Sample Rate = LRCK = MCK / Ratio +/// +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Ratio { + _32x, + _48x, + _64x, + _96x, + _128x, + _192x, + _256x, + _384x, + _512x, +} + +impl Ratio { + const RATIOS: &'static [u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; + + /// Return the value that needs to be written to the register. + pub fn to_register_value(&self) -> u8 { + usize::from(*self) as u8 + } + + pub fn to_divisor(&self) -> u32 { + Self::RATIOS[usize::from(*self)] + } +} + +impl From for usize { + fn from(variant: Ratio) -> Self { + variant as _ + } +} + +/// Approximate sample rates. +/// +/// Those are common sample rates that can not be configured without an small error. +/// +/// For custom master clock configuration, please refer to [MasterClock]. +#[derive(Clone, Copy)] +pub enum ApproxSampleRate { + _11025, + _16000, + _22050, + _32000, + _44100, + _48000, +} + +impl From for MasterClock { + fn from(value: ApproxSampleRate) -> Self { + match value { + // error = 86 + ApproxSampleRate::_11025 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_192x), + // error = 127 + ApproxSampleRate::_16000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_96x), + // error = 172 + ApproxSampleRate::_22050 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_96x), + // error = 254 + ApproxSampleRate::_32000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_48x), + // error = 344 + ApproxSampleRate::_44100 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_48x), + // error = 381 + ApproxSampleRate::_48000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_32x), + } + } +} + +impl ApproxSampleRate { + pub fn sample_rate(&self) -> u32 { + MasterClock::from(*self).sample_rate() + } +} + +/// Exact sample rates. +/// +/// Those are non standard sample rates that can be configured without error. +/// +/// For custom master clock configuration, please refer to [Mode]. +#[derive(Clone, Copy)] +pub enum ExactSampleRate { + _8000, + _10582, + _12500, + _15625, + _15873, + _25000, + _31250, + _50000, + _62500, + _100000, + _125000, +} + +impl ExactSampleRate { + pub fn sample_rate(&self) -> u32 { + MasterClock::from(*self).sample_rate() + } +} + +impl From for MasterClock { + fn from(value: ExactSampleRate) -> Self { + match value { + ExactSampleRate::_8000 => MasterClock::new(MckFreq::_32MDiv125, Ratio::_32x), + ExactSampleRate::_10582 => MasterClock::new(MckFreq::_32MDiv63, Ratio::_48x), + ExactSampleRate::_12500 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_256x), + ExactSampleRate::_15625 => MasterClock::new(MckFreq::_32MDiv32, Ratio::_64x), + ExactSampleRate::_15873 => MasterClock::new(MckFreq::_32MDiv63, Ratio::_32x), + ExactSampleRate::_25000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_128x), + ExactSampleRate::_31250 => MasterClock::new(MckFreq::_32MDiv32, Ratio::_32x), + ExactSampleRate::_50000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_64x), + ExactSampleRate::_62500 => MasterClock::new(MckFreq::_32MDiv16, Ratio::_32x), + ExactSampleRate::_100000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_32x), + ExactSampleRate::_125000 => MasterClock::new(MckFreq::_32MDiv8, Ratio::_32x), + } + } +} + +/// Sample width. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum SampleWidth { + _8bit, + _16bit, + _24bit, +} + +impl From for u8 { + fn from(variant: SampleWidth) -> Self { + variant as _ + } +} + +/// Channel used for the most significant sample value in a frame. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Align { + Left, + Right, +} + +impl From for bool { + fn from(variant: Align) -> Self { + match variant { + Align::Left => false, + Align::Right => true, + } + } +} + +/// Frame format. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Format { + I2S, + Aligned, +} + +impl From for bool { + fn from(variant: Format) -> Self { + match variant { + Format::I2S => false, + Format::Aligned => true, + } + } +} + +/// Channels +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Channels { + Stereo, + MonoLeft, + MonoRight, +} + +impl From for u8 { + fn from(variant: Channels) -> Self { + variant as _ + } +} + +/// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. +pub struct I2S<'d, T: Instance> { + i2s: PeripheralRef<'d, T>, + irq: PeripheralRef<'d, T::Interrupt>, + mck: Option>, + sck: PeripheralRef<'d, AnyPin>, + lrck: PeripheralRef<'d, AnyPin>, + sdin: Option>, + sdout: Option>, + master_clock: Option, + config: Config, +} + +impl<'d, T: Instance> I2S<'d, T> { + /// Create a new I2S in master mode + pub fn master( + i2s: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + mck: impl Peripheral

+ 'd, + sck: impl Peripheral

+ 'd, + lrck: impl Peripheral

+ 'd, + master_clock: MasterClock, + config: Config, + ) -> Self { + into_ref!(i2s, irq, mck, sck, lrck); + Self { + i2s, + irq, + mck: Some(mck.map_into()), + sck: sck.map_into(), + lrck: lrck.map_into(), + sdin: None, + sdout: None, + master_clock: Some(master_clock), + config, + } + } + + /// Create a new I2S in slave mode + pub fn slave( + i2s: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + sck: impl Peripheral

+ 'd, + lrck: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(i2s, irq, sck, lrck); + Self { + i2s, + irq, + mck: None, + sck: sck.map_into(), + lrck: lrck.map_into(), + sdin: None, + sdout: None, + master_clock: None, + config, + } + } + + /// I2S output only + pub fn output( + mut self, + sdout: impl Peripheral

+ 'd, + buffers: MultiBuffering, + ) -> OutputStream<'d, T, S, NB, NS> { + self.sdout = Some(sdout.into_ref().map_into()); + OutputStream { + _p: self.build(), + buffers, + } + } + + /// I2S input only + pub fn input( + mut self, + sdin: impl Peripheral

+ 'd, + buffers: MultiBuffering, + ) -> InputStream<'d, T, S, NB, NS> { + self.sdin = Some(sdin.into_ref().map_into()); + InputStream { + _p: self.build(), + buffers, + } + } + + /// I2S full duplex (input and output) + pub fn full_duplex( + mut self, + sdin: impl Peripheral

+ 'd, + sdout: impl Peripheral

+ 'd, + buffers_out: MultiBuffering, + buffers_in: MultiBuffering, + ) -> FullDuplexStream<'d, T, S, NB, NS> { + self.sdout = Some(sdout.into_ref().map_into()); + self.sdin = Some(sdin.into_ref().map_into()); + + FullDuplexStream { + _p: self.build(), + buffers_out, + buffers_in, + } + } + + fn build(self) -> PeripheralRef<'d, T> { + self.apply_config(); + self.select_pins(); + self.setup_interrupt(); + + let device = Device::::new(); + device.enable(); + + self.i2s + } + + fn apply_config(&self) { + let c = &T::regs().config; + match &self.master_clock { + Some(MasterClock { freq, ratio }) => { + c.mode.write(|w| w.mode().master()); + c.mcken.write(|w| w.mcken().enabled()); + c.mckfreq + .write(|w| unsafe { w.mckfreq().bits(freq.to_register_value()) }); + c.ratio.write(|w| unsafe { w.ratio().bits(ratio.to_register_value()) }); + } + None => { + c.mode.write(|w| w.mode().slave()); + } + }; + + c.swidth + .write(|w| unsafe { w.swidth().bits(self.config.sample_width.into()) }); + c.align.write(|w| w.align().bit(self.config.align.into())); + c.format.write(|w| w.format().bit(self.config.format.into())); + c.channels + .write(|w| unsafe { w.channels().bits(self.config.channels.into()) }); + } + + fn select_pins(&self) { + let psel = &T::regs().psel; + + if let Some(mck) = &self.mck { + psel.mck.write(|w| { + unsafe { w.bits(mck.psel_bits()) }; + w.connect().connected() + }); + } + + psel.sck.write(|w| { + unsafe { w.bits(self.sck.psel_bits()) }; + w.connect().connected() + }); + + psel.lrck.write(|w| { + unsafe { w.bits(self.lrck.psel_bits()) }; + w.connect().connected() + }); + + if let Some(sdin) = &self.sdin { + psel.sdin.write(|w| { + unsafe { w.bits(sdin.psel_bits()) }; + w.connect().connected() + }); + } + + if let Some(sdout) = &self.sdout { + psel.sdout.write(|w| { + unsafe { w.bits(sdout.psel_bits()) }; + w.connect().connected() + }); + } + } + + fn setup_interrupt(&self) { + self.irq.set_handler(Self::on_interrupt); + self.irq.unpend(); + self.irq.enable(); + + let device = Device::::new(); + device.disable_tx_ptr_interrupt(); + device.disable_rx_ptr_interrupt(); + device.disable_stopped_interrupt(); + + device.reset_tx_ptr_event(); + device.reset_rx_ptr_event(); + device.reset_stopped_event(); + + device.enable_tx_ptr_interrupt(); + device.enable_rx_ptr_interrupt(); + device.enable_stopped_interrupt(); + } + + fn on_interrupt(_: *mut ()) { + let device = Device::::new(); + let s = T::state(); + + if device.is_tx_ptr_updated() { + trace!("TX INT"); + s.tx_waker.wake(); + device.disable_tx_ptr_interrupt(); + } + + if device.is_rx_ptr_updated() { + trace!("RX INT"); + s.rx_waker.wake(); + device.disable_rx_ptr_interrupt(); + } + + if device.is_stopped() { + trace!("STOPPED INT"); + s.stop_waker.wake(); + device.disable_stopped_interrupt(); + } + } + + async fn stop() { + compiler_fence(Ordering::SeqCst); + + let device = Device::::new(); + device.stop(); + + T::state().started.store(false, Ordering::Relaxed); + + poll_fn(|cx| { + T::state().stop_waker.register(cx.waker()); + + if device.is_stopped() { + trace!("STOP: Ready"); + device.reset_stopped_event(); + Poll::Ready(()) + } else { + trace!("STOP: Pending"); + Poll::Pending + } + }) + .await; + + device.disable(); + } + + async fn send_from_ram(buffer_ptr: *const [S]) -> Result<(), Error> + where + S: Sample, + { + trace!("SEND: {}", buffer_ptr as *const S as u32); + + slice_in_ram_or(buffer_ptr, Error::BufferNotInDataMemory)?; + + compiler_fence(Ordering::SeqCst); + + let device = Device::::new(); + + device.update_tx(buffer_ptr)?; + + Self::wait_tx_ptr_update().await; + + compiler_fence(Ordering::SeqCst); + + Ok(()) + } + + async fn wait_tx_ptr_update() { + let drop = OnDrop::new(move || { + trace!("TX DROP: Stopping"); + + let device = Device::::new(); + device.disable_tx_ptr_interrupt(); + device.reset_tx_ptr_event(); + device.disable_tx(); + + // TX is stopped almost instantly, spinning is fine. + while !device.is_tx_ptr_updated() {} + + trace!("TX DROP: Stopped"); + }); + + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + + let device = Device::::new(); + if device.is_tx_ptr_updated() { + trace!("TX POLL: Ready"); + device.reset_tx_ptr_event(); + device.enable_tx_ptr_interrupt(); + Poll::Ready(()) + } else { + trace!("TX POLL: Pending"); + Poll::Pending + } + }) + .await; + + drop.defuse(); + } + + async fn receive_from_ram(buffer_ptr: *mut [S]) -> Result<(), Error> + where + S: Sample, + { + trace!("RECEIVE: {}", buffer_ptr as *const S as u32); + + // NOTE: RAM slice check for rx is not necessary, as a mutable + // slice can only be built from data located in RAM. + + compiler_fence(Ordering::SeqCst); + + let device = Device::::new(); + + device.update_rx(buffer_ptr)?; + + Self::wait_rx_ptr_update().await; + + compiler_fence(Ordering::SeqCst); + + Ok(()) + } + + async fn wait_rx_ptr_update() { + let drop = OnDrop::new(move || { + trace!("RX DROP: Stopping"); + + let device = Device::::new(); + device.disable_rx_ptr_interrupt(); + device.reset_rx_ptr_event(); + device.disable_rx(); + + // TX is stopped almost instantly, spinning is fine. + while !device.is_rx_ptr_updated() {} + + trace!("RX DROP: Stopped"); + }); + + poll_fn(|cx| { + T::state().rx_waker.register(cx.waker()); + + let device = Device::::new(); + if device.is_rx_ptr_updated() { + trace!("RX POLL: Ready"); + device.reset_rx_ptr_event(); + device.enable_rx_ptr_interrupt(); + Poll::Ready(()) + } else { + trace!("RX POLL: Pending"); + Poll::Pending + } + }) + .await; + + drop.defuse(); + } +} + +/// I2S output +pub struct OutputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { + _p: PeripheralRef<'d, T>, + buffers: MultiBuffering, +} + +impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> OutputStream<'d, T, S, NB, NS> { + /// Get a mutable reference to the current buffer. + pub fn buffer(&mut self) -> &mut [S] { + self.buffers.get_mut() + } + + /// Prepare the initial buffer and start the I2S transfer. + pub async fn start(&mut self) -> Result<(), Error> + where + S: Sample, + { + let device = Device::::new(); + + let s = T::state(); + if s.started.load(Ordering::Relaxed) { + self.stop().await; + } + + device.enable(); + device.enable_tx(); + + device.update_tx(self.buffers.switch())?; + + s.started.store(true, Ordering::Relaxed); + + device.start(); + + I2S::::wait_tx_ptr_update().await; + + Ok(()) + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + I2S::::stop().await + } + + /// Sends the current buffer for transmission in the DMA. + /// Switches to use the next available buffer. + pub async fn send(&mut self) -> Result<(), Error> + where + S: Sample, + { + I2S::::send_from_ram(self.buffers.switch()).await + } +} + +/// I2S input +pub struct InputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { + _p: PeripheralRef<'d, T>, + buffers: MultiBuffering, +} + +impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> InputStream<'d, T, S, NB, NS> { + /// Get a mutable reference to the current buffer. + pub fn buffer(&mut self) -> &mut [S] { + self.buffers.get_mut() + } + + /// Prepare the initial buffer and start the I2S transfer. + pub async fn start(&mut self) -> Result<(), Error> + where + S: Sample, + { + let device = Device::::new(); + + let s = T::state(); + if s.started.load(Ordering::Relaxed) { + self.stop().await; + } + + device.enable(); + device.enable_rx(); + + device.update_rx(self.buffers.switch())?; + + s.started.store(true, Ordering::Relaxed); + + device.start(); + + I2S::::wait_rx_ptr_update().await; + + Ok(()) + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + I2S::::stop().await + } + + /// Sets the current buffer for reception from the DMA. + /// Switches to use the next available buffer. + #[allow(unused_mut)] + pub async fn receive(&mut self) -> Result<(), Error> + where + S: Sample, + { + I2S::::receive_from_ram(self.buffers.switch_mut()).await + } +} + +/// I2S full duplex stream (input & output) +pub struct FullDuplexStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { + _p: PeripheralRef<'d, T>, + buffers_out: MultiBuffering, + buffers_in: MultiBuffering, +} + +impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> FullDuplexStream<'d, T, S, NB, NS> { + /// Get the current output and input buffers. + pub fn buffers(&mut self) -> (&mut [S], &[S]) { + (self.buffers_out.get_mut(), self.buffers_in.get()) + } + + /// Prepare the initial buffers and start the I2S transfer. + pub async fn start(&mut self) -> Result<(), Error> + where + S: Sample, + { + let device = Device::::new(); + + let s = T::state(); + if s.started.load(Ordering::Relaxed) { + self.stop().await; + } + + device.enable(); + device.enable_tx(); + device.enable_rx(); + + device.update_tx(self.buffers_out.switch())?; + device.update_rx(self.buffers_in.switch_mut())?; + + s.started.store(true, Ordering::Relaxed); + + device.start(); + + I2S::::wait_tx_ptr_update().await; + I2S::::wait_rx_ptr_update().await; + + Ok(()) + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + I2S::::stop().await + } + + /// Sets the current buffers for output and input for transmission/reception from the DMA. + /// Switch to use the next available buffers for output/input. + pub async fn send_and_receive(&mut self) -> Result<(), Error> + where + S: Sample, + { + I2S::::send_from_ram(self.buffers_out.switch()).await?; + I2S::::receive_from_ram(self.buffers_in.switch_mut()).await?; + Ok(()) + } +} + +/// Helper encapsulating common I2S device operations. +struct Device(&'static RegisterBlock, PhantomData); + +impl Device { + fn new() -> Self { + Self(T::regs(), PhantomData) + } + + #[inline(always)] + pub fn enable(&self) { + trace!("ENABLED"); + self.0.enable.write(|w| w.enable().enabled()); + } + + #[inline(always)] + pub fn disable(&self) { + trace!("DISABLED"); + self.0.enable.write(|w| w.enable().disabled()); + } + + #[inline(always)] + fn enable_tx(&self) { + trace!("TX ENABLED"); + self.0.config.txen.write(|w| w.txen().enabled()); + } + + #[inline(always)] + fn disable_tx(&self) { + trace!("TX DISABLED"); + self.0.config.txen.write(|w| w.txen().disabled()); + } + + #[inline(always)] + fn enable_rx(&self) { + trace!("RX ENABLED"); + self.0.config.rxen.write(|w| w.rxen().enabled()); + } + + #[inline(always)] + fn disable_rx(&self) { + trace!("RX DISABLED"); + self.0.config.rxen.write(|w| w.rxen().disabled()); + } + + #[inline(always)] + fn start(&self) { + trace!("START"); + self.0.tasks_start.write(|w| unsafe { w.bits(1) }); + } + + #[inline(always)] + fn stop(&self) { + self.0.tasks_stop.write(|w| unsafe { w.bits(1) }); + } + + #[inline(always)] + fn is_stopped(&self) -> bool { + self.0.events_stopped.read().bits() != 0 + } + + #[inline(always)] + fn reset_stopped_event(&self) { + trace!("STOPPED EVENT: Reset"); + self.0.events_stopped.reset(); + } + + #[inline(always)] + fn disable_stopped_interrupt(&self) { + trace!("STOPPED INTERRUPT: Disabled"); + self.0.intenclr.write(|w| w.stopped().clear()); + } + + #[inline(always)] + fn enable_stopped_interrupt(&self) { + trace!("STOPPED INTERRUPT: Enabled"); + self.0.intenset.write(|w| w.stopped().set()); + } + + #[inline(always)] + fn reset_tx_ptr_event(&self) { + trace!("TX PTR EVENT: Reset"); + self.0.events_txptrupd.reset(); + } + + #[inline(always)] + fn reset_rx_ptr_event(&self) { + trace!("RX PTR EVENT: Reset"); + self.0.events_rxptrupd.reset(); + } + + #[inline(always)] + fn disable_tx_ptr_interrupt(&self) { + trace!("TX PTR INTERRUPT: Disabled"); + self.0.intenclr.write(|w| w.txptrupd().clear()); + } + + #[inline(always)] + fn disable_rx_ptr_interrupt(&self) { + trace!("RX PTR INTERRUPT: Disabled"); + self.0.intenclr.write(|w| w.rxptrupd().clear()); + } + + #[inline(always)] + fn enable_tx_ptr_interrupt(&self) { + trace!("TX PTR INTERRUPT: Enabled"); + self.0.intenset.write(|w| w.txptrupd().set()); + } + + #[inline(always)] + fn enable_rx_ptr_interrupt(&self) { + trace!("RX PTR INTERRUPT: Enabled"); + self.0.intenset.write(|w| w.rxptrupd().set()); + } + + #[inline(always)] + fn is_tx_ptr_updated(&self) -> bool { + self.0.events_txptrupd.read().bits() != 0 + } + + #[inline(always)] + fn is_rx_ptr_updated(&self) -> bool { + self.0.events_rxptrupd.read().bits() != 0 + } + + #[inline] + fn update_tx(&self, buffer_ptr: *const [S]) -> Result<(), Error> { + let (ptr, maxcnt) = Self::validated_dma_parts(buffer_ptr)?; + self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); + Ok(()) + } + + #[inline] + fn update_rx(&self, buffer_ptr: *const [S]) -> Result<(), Error> { + let (ptr, maxcnt) = Self::validated_dma_parts(buffer_ptr)?; + self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); + Ok(()) + } + + fn validated_dma_parts(buffer_ptr: *const [S]) -> Result<(u32, u32), Error> { + let (ptr, len) = slice_ptr_parts(buffer_ptr); + let ptr = ptr as u32; + let bytes_len = len * size_of::(); + let maxcnt = (bytes_len / size_of::()) as u32; + + trace!("PTR={}, MAXCNT={}", ptr, maxcnt); + + if ptr % 4 != 0 { + Err(Error::BufferMisaligned) + } else if bytes_len % 4 != 0 { + Err(Error::BufferLengthMisaligned) + } else if maxcnt as usize > EASY_DMA_SIZE { + Err(Error::BufferTooLong) + } else { + Ok((ptr, maxcnt)) + } + } +} + +/// Sample details +pub trait Sample: Sized + Copy + Default { + const WIDTH: usize; + const SCALE: Self; +} + +impl Sample for i8 { + const WIDTH: usize = 8; + const SCALE: Self = 1 << (Self::WIDTH - 1); +} + +impl Sample for i16 { + const WIDTH: usize = 16; + const SCALE: Self = 1 << (Self::WIDTH - 1); +} + +impl Sample for i32 { + const WIDTH: usize = 24; + const SCALE: Self = 1 << (Self::WIDTH - 1); +} + +/// A 4-bytes aligned buffer. +#[derive(Clone, Copy)] +#[repr(align(4))] +pub struct AlignedBuffer([T; N]); + +impl AlignedBuffer { + pub fn new(array: [T; N]) -> Self { + Self(array) + } +} + +impl Default for AlignedBuffer { + fn default() -> Self { + Self([T::default(); N]) + } +} + +impl Deref for AlignedBuffer { + type Target = [T]; + fn deref(&self) -> &Self::Target { + self.0.as_slice() + } +} + +impl DerefMut for AlignedBuffer { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0.as_mut_slice() + } +} + +pub struct MultiBuffering { + buffers: [AlignedBuffer; NB], + index: usize, +} + +impl MultiBuffering { + pub fn new() -> Self { + assert!(NB > 1); + Self { + buffers: [AlignedBuffer::::default(); NB], + index: 0, + } + } + + fn get(&self) -> &[S] { + &self.buffers[self.index] + } + + fn get_mut(&mut self) -> &mut [S] { + &mut self.buffers[self.index] + } + + /// Advance to use the next buffer and return a non mutable pointer to the previous one. + fn switch(&mut self) -> *const [S] { + let prev_index = self.index; + self.index = (self.index + 1) % NB; + self.buffers[prev_index].deref() as *const [S] + } + + /// Advance to use the next buffer and return a mutable pointer to the previous one. + fn switch_mut(&mut self) -> *mut [S] { + let prev_index = self.index; + self.index = (self.index + 1) % NB; + self.buffers[prev_index].deref_mut() as *mut [S] + } +} + +pub(crate) mod sealed { + use core::sync::atomic::AtomicBool; + + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub started: AtomicBool, + pub rx_waker: AtomicWaker, + pub tx_waker: AtomicWaker, + pub stop_waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + started: AtomicBool::new(false), + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + stop_waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::i2s::RegisterBlock; + fn state() -> &'static State; + } +} + +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + type Interrupt: Interrupt; +} + +macro_rules! impl_i2s { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::i2s::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::i2s::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::i2s::sealed::State { + static STATE: crate::i2s::sealed::State = crate::i2s::sealed::State::new(); + &STATE + } + } + impl crate::i2s::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index cac55071..1dd0e790 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -43,7 +43,11 @@ //! mutable slices always reside in RAM. #![no_std] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr( + feature = "nightly", + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) +)] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] #[cfg(not(any( feature = "nrf51", @@ -74,6 +78,8 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] +pub mod i2s; pub mod nvmc; #[cfg(any( feature = "nrf52810", @@ -95,10 +101,12 @@ pub mod rng; #[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; pub mod spim; +pub mod spis; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod temp; pub mod timer; pub mod twim; +pub mod twis; pub mod uarte; #[cfg(any( feature = "_nrf5340-app", @@ -266,5 +274,12 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(feature = "_time-driver")] time_driver::init(config.time_interrupt_priority); + // Disable UARTE (enabled by default for some reason) + #[cfg(feature = "_nrf9160")] + unsafe { + (*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled()); + (*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled()); + } + peripherals } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index d821d235..7bb4e39f 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -477,45 +477,34 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use core::future::Future; use super::*; impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> { - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } + async fn flush(&mut self) -> Result<(), Error> { + Ok(()) } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead for Spim<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, words: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(words) + async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> { + self.read(words).await } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite for Spim<'d, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(data) + async fn write(&mut self, data: &[u8]) -> Result<(), Error> { + self.write(data).await } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spim<'d, T> { - type TransferFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> { - self.transfer(rx, tx) + async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { + self.transfer(rx, tx).await } - type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> { - self.transfer_in_place(words) + async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> { + self.transfer_in_place(words).await } } } diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs new file mode 100644 index 00000000..44af61a1 --- /dev/null +++ b/embassy-nrf/src/spis.rs @@ -0,0 +1,539 @@ +#![macro_use] +use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_embedded_hal::SetConfig; +use embassy_hal_common::{into_ref, PeripheralRef}; +pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; + +use crate::chip::FORCE_COPY_BUFFER_SIZE; +use crate::gpio::sealed::Pin as _; +use crate::gpio::{self, AnyPin, Pin as GpioPin}; +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; +use crate::{pac, Peripheral}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + TxBufferTooLong, + RxBufferTooLong, + /// EasyDMA can only read from data memory, read only buffers in flash will fail. + DMABufferNotInDataMemory, +} + +/// Interface for the SPIS peripheral using EasyDMA to offload the transmission and reception workload. +/// +/// For more details about EasyDMA, consult the module documentation. +pub struct Spis<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +#[non_exhaustive] +pub struct Config { + pub mode: Mode, + pub orc: u8, + pub def: u8, + pub auto_acquire: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + mode: MODE_0, + orc: 0x00, + def: 0x00, + auto_acquire: true, + } + } +} + +impl<'d, T: Instance> Spis<'d, T> { + pub fn new( + spis: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + cs: impl Peripheral

+ 'd, + sck: impl Peripheral

+ 'd, + miso: impl Peripheral

+ 'd, + mosi: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(cs, sck, miso, mosi); + Self::new_inner( + spis, + irq, + cs.map_into(), + sck.map_into(), + Some(miso.map_into()), + Some(mosi.map_into()), + config, + ) + } + + pub fn new_txonly( + spis: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + cs: impl Peripheral

+ 'd, + sck: impl Peripheral

+ 'd, + miso: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(cs, sck, miso); + Self::new_inner( + spis, + irq, + cs.map_into(), + sck.map_into(), + Some(miso.map_into()), + None, + config, + ) + } + + pub fn new_rxonly( + spis: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + cs: impl Peripheral

+ 'd, + sck: impl Peripheral

+ 'd, + mosi: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(cs, sck, mosi); + Self::new_inner( + spis, + irq, + cs.map_into(), + sck.map_into(), + None, + Some(mosi.map_into()), + config, + ) + } + + fn new_inner( + spis: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + cs: PeripheralRef<'d, AnyPin>, + sck: PeripheralRef<'d, AnyPin>, + miso: Option>, + mosi: Option>, + config: Config, + ) -> Self { + compiler_fence(Ordering::SeqCst); + + into_ref!(spis, irq, cs, sck); + + let r = T::regs(); + + // Configure pins. + sck.conf().write(|w| w.input().connect().drive().h0h1()); + r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); + cs.conf().write(|w| w.input().connect().drive().h0h1()); + r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) }); + if let Some(mosi) = &mosi { + mosi.conf().write(|w| w.input().connect().drive().h0h1()); + r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) }); + } + if let Some(miso) = &miso { + miso.conf().write(|w| w.dir().output().drive().h0h1()); + r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) }); + } + + // Enable SPIS instance. + r.enable.write(|w| w.enable().enabled()); + + // Configure mode. + let mode = config.mode; + r.config.write(|w| { + match mode { + MODE_0 => { + w.order().msb_first(); + w.cpol().active_high(); + w.cpha().leading(); + } + MODE_1 => { + w.order().msb_first(); + w.cpol().active_high(); + w.cpha().trailing(); + } + MODE_2 => { + w.order().msb_first(); + w.cpol().active_low(); + w.cpha().leading(); + } + MODE_3 => { + w.order().msb_first(); + w.cpol().active_low(); + w.cpha().trailing(); + } + } + + w + }); + + // Set over-read character. + let orc = config.orc; + r.orc.write(|w| unsafe { w.orc().bits(orc) }); + + // Set default character. + let def = config.def; + r.def.write(|w| unsafe { w.def().bits(def) }); + + // Configure auto-acquire on 'transfer end' event. + if config.auto_acquire { + r.shorts.write(|w| w.end_acquire().bit(true)); + } + + // Disable all events interrupts. + r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + Self { _p: spis } + } + + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + if r.events_end.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.end().clear()); + } + + if r.events_acquired.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.acquired().clear()); + } + } + + fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { + slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; + // NOTE: RAM slice check for rx is not necessary, as a mutable + // slice can only be built from data located in RAM. + + compiler_fence(Ordering::SeqCst); + + let r = T::regs(); + + // Set up the DMA write. + let (ptr, len) = slice_ptr_parts(tx); + r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); + r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + // Set up the DMA read. + let (ptr, len) = slice_ptr_parts_mut(rx); + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + // Reset end event. + r.events_end.reset(); + + // Release the semaphore. + r.tasks_release.write(|w| unsafe { w.bits(1) }); + + Ok(()) + } + + fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> { + compiler_fence(Ordering::SeqCst); + let r = T::regs(); + + // Acquire semaphore. + if r.semstat.read().bits() != 1 { + r.events_acquired.reset(); + r.tasks_acquire.write(|w| unsafe { w.bits(1) }); + // Wait until CPU has acquired the semaphore. + while r.semstat.read().bits() != 1 {} + } + + self.prepare(rx, tx)?; + + // Wait for 'end' event. + while r.events_end.read().bits() == 0 {} + + let n_rx = r.rxd.amount.read().bits() as usize; + let n_tx = r.txd.amount.read().bits() as usize; + + compiler_fence(Ordering::SeqCst); + + Ok((n_rx, n_tx)) + } + + fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { + match self.blocking_inner_from_ram(rx, tx) { + Ok(n) => Ok(n), + Err(Error::DMABufferNotInDataMemory) => { + trace!("Copying SPIS tx buffer into RAM for DMA"); + let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; + tx_ram_buf.copy_from_slice(tx); + self.blocking_inner_from_ram(rx, tx_ram_buf) + } + Err(error) => Err(error), + } + } + + async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> { + let r = T::regs(); + let s = T::state(); + + // Clear status register. + r.status.write(|w| w.overflow().clear().overread().clear()); + + // Acquire semaphore. + if r.semstat.read().bits() != 1 { + // Reset and enable the acquire event. + r.events_acquired.reset(); + r.intenset.write(|w| w.acquired().set()); + + // Request acquiring the SPIS semaphore. + r.tasks_acquire.write(|w| unsafe { w.bits(1) }); + + // Wait until CPU has acquired the semaphore. + poll_fn(|cx| { + s.waker.register(cx.waker()); + if r.events_acquired.read().bits() == 1 { + r.events_acquired.reset(); + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + } + + self.prepare(rx, tx)?; + + // Wait for 'end' event. + r.intenset.write(|w| w.end().set()); + poll_fn(|cx| { + s.waker.register(cx.waker()); + if r.events_end.read().bits() != 0 { + r.events_end.reset(); + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + let n_rx = r.rxd.amount.read().bits() as usize; + let n_tx = r.txd.amount.read().bits() as usize; + + compiler_fence(Ordering::SeqCst); + + Ok((n_rx, n_tx)) + } + + async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { + match self.async_inner_from_ram(rx, tx).await { + Ok(n) => Ok(n), + Err(Error::DMABufferNotInDataMemory) => { + trace!("Copying SPIS tx buffer into RAM for DMA"); + let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()]; + tx_ram_buf.copy_from_slice(tx); + self.async_inner_from_ram(rx, tx_ram_buf).await + } + Err(error) => Err(error), + } + } + + /// Reads data from the SPI bus without sending anything. Blocks until `cs` is deasserted. + /// Returns number of bytes read. + pub fn blocking_read(&mut self, data: &mut [u8]) -> Result { + self.blocking_inner(data, &[]).map(|n| n.0) + } + + /// Simultaneously sends and receives data. Blocks until the transmission is completed. + /// If necessary, the write buffer will be copied into RAM (see struct description for detail). + /// Returns number of bytes transferred `(n_rx, n_tx)`. + pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { + self.blocking_inner(read, write) + } + + /// Same as [`blocking_transfer`](Spis::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + /// Returns number of bytes transferred `(n_rx, n_tx)`. + pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { + self.blocking_inner_from_ram(read, write) + } + + /// Simultaneously sends and receives data. + /// Places the received data into the same buffer and blocks until the transmission is completed. + /// Returns number of bytes transferred. + pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result { + self.blocking_inner_from_ram(data, data).map(|n| n.0) + } + + /// Sends data, discarding any received data. Blocks until the transmission is completed. + /// If necessary, the write buffer will be copied into RAM (see struct description for detail). + /// Returns number of bytes written. + pub fn blocking_write(&mut self, data: &[u8]) -> Result { + self.blocking_inner(&mut [], data).map(|n| n.1) + } + + /// Same as [`blocking_write`](Spis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + /// Returns number of bytes written. + pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result { + self.blocking_inner_from_ram(&mut [], data).map(|n| n.1) + } + + /// Reads data from the SPI bus without sending anything. + /// Returns number of bytes read. + pub async fn read(&mut self, data: &mut [u8]) -> Result { + self.async_inner(data, &[]).await.map(|n| n.0) + } + + /// Simultaneously sends and receives data. + /// If necessary, the write buffer will be copied into RAM (see struct description for detail). + /// Returns number of bytes transferred `(n_rx, n_tx)`. + pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { + self.async_inner(read, write).await + } + + /// Same as [`transfer`](Spis::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + /// Returns number of bytes transferred `(n_rx, n_tx)`. + pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> { + self.async_inner_from_ram(read, write).await + } + + /// Simultaneously sends and receives data. Places the received data into the same buffer. + /// Returns number of bytes transferred. + pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result { + self.async_inner_from_ram(data, data).await.map(|n| n.0) + } + + /// Sends data, discarding any received data. + /// If necessary, the write buffer will be copied into RAM (see struct description for detail). + /// Returns number of bytes written. + pub async fn write(&mut self, data: &[u8]) -> Result { + self.async_inner(&mut [], data).await.map(|n| n.1) + } + + /// Same as [`write`](Spis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + /// Returns number of bytes written. + pub async fn write_from_ram(&mut self, data: &[u8]) -> Result { + self.async_inner_from_ram(&mut [], data).await.map(|n| n.1) + } + + /// Checks if last transaction overread. + pub fn is_overread(&mut self) -> bool { + T::regs().status.read().overread().is_present() + } + + /// Checks if last transaction overflowed. + pub fn is_overflow(&mut self) -> bool { + T::regs().status.read().overflow().is_present() + } +} + +impl<'d, T: Instance> Drop for Spis<'d, T> { + fn drop(&mut self) { + trace!("spis drop"); + + // Disable + let r = T::regs(); + r.enable.write(|w| w.enable().disabled()); + + gpio::deconfigure_pin(r.psel.sck.read().bits()); + gpio::deconfigure_pin(r.psel.csn.read().bits()); + gpio::deconfigure_pin(r.psel.miso.read().bits()); + gpio::deconfigure_pin(r.psel.mosi.read().bits()); + + trace!("spis drop: done"); + } +} + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + use super::*; + + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static pac::spis0::RegisterBlock; + fn state() -> &'static State; + } +} + +pub trait Instance: Peripheral

+ sealed::Instance + 'static { + type Interrupt: Interrupt; +} + +macro_rules! impl_spis { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::spis::sealed::Instance for peripherals::$type { + fn regs() -> &'static pac::spis0::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::spis::sealed::State { + static STATE: crate::spis::sealed::State = crate::spis::sealed::State::new(); + &STATE + } + } + impl crate::spis::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} + +// ==================== + +impl<'d, T: Instance> SetConfig for Spis<'d, T> { + type Config = Config; + fn set_config(&mut self, config: &Self::Config) { + let r = T::regs(); + // Configure mode. + let mode = config.mode; + r.config.write(|w| { + match mode { + MODE_0 => { + w.order().msb_first(); + w.cpol().active_high(); + w.cpha().leading(); + } + MODE_1 => { + w.order().msb_first(); + w.cpol().active_high(); + w.cpha().trailing(); + } + MODE_2 => { + w.order().msb_first(); + w.cpol().active_low(); + w.cpha().leading(); + } + MODE_3 => { + w.order().msb_first(); + w.cpol().active_low(); + w.cpha().trailing(); + } + } + + w + }); + + // Set over-read character. + let orc = config.orc; + r.orc.write(|w| unsafe { w.orc().bits(orc) }); + + // Set default character. + let def = config.def; + r.def.write(|w| unsafe { w.def().bits(def) }); + + // Configure auto-acquire on 'transfer end' event. + let auto_acquire = config.auto_acquire; + r.shorts.write(|w| w.end_acquire().bit(auto_acquire)); + } +} diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 8d6171fa..4eafd18c 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -841,39 +841,31 @@ mod eh1 { mod eha { use super::*; impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(address, buffer) + async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), Error> { + self.read(address, buffer).await } - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(address, bytes) + async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), Error> { + self.write(address, bytes).await } - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write_read<'a>( + async fn write_read<'a>( &'a mut self, address: u8, wr_buffer: &'a [u8], rd_buffer: &'a mut [u8], - ) -> Self::WriteReadFuture<'a> { - self.write_read(address, wr_buffer, rd_buffer) + ) -> Result<(), Error> { + self.write_read(address, wr_buffer, rd_buffer).await } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: u8, operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + ) -> Result<(), Error> { let _ = address; let _ = operations; - async move { todo!() } + todo!() } } } diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs new file mode 100644 index 00000000..4091b017 --- /dev/null +++ b/embassy-nrf/src/twis.rs @@ -0,0 +1,759 @@ +#![macro_use] + +//! HAL interface to the TWIS peripheral. +//! +//! See product specification: +//! +//! - nRF52832: Section 33 +//! - nRF52840: Section 6.31 +use core::future::{poll_fn, Future}; +use core::sync::atomic::compiler_fence; +use core::sync::atomic::Ordering::SeqCst; +use core::task::Poll; + +use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +#[cfg(feature = "time")] +use embassy_time::{Duration, Instant}; + +use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; +use crate::gpio::Pin as GpioPin; +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::util::slice_in_ram_or; +use crate::{gpio, pac, Peripheral}; + +#[non_exhaustive] +pub struct Config { + pub address0: u8, + pub address1: Option, + pub orc: u8, + pub sda_high_drive: bool, + pub sda_pullup: bool, + pub scl_high_drive: bool, + pub scl_pullup: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + address0: 0x55, + address1: None, + orc: 0x00, + scl_high_drive: false, + sda_pullup: false, + sda_high_drive: false, + scl_pullup: false, + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum Status { + Read, + Write, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + TxBufferTooLong, + RxBufferTooLong, + DataNack, + Bus, + DMABufferNotInDataMemory, + Overflow, + OverRead, + Timeout, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Command { + Read, + WriteRead(usize), + Write(usize), +} + +/// Interface to a TWIS instance using EasyDMA to offload the transmission and reception workload. +/// +/// For more details about EasyDMA, consult the module documentation. +pub struct Twis<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Twis<'d, T> { + pub fn new( + twis: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + sda: impl Peripheral

+ 'd, + scl: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(twis, irq, sda, scl); + + let r = T::regs(); + + // Configure pins + sda.conf().write(|w| { + w.dir().input(); + w.input().connect(); + if config.sda_high_drive { + w.drive().h0d1(); + } else { + w.drive().s0d1(); + } + if config.sda_pullup { + w.pull().pullup(); + } + w + }); + scl.conf().write(|w| { + w.dir().input(); + w.input().connect(); + if config.scl_high_drive { + w.drive().h0d1(); + } else { + w.drive().s0d1(); + } + if config.scl_pullup { + w.pull().pullup(); + } + w + }); + + // Select pins. + r.psel.sda.write(|w| unsafe { w.bits(sda.psel_bits()) }); + r.psel.scl.write(|w| unsafe { w.bits(scl.psel_bits()) }); + + // Enable TWIS instance. + r.enable.write(|w| w.enable().enabled()); + + // Disable all events interrupts + r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + + // Set address + r.address[0].write(|w| unsafe { w.address().bits(config.address0) }); + r.config.write(|w| w.address0().enabled()); + if let Some(address1) = config.address1 { + r.address[1].write(|w| unsafe { w.address().bits(address1) }); + r.config.modify(|_r, w| w.address1().enabled()); + } + + // Set over-read character + r.orc.write(|w| unsafe { w.orc().bits(config.orc) }); + + // Generate suspend on read event + r.shorts.write(|w| w.read_suspend().enabled()); + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + Self { _p: twis } + } + + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + if r.events_read.read().bits() != 0 || r.events_write.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.read().clear().write().clear()); + } + if r.events_stopped.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.stopped().clear()); + } + if r.events_error.read().bits() != 0 { + s.waker.wake(); + r.intenclr.modify(|_r, w| w.error().clear()); + } + } + + /// Set TX buffer, checking that it is in RAM and has suitable length. + unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { + slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; + + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::TxBufferTooLong); + } + + let r = T::regs(); + + r.txd.ptr.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the I2C transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + w.ptr().bits(buffer.as_ptr() as u32)); + r.txd.maxcnt.write(|w| + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to `u8` is also fine. + // + // The MAXCNT field is 8 bits wide and accepts the full range of + // values. + w.maxcnt().bits(buffer.len() as _)); + + Ok(()) + } + + /// Set RX buffer, checking that it has suitable length. + unsafe fn set_rx_buffer(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + // NOTE: RAM slice check is not necessary, as a mutable + // slice can only be built from data located in RAM. + + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::RxBufferTooLong); + } + + let r = T::regs(); + + r.rxd.ptr.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the I2C transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + // + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + w.ptr().bits(buffer.as_mut_ptr() as u32)); + r.rxd.maxcnt.write(|w| + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to the type of maxcnt + // is also fine. + // + // Note that that nrf52840 maxcnt is a wider + // type than a u8, so we use a `_` cast rather than a `u8` cast. + // The MAXCNT field is thus at least 8 bits wide and accepts the + // full range of values that fit in a `u8`. + w.maxcnt().bits(buffer.len() as _)); + + Ok(()) + } + + fn clear_errorsrc(&mut self) { + let r = T::regs(); + r.errorsrc + .write(|w| w.overflow().bit(true).overread().bit(true).dnack().bit(true)); + } + + /// Returns matched address for latest command. + pub fn address_match(&self) -> u8 { + let r = T::regs(); + r.address[r.match_.read().bits() as usize].read().address().bits() + } + + /// Returns the index of the address matched in the latest command. + pub fn address_match_index(&self) -> usize { + T::regs().match_.read().bits() as _ + } + + /// Wait for read, write, stop or error + fn blocking_listen_wait(&mut self) -> Result { + let r = T::regs(); + loop { + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + while r.events_stopped.read().bits() == 0 {} + return Err(Error::Overflow); + } + if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Err(Error::Bus); + } + if r.events_read.read().bits() != 0 { + r.events_read.reset(); + return Ok(Status::Read); + } + if r.events_write.read().bits() != 0 { + r.events_write.reset(); + return Ok(Status::Write); + } + } + } + + /// Wait for stop, repeated start or error + fn blocking_listen_wait_end(&mut self, status: Status) -> Result { + let r = T::regs(); + loop { + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Overflow); + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return match status { + Status::Read => Ok(Command::Read), + Status::Write => { + let n = r.rxd.amount.read().bits() as usize; + Ok(Command::Write(n)) + } + }; + } else if r.events_read.read().bits() != 0 { + r.events_read.reset(); + let n = r.rxd.amount.read().bits() as usize; + return Ok(Command::WriteRead(n)); + } + } + } + + /// Wait for stop or error + fn blocking_wait(&mut self) -> Result { + let r = T::regs(); + loop { + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + let errorsrc = r.errorsrc.read(); + if errorsrc.overread().is_detected() { + return Err(Error::OverRead); + } else if errorsrc.dnack().is_received() { + return Err(Error::DataNack); + } else { + return Err(Error::Bus); + } + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + let n = r.txd.amount.read().bits() as usize; + return Ok(n); + } + } + } + + /// Wait for stop or error with timeout + #[cfg(feature = "time")] + fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result { + let r = T::regs(); + let deadline = Instant::now() + timeout; + loop { + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + let errorsrc = r.errorsrc.read(); + if errorsrc.overread().is_detected() { + return Err(Error::OverRead); + } else if errorsrc.dnack().is_received() { + return Err(Error::DataNack); + } else { + return Err(Error::Bus); + } + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + let n = r.txd.amount.read().bits() as usize; + return Ok(n); + } else if Instant::now() > deadline { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Timeout); + } + } + } + + /// Wait for read, write, stop or error with timeout + #[cfg(feature = "time")] + fn blocking_listen_wait_timeout(&mut self, timeout: Duration) -> Result { + let r = T::regs(); + let deadline = Instant::now() + timeout; + loop { + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + while r.events_stopped.read().bits() == 0 {} + return Err(Error::Overflow); + } + if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Err(Error::Bus); + } + if r.events_read.read().bits() != 0 { + r.events_read.reset(); + return Ok(Status::Read); + } + if r.events_write.read().bits() != 0 { + r.events_write.reset(); + return Ok(Status::Write); + } + if Instant::now() > deadline { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Timeout); + } + } + } + + /// Wait for stop, repeated start or error with timeout + #[cfg(feature = "time")] + fn blocking_listen_wait_end_timeout(&mut self, status: Status, timeout: Duration) -> Result { + let r = T::regs(); + let deadline = Instant::now() + timeout; + loop { + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Overflow); + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return match status { + Status::Read => Ok(Command::Read), + Status::Write => { + let n = r.rxd.amount.read().bits() as usize; + Ok(Command::Write(n)) + } + }; + } else if r.events_read.read().bits() != 0 { + r.events_read.reset(); + let n = r.rxd.amount.read().bits() as usize; + return Ok(Command::WriteRead(n)); + } else if Instant::now() > deadline { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Err(Error::Timeout); + } + } + } + + /// Wait for stop or error + fn async_wait(&mut self) -> impl Future> { + poll_fn(move |cx| { + let r = T::regs(); + let s = T::state(); + + s.waker.register(cx.waker()); + + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + let errorsrc = r.errorsrc.read(); + if errorsrc.overread().is_detected() { + return Poll::Ready(Err(Error::OverRead)); + } else if errorsrc.dnack().is_received() { + return Poll::Ready(Err(Error::DataNack)); + } else { + return Poll::Ready(Err(Error::Bus)); + } + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + let n = r.txd.amount.read().bits() as usize; + return Poll::Ready(Ok(n)); + } + + Poll::Pending + }) + } + + /// Wait for read or write + fn async_listen_wait(&mut self) -> impl Future> { + poll_fn(move |cx| { + let r = T::regs(); + let s = T::state(); + + s.waker.register(cx.waker()); + + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Poll::Ready(Err(Error::Overflow)); + } else if r.events_read.read().bits() != 0 { + r.events_read.reset(); + return Poll::Ready(Ok(Status::Read)); + } else if r.events_write.read().bits() != 0 { + r.events_write.reset(); + return Poll::Ready(Ok(Status::Write)); + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Poll::Ready(Err(Error::Bus)); + } + Poll::Pending + }) + } + + /// Wait for stop, repeated start or error + fn async_listen_wait_end(&mut self, status: Status) -> impl Future> { + poll_fn(move |cx| { + let r = T::regs(); + let s = T::state(); + + s.waker.register(cx.waker()); + + // stop if an error occured + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + return Poll::Ready(Err(Error::Overflow)); + } else if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return match status { + Status::Read => Poll::Ready(Ok(Command::Read)), + Status::Write => { + let n = r.rxd.amount.read().bits() as usize; + Poll::Ready(Ok(Command::Write(n))) + } + }; + } else if r.events_read.read().bits() != 0 { + r.events_read.reset(); + let n = r.rxd.amount.read().bits() as usize; + return Poll::Ready(Ok(Command::WriteRead(n))); + } + Poll::Pending + }) + } + + fn setup_respond_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> { + let r = T::regs(); + + compiler_fence(SeqCst); + + // Set up the DMA write. + unsafe { self.set_tx_buffer(buffer)? }; + + // Clear events + r.events_stopped.reset(); + r.events_error.reset(); + self.clear_errorsrc(); + + if inten { + r.intenset.write(|w| w.stopped().set().error().set()); + } else { + r.intenclr.write(|w| w.stopped().clear().error().clear()); + } + + // Start write operation. + r.tasks_preparetx.write(|w| unsafe { w.bits(1) }); + r.tasks_resume.write(|w| unsafe { w.bits(1) }); + Ok(()) + } + + fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { + match self.setup_respond_from_ram(wr_buffer, inten) { + Ok(_) => Ok(()), + Err(Error::DMABufferNotInDataMemory) => { + trace!("Copying TWIS tx buffer into RAM for DMA"); + let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; + tx_ram_buf.copy_from_slice(wr_buffer); + self.setup_respond_from_ram(&tx_ram_buf, inten) + } + Err(error) => Err(error), + } + } + + fn setup_listen(&mut self, buffer: &mut [u8], inten: bool) -> Result<(), Error> { + let r = T::regs(); + compiler_fence(SeqCst); + + // Set up the DMA read. + unsafe { self.set_rx_buffer(buffer)? }; + + // Clear events + r.events_read.reset(); + r.events_write.reset(); + r.events_stopped.reset(); + r.events_error.reset(); + self.clear_errorsrc(); + + if inten { + r.intenset + .write(|w| w.stopped().set().error().set().read().set().write().set()); + } else { + r.intenclr + .write(|w| w.stopped().clear().error().clear().read().clear().write().clear()); + } + + // Start read operation. + r.tasks_preparerx.write(|w| unsafe { w.bits(1) }); + + Ok(()) + } + + fn setup_listen_end(&mut self, inten: bool) -> Result<(), Error> { + let r = T::regs(); + compiler_fence(SeqCst); + + // Clear events + r.events_read.reset(); + r.events_write.reset(); + r.events_stopped.reset(); + r.events_error.reset(); + self.clear_errorsrc(); + + if inten { + r.intenset.write(|w| w.stopped().set().error().set().read().set()); + } else { + r.intenclr.write(|w| w.stopped().clear().error().clear().read().clear()); + } + + Ok(()) + } + + /// Wait for commands from an I2C master. + /// `buffer` is provided in case master does a 'write' and is unused for 'read'. + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + /// To know which one of the addresses were matched, call `address_match` or `address_match_index` + pub fn blocking_listen(&mut self, buffer: &mut [u8]) -> Result { + self.setup_listen(buffer, false)?; + let status = self.blocking_listen_wait()?; + if status == Status::Write { + self.setup_listen_end(false)?; + let command = self.blocking_listen_wait_end(status)?; + return Ok(command); + } + Ok(Command::Read) + } + + /// Respond to an I2C master READ command. + /// Returns the number of bytes written. + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + pub fn blocking_respond_to_read(&mut self, buffer: &[u8]) -> Result { + self.setup_respond(buffer, false)?; + self.blocking_wait() + } + + /// Same as [`blocking_respond_to_read`](Twis::blocking_respond_to_read) but will fail instead of copying data into RAM. + /// Consult the module level documentation to learn more. + pub fn blocking_respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result { + self.setup_respond_from_ram(buffer, false)?; + self.blocking_wait() + } + + // =========================================== + + /// Wait for commands from an I2C master, with timeout. + /// `buffer` is provided in case master does a 'write' and is unused for 'read'. + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + /// To know which one of the addresses were matched, call `address_match` or `address_match_index` + #[cfg(feature = "time")] + pub fn blocking_listen_timeout(&mut self, buffer: &mut [u8], timeout: Duration) -> Result { + self.setup_listen(buffer, false)?; + let status = self.blocking_listen_wait_timeout(timeout)?; + if status == Status::Write { + self.setup_listen_end(false)?; + let command = self.blocking_listen_wait_end_timeout(status, timeout)?; + return Ok(command); + } + Ok(Command::Read) + } + + /// Respond to an I2C master READ command with timeout. + /// Returns the number of bytes written. + /// See [`blocking_respond_to_read`]. + #[cfg(feature = "time")] + pub fn blocking_respond_to_read_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { + self.setup_respond(buffer, false)?; + self.blocking_wait_timeout(timeout) + } + + /// Same as [`blocking_respond_to_read_timeout`](Twis::blocking_respond_to_read_timeout) but will fail instead of copying data into RAM. + /// Consult the module level documentation to learn more. + #[cfg(feature = "time")] + pub fn blocking_respond_to_read_from_ram_timeout( + &mut self, + buffer: &[u8], + timeout: Duration, + ) -> Result { + self.setup_respond_from_ram(buffer, false)?; + self.blocking_wait_timeout(timeout) + } + + // =========================================== + + /// Wait asynchronously for commands from an I2C master. + /// `buffer` is provided in case master does a 'write' and is unused for 'read'. + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + /// To know which one of the addresses were matched, call `address_match` or `address_match_index` + pub async fn listen(&mut self, buffer: &mut [u8]) -> Result { + self.setup_listen(buffer, true)?; + let status = self.async_listen_wait().await?; + if status == Status::Write { + self.setup_listen_end(true)?; + let command = self.async_listen_wait_end(status).await?; + return Ok(command); + } + Ok(Command::Read) + } + + /// Respond to an I2C master READ command, asynchronously. + /// Returns the number of bytes written. + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result { + self.setup_respond(buffer, true)?; + self.async_wait().await + } + + /// Same as [`respond_to_read`](Twis::respond_to_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + pub async fn respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result { + self.setup_respond_from_ram(buffer, true)?; + self.async_wait().await + } +} + +impl<'a, T: Instance> Drop for Twis<'a, T> { + fn drop(&mut self) { + trace!("twis drop"); + + // TODO: check for abort + + // disable! + let r = T::regs(); + r.enable.write(|w| w.enable().disabled()); + + gpio::deconfigure_pin(r.psel.sda.read().bits()); + gpio::deconfigure_pin(r.psel.scl.read().bits()); + + trace!("twis drop: done"); + } +} + +pub(crate) mod sealed { + use super::*; + + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static pac::twis0::RegisterBlock; + fn state() -> &'static State; + } +} + +pub trait Instance: Peripheral

+ sealed::Instance + 'static { + type Interrupt: Interrupt; +} + +macro_rules! impl_twis { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::twis::sealed::Instance for peripherals::$type { + fn regs() -> &'static pac::twis0::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::twis::sealed::State { + static STATE: crate::twis::sealed::State = crate::twis::sealed::State::new(); + &STATE + } + } + impl crate::twis::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 636d6c7a..63df1b68 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -986,7 +986,7 @@ mod eha { type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + fn flush(&mut self) -> Result<(), Self::Error> { async move { Ok(()) } } } @@ -1000,7 +1000,7 @@ mod eha { type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + fn flush(&mut self) -> Result<(), Self::Error> { async move { Ok(()) } } } @@ -1012,4 +1012,26 @@ mod eha { self.read(buffer) } } + + impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> { + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + + fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read(buffer) + } + } + + impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> { + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + + fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> { + self.write(buffer) + } + + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; + + fn flush(&mut self) -> Result<(), Self::Error> { + async move { Ok(()) } + } + } } diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index eb1472fa..ed4d5cf3 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -1,6 +1,6 @@ #![macro_use] -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::marker::PhantomData; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; @@ -28,11 +28,7 @@ static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); /// here provides a hook into determining whether it is. pub trait UsbSupply { fn is_usb_detected(&self) -> bool; - - type UsbPowerReadyFuture<'a>: Future> + 'a - where - Self: 'a; - fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_>; + async fn wait_power_ready(&mut self) -> Result<(), ()>; } pub struct Driver<'d, T: Instance, P: UsbSupply> { @@ -102,8 +98,7 @@ impl UsbSupply for PowerUsb { regs.usbregstatus.read().vbusdetect().is_vbus_present() } - type UsbPowerReadyFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_> { + async fn wait_power_ready(&mut self) -> Result<(), ()> { poll_fn(move |cx| { POWER_WAKER.register(cx.waker()); let regs = unsafe { &*pac::POWER::ptr() }; @@ -116,6 +111,7 @@ impl UsbSupply for PowerUsb { Poll::Pending } }) + .await } } @@ -147,8 +143,7 @@ impl UsbSupply for &SignalledSupply { self.usb_detected.load(Ordering::Relaxed) } - type UsbPowerReadyFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_> { + async fn wait_power_ready(&mut self) -> Result<(), ()> { poll_fn(move |cx| { POWER_WAKER.register(cx.waker()); @@ -160,6 +155,7 @@ impl UsbSupply for &SignalledSupply { Poll::Pending } }) + .await } } @@ -289,61 +285,52 @@ pub struct Bus<'d, T: Instance, P: UsbSupply> { } impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { - type EnableFuture<'a> = impl Future + 'a where Self: 'a; - type DisableFuture<'a> = impl Future + 'a where Self: 'a; - type PollFuture<'a> = impl Future + 'a where Self: 'a; - type RemoteWakeupFuture<'a> = impl Future> + 'a where Self: 'a; + async fn enable(&mut self) { + let regs = T::regs(); - fn enable(&mut self) -> Self::EnableFuture<'_> { - async move { - let regs = T::regs(); + errata::pre_enable(); - errata::pre_enable(); + regs.enable.write(|w| w.enable().enabled()); - regs.enable.write(|w| w.enable().enabled()); - - // Wait until the peripheral is ready. - regs.intenset.write(|w| w.usbevent().set_bit()); - poll_fn(|cx| { - BUS_WAKER.register(cx.waker()); - if regs.eventcause.read().ready().is_ready() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - regs.eventcause.write(|w| w.ready().clear_bit_by_one()); - - errata::post_enable(); - - unsafe { NVIC::unmask(pac::Interrupt::USBD) }; - - regs.intenset.write(|w| { - w.usbreset().set_bit(); - w.usbevent().set_bit(); - w.epdata().set_bit(); - w - }); - - if self.usb_supply.wait_power_ready().await.is_ok() { - // Enable the USB pullup, allowing enumeration. - regs.usbpullup.write(|w| w.connect().enabled()); - trace!("enabled"); + // Wait until the peripheral is ready. + regs.intenset.write(|w| w.usbevent().set_bit()); + poll_fn(|cx| { + BUS_WAKER.register(cx.waker()); + if regs.eventcause.read().ready().is_ready() { + Poll::Ready(()) } else { - trace!("usb power not ready due to usb removal"); + Poll::Pending } + }) + .await; + regs.eventcause.write(|w| w.ready().clear_bit_by_one()); + + errata::post_enable(); + + unsafe { NVIC::unmask(pac::Interrupt::USBD) }; + + regs.intenset.write(|w| { + w.usbreset().set_bit(); + w.usbevent().set_bit(); + w.epdata().set_bit(); + w + }); + + if self.usb_supply.wait_power_ready().await.is_ok() { + // Enable the USB pullup, allowing enumeration. + regs.usbpullup.write(|w| w.connect().enabled()); + trace!("enabled"); + } else { + trace!("usb power not ready due to usb removal"); } } - fn disable(&mut self) -> Self::DisableFuture<'_> { - async move { - let regs = T::regs(); - regs.enable.write(|x| x.enable().disabled()); - } + async fn disable(&mut self) { + let regs = T::regs(); + regs.enable.write(|x| x.enable().disabled()); } - fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> { + async fn poll(&mut self) -> Event { poll_fn(move |cx| { BUS_WAKER.register(cx.waker()); let regs = T::regs(); @@ -401,6 +388,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { Poll::Pending }) + .await } #[inline] @@ -493,42 +481,40 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { } #[inline] - fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> { - async move { - let regs = T::regs(); + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + let regs = T::regs(); - if regs.lowpower.read().lowpower().is_low_power() { - errata::pre_wakeup(); + if regs.lowpower.read().lowpower().is_low_power() { + errata::pre_wakeup(); - regs.lowpower.write(|w| w.lowpower().force_normal()); + regs.lowpower.write(|w| w.lowpower().force_normal()); - poll_fn(|cx| { - BUS_WAKER.register(cx.waker()); - let regs = T::regs(); - let r = regs.eventcause.read(); + poll_fn(|cx| { + BUS_WAKER.register(cx.waker()); + let regs = T::regs(); + let r = regs.eventcause.read(); - if regs.events_usbreset.read().bits() != 0 { - Poll::Ready(()) - } else if r.resume().bit() { - Poll::Ready(()) - } else if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().allowed()); + if regs.events_usbreset.read().bits() != 0 { + Poll::Ready(()) + } else if r.resume().bit() { + Poll::Ready(()) + } else if r.usbwuallowed().bit() { + regs.eventcause.write(|w| w.usbwuallowed().allowed()); - regs.dpdmvalue.write(|w| w.state().resume()); - regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit()); + regs.dpdmvalue.write(|w| w.state().resume()); + regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit()); - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; - errata::post_wakeup(); - } - - Ok(()) + errata::post_wakeup(); } + + Ok(()) } } @@ -594,9 +580,7 @@ impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir &self.info } - type WaitEnabledFuture<'a> = impl Future + 'a where Self: 'a; - - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { + async fn wait_enabled(&mut self) { let i = self.info.addr.index(); assert!(i != 0); @@ -608,6 +592,7 @@ impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir Poll::Pending } }) + .await } } @@ -712,34 +697,26 @@ unsafe fn write_dma(i: usize, buf: &[u8]) { } impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + async fn read(&mut self, buf: &mut [u8]) -> Result { + let i = self.info.addr.index(); + assert!(i != 0); - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - let i = self.info.addr.index(); - assert!(i != 0); + self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; - self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; - - unsafe { read_dma::(i, buf) } - } + unsafe { read_dma::(i, buf) } } } impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + let i = self.info.addr.index(); + assert!(i != 0); - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - let i = self.info.addr.index(); - assert!(i != 0); + self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; - self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; + unsafe { write_dma::(i, buf) } - unsafe { write_dma::(i, buf) } - - Ok(()) - } + Ok(()) } } @@ -749,136 +726,120 @@ pub struct ControlPipe<'d, T: Instance> { } impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { - type SetupFuture<'a> = impl Future + 'a where Self: 'a; - type DataOutFuture<'a> = impl Future> + 'a where Self: 'a; - type DataInFuture<'a> = impl Future> + 'a where Self: 'a; - type AcceptFuture<'a> = impl Future + 'a where Self: 'a; - type RejectFuture<'a> = impl Future + 'a where Self: 'a; - fn max_packet_size(&self) -> usize { usize::from(self.max_packet_size) } - fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> { - async move { + async fn setup(&mut self) -> [u8; 8] { + let regs = T::regs(); + + // Reset shorts + regs.shorts.write(|w| w); + + // Wait for SETUP packet + regs.intenset.write(|w| w.ep0setup().set()); + poll_fn(|cx| { + EP0_WAKER.register(cx.waker()); let regs = T::regs(); + if regs.events_ep0setup.read().bits() != 0 { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; - // Reset shorts - regs.shorts.write(|w| w); + regs.events_ep0setup.reset(); - // Wait for SETUP packet - regs.intenset.write(|w| w.ep0setup().set()); - poll_fn(|cx| { - EP0_WAKER.register(cx.waker()); - let regs = T::regs(); - if regs.events_ep0setup.read().bits() != 0 { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; + let mut buf = [0; 8]; + buf[0] = regs.bmrequesttype.read().bits() as u8; + buf[1] = regs.brequest.read().brequest().bits(); + buf[2] = regs.wvaluel.read().wvaluel().bits(); + buf[3] = regs.wvalueh.read().wvalueh().bits(); + buf[4] = regs.windexl.read().windexl().bits(); + buf[5] = regs.windexh.read().windexh().bits(); + buf[6] = regs.wlengthl.read().wlengthl().bits(); + buf[7] = regs.wlengthh.read().wlengthh().bits(); - regs.events_ep0setup.reset(); - - let mut buf = [0; 8]; - buf[0] = regs.bmrequesttype.read().bits() as u8; - buf[1] = regs.brequest.read().brequest().bits(); - buf[2] = regs.wvaluel.read().wvaluel().bits(); - buf[3] = regs.wvalueh.read().wvalueh().bits(); - buf[4] = regs.windexl.read().windexl().bits(); - buf[5] = regs.windexh.read().windexh().bits(); - buf[6] = regs.wlengthl.read().wlengthl().bits(); - buf[7] = regs.wlengthh.read().wlengthh().bits(); - - buf - } + buf } - fn data_out<'a>(&'a mut self, buf: &'a mut [u8], _first: bool, _last: bool) -> Self::DataOutFuture<'a> { - async move { + async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result { + let regs = T::regs(); + + regs.events_ep0datadone.reset(); + + // This starts a RX on EP0. events_ep0datadone notifies when done. + regs.tasks_ep0rcvout.write(|w| w.tasks_ep0rcvout().set_bit()); + + // Wait until ready + regs.intenset.write(|w| { + w.usbreset().set(); + w.ep0setup().set(); + w.ep0datadone().set() + }); + poll_fn(|cx| { + EP0_WAKER.register(cx.waker()); let regs = T::regs(); + if regs.events_ep0datadone.read().bits() != 0 { + Poll::Ready(Ok(())) + } else if regs.events_usbreset.read().bits() != 0 { + trace!("aborted control data_out: usb reset"); + Poll::Ready(Err(EndpointError::Disabled)) + } else if regs.events_ep0setup.read().bits() != 0 { + trace!("aborted control data_out: received another SETUP"); + Poll::Ready(Err(EndpointError::Disabled)) + } else { + Poll::Pending + } + }) + .await?; - regs.events_ep0datadone.reset(); - - // This starts a RX on EP0. events_ep0datadone notifies when done. - regs.tasks_ep0rcvout.write(|w| w.tasks_ep0rcvout().set_bit()); - - // Wait until ready - regs.intenset.write(|w| { - w.usbreset().set(); - w.ep0setup().set(); - w.ep0datadone().set() - }); - poll_fn(|cx| { - EP0_WAKER.register(cx.waker()); - let regs = T::regs(); - if regs.events_ep0datadone.read().bits() != 0 { - Poll::Ready(Ok(())) - } else if regs.events_usbreset.read().bits() != 0 { - trace!("aborted control data_out: usb reset"); - Poll::Ready(Err(EndpointError::Disabled)) - } else if regs.events_ep0setup.read().bits() != 0 { - trace!("aborted control data_out: received another SETUP"); - Poll::Ready(Err(EndpointError::Disabled)) - } else { - Poll::Pending - } - }) - .await?; - - unsafe { read_dma::(0, buf) } - } + unsafe { read_dma::(0, buf) } } - fn data_in<'a>(&'a mut self, buf: &'a [u8], _first: bool, last: bool) -> Self::DataInFuture<'a> { - async move { + async fn data_in(&mut self, buf: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> { + let regs = T::regs(); + regs.events_ep0datadone.reset(); + + regs.shorts.write(|w| w.ep0datadone_ep0status().bit(last)); + + // This starts a TX on EP0. events_ep0datadone notifies when done. + unsafe { write_dma::(0, buf) } + + regs.intenset.write(|w| { + w.usbreset().set(); + w.ep0setup().set(); + w.ep0datadone().set() + }); + + poll_fn(|cx| { + cx.waker().wake_by_ref(); + EP0_WAKER.register(cx.waker()); let regs = T::regs(); - regs.events_ep0datadone.reset(); - - regs.shorts.write(|w| w.ep0datadone_ep0status().bit(last)); - - // This starts a TX on EP0. events_ep0datadone notifies when done. - unsafe { write_dma::(0, buf) } - - regs.intenset.write(|w| { - w.usbreset().set(); - w.ep0setup().set(); - w.ep0datadone().set() - }); - - poll_fn(|cx| { - cx.waker().wake_by_ref(); - EP0_WAKER.register(cx.waker()); - let regs = T::regs(); - if regs.events_ep0datadone.read().bits() != 0 { - Poll::Ready(Ok(())) - } else if regs.events_usbreset.read().bits() != 0 { - trace!("aborted control data_in: usb reset"); - Poll::Ready(Err(EndpointError::Disabled)) - } else if regs.events_ep0setup.read().bits() != 0 { - trace!("aborted control data_in: received another SETUP"); - Poll::Ready(Err(EndpointError::Disabled)) - } else { - Poll::Pending - } - }) - .await - } + if regs.events_ep0datadone.read().bits() != 0 { + Poll::Ready(Ok(())) + } else if regs.events_usbreset.read().bits() != 0 { + trace!("aborted control data_in: usb reset"); + Poll::Ready(Err(EndpointError::Disabled)) + } else if regs.events_ep0setup.read().bits() != 0 { + trace!("aborted control data_in: received another SETUP"); + Poll::Ready(Err(EndpointError::Disabled)) + } else { + Poll::Pending + } + }) + .await } - fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> { - async move { - let regs = T::regs(); - regs.tasks_ep0status.write(|w| w.tasks_ep0status().bit(true)); - } + async fn accept(&mut self) { + let regs = T::regs(); + regs.tasks_ep0status.write(|w| w.tasks_ep0status().bit(true)); } - fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> { - async move { - let regs = T::regs(); - regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); - } + async fn reject(&mut self) { + let regs = T::regs(); + regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); } } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 04b0c13c..284d458c 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -13,7 +13,7 @@ flavors = [ ] [features] -defmt = ["dep:defmt", "embassy-usb-driver?/defmt"] +defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"] # Reexport the PAC for the currently enabled chip at `embassy_rp::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-rp may major-bump (breaking) the PAC version. @@ -53,13 +53,14 @@ cortex-m = "0.7.6" critical-section = "1.1" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } chrono = { version = "0.4", default-features = false, optional = true } -embedded-io = { version = "0.3.1", features = ["async"], optional = true } +embedded-io = { version = "0.4.0", features = ["async"], optional = true } embedded-storage = { version = "0.3" } +rand_core = "0.6.4" rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs new file mode 100644 index 00000000..025c6f91 --- /dev/null +++ b/embassy-rp/src/adc.rs @@ -0,0 +1,173 @@ +use core::future::poll_fn; +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_common::into_ref; +use embassy_sync::waitqueue::AtomicWaker; +use embedded_hal_02::adc::{Channel, OneShot}; + +use crate::interrupt::{self, InterruptExt}; +use crate::peripherals::ADC; +use crate::{pac, peripherals, Peripheral}; +static WAKER: AtomicWaker = AtomicWaker::new(); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + // No errors for now +} + +#[non_exhaustive] +pub struct Config {} + +impl Default for Config { + fn default() -> Self { + Self {} + } +} +pub struct Adc<'d> { + phantom: PhantomData<&'d ADC>, +} + +impl<'d> Adc<'d> { + #[inline] + fn regs() -> pac::adc::Adc { + pac::ADC + } + + #[inline] + fn reset() -> pac::resets::regs::Peripherals { + let mut ret = pac::resets::regs::Peripherals::default(); + ret.set_adc(true); + ret + } + + pub fn new( + _inner: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + _config: Config, + ) -> Self { + into_ref!(irq); + unsafe { + let reset = Self::reset(); + crate::reset::reset(reset); + crate::reset::unreset_wait(reset); + let r = Self::regs(); + // Enable ADC + r.cs().write(|w| w.set_en(true)); + // Wait for ADC ready + while !r.cs().read().ready() {} + } + + // Setup IRQ + irq.disable(); + irq.set_handler(|_| unsafe { + let r = Self::regs(); + r.inte().write(|w| w.set_fifo(false)); + WAKER.wake(); + }); + irq.unpend(); + irq.enable(); + + Self { phantom: PhantomData } + } + + async fn wait_for_ready() { + let r = Self::regs(); + unsafe { + r.inte().write(|w| w.set_fifo(true)); + compiler_fence(Ordering::SeqCst); + poll_fn(|cx| { + WAKER.register(cx.waker()); + if r.cs().read().ready() { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + } + } + + pub async fn read, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { + let r = Self::regs(); + unsafe { + r.cs().modify(|w| { + w.set_ainsel(PIN::channel()); + w.set_start_once(true) + }); + Self::wait_for_ready().await; + r.result().read().result().into() + } + } + + pub async fn read_temperature(&mut self) -> u16 { + let r = Self::regs(); + unsafe { + r.cs().modify(|w| w.set_ts_en(true)); + if !r.cs().read().ready() { + Self::wait_for_ready().await; + } + r.cs().modify(|w| { + w.set_ainsel(4); + w.set_start_once(true) + }); + Self::wait_for_ready().await; + r.result().read().result().into() + } + } + + pub fn blocking_read, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { + let r = Self::regs(); + unsafe { + r.cs().modify(|w| { + w.set_ainsel(PIN::channel()); + w.set_start_once(true) + }); + while !r.cs().read().ready() {} + r.result().read().result().into() + } + } + + pub fn blocking_read_temperature(&mut self) -> u16 { + let r = Self::regs(); + unsafe { + r.cs().modify(|w| w.set_ts_en(true)); + while !r.cs().read().ready() {} + r.cs().modify(|w| { + w.set_ainsel(4); + w.set_start_once(true) + }); + while !r.cs().read().ready() {} + r.result().read().result().into() + } + } +} + +macro_rules! impl_pin { + ($pin:ident, $channel:expr) => { + impl Channel> for peripherals::$pin { + type ID = u8; + fn channel() -> u8 { + $channel + } + } + }; +} + +impl_pin!(PIN_26, 0); +impl_pin!(PIN_27, 1); +impl_pin!(PIN_28, 2); +impl_pin!(PIN_29, 3); + +impl OneShot, WORD, PIN> for Adc<'static> +where + WORD: From, + PIN: Channel, ID = u8>, +{ + type Error = (); + fn read(&mut self, pin: &mut PIN) -> nb::Result { + Ok(self.blocking_read(pin).into()) + } +} diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 1c446f38..85c9bbb7 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -5,7 +5,7 @@ use crate::{pac, reset}; const XOSC_MHZ: u32 = 12; /// safety: must be called exactly once at bootup -pub unsafe fn init() { +pub(crate) unsafe fn init() { // Reset everything except: // - QSPI (we're using it to run this code!) // - PLLs (it may be suicide if that's what's clocking us) @@ -196,3 +196,40 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1: // Turn on post divider p.pwr().modify(|w| w.set_postdivpd(false)); } + +/// Random number generator based on the ROSC RANDOMBIT register. +/// +/// This will not produce random values if the ROSC is stopped or run at some +/// harmonic of the bus frequency. With default clock settings these are not +/// issues. +pub struct RoscRng; + +impl RoscRng { + fn next_u8() -> u8 { + let random_reg = pac::ROSC.randombit(); + let mut acc = 0; + for _ in 0..u8::BITS { + acc <<= 1; + acc |= unsafe { random_reg.read().randombit() as u8 }; + } + acc + } +} + +impl rand_core::RngCore for RoscRng { + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + Ok(self.fill_bytes(dest)) + } + + fn next_u32(&mut self) -> u32 { + rand_core::impls::next_u32_via_fill(self) + } + + fn next_u64(&mut self) -> u64 { + rand_core::impls::next_u64_via_fill(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + dest.fill_with(Self::next_u8) + } +} diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index d09cc62f..a972d5f6 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -59,6 +59,11 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { } pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + trace!( + "Reading from 0x{:x} to 0x{:x}", + FLASH_BASE + offset as usize, + FLASH_BASE + offset as usize + bytes.len() + ); check_read(self, offset, bytes.len())?; let flash_data = unsafe { core::slice::from_raw_parts((FLASH_BASE as u32 + offset) as *const u8, bytes.len()) }; diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index f79f592b..930de206 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -411,6 +411,16 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { pub fn toggle(&mut self) { self.pin.toggle_set_as_output() } + + #[inline] + pub fn is_high(&self) -> bool { + self.pin.is_high() + } + + #[inline] + pub fn is_low(&self) -> bool { + self.pin.is_low() + } } /// GPIO flexible pin. @@ -791,6 +801,18 @@ mod eh02 { } } + impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for OutputOpenDrain<'d, T> { + type Error = Infallible; + + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + fn is_low(&self) -> Result { + Ok(self.is_low()) + } + } + impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for OutputOpenDrain<'d, T> { type Error = Infallible; @@ -870,9 +892,6 @@ mod eh02 { mod eh1 { use core::convert::Infallible; - #[cfg(feature = "nightly")] - use futures::FutureExt; - use super::*; impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Input<'d, T> { @@ -949,6 +968,16 @@ mod eh1 { } } + impl<'d, T: Pin> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d, T> { + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + fn is_low(&self) -> Result { + Ok(self.is_low()) + } + } + impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Flex<'d, T> { type Error = Infallible; } @@ -991,57 +1020,57 @@ mod eh1 { #[cfg(feature = "nightly")] impl<'d, T: Pin> embedded_hal_async::digital::Wait for Flex<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) } } #[cfg(feature = "nightly")] impl<'d, T: Pin> embedded_hal_async::digital::Wait for Input<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) } } } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index d6742f6a..e48e16d8 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -717,8 +717,6 @@ mod eh1 { } #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod nightly { - use core::future::Future; - use embedded_hal_1::i2c::Operation; use embedded_hal_async::i2c::AddressMode; @@ -729,74 +727,55 @@ mod nightly { A: AddressMode + Into + 'static, T: Instance + 'd, { - type ReadFuture<'a> = impl Future> + 'a - where Self: 'a; - type WriteFuture<'a> = impl Future> + 'a - where Self: 'a; - type WriteReadFuture<'a> = impl Future> + 'a - where Self: 'a; - type TransactionFuture<'a, 'b> = impl Future> + 'a - where Self: 'a, 'b: 'a; - - fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + async fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Result<(), Self::Error> { let addr: u16 = address.into(); - async move { - Self::setup(addr)?; - self.read_async_internal(buffer, false, true).await - } + Self::setup(addr)?; + self.read_async_internal(read, false, true).await } - fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> { + async fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Result<(), Self::Error> { let addr: u16 = address.into(); - async move { - Self::setup(addr)?; - self.write_async_internal(write.iter().copied(), true).await - } + Self::setup(addr)?; + self.write_async_internal(write.iter().copied(), true).await } - - fn write_read<'a>( + async fn write_read<'a>( &'a mut self, address: A, - bytes: &'a [u8], - buffer: &'a mut [u8], - ) -> Self::WriteReadFuture<'a> { + write: &'a [u8], + read: &'a mut [u8], + ) -> Result<(), Self::Error> { let addr: u16 = address.into(); - async move { - Self::setup(addr)?; - self.write_async_internal(bytes.iter().cloned(), false).await?; - self.read_async_internal(buffer, false, true).await - } + Self::setup(addr)?; + self.write_async_internal(write.iter().cloned(), false).await?; + self.read_async_internal(read, false, true).await } - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: A, operations: &'a mut [Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + ) -> Result<(), Self::Error> { let addr: u16 = address.into(); - async move { - let mut iterator = operations.iter_mut(); + let mut iterator = operations.iter_mut(); - while let Some(op) = iterator.next() { - let last = iterator.len() == 0; + while let Some(op) = iterator.next() { + let last = iterator.len() == 0; - match op { - Operation::Read(buffer) => { - Self::setup(addr)?; - self.read_async_internal(buffer, false, last).await?; - } - Operation::Write(buffer) => { - Self::setup(addr)?; - self.write_async_internal(buffer.into_iter().cloned(), last).await?; - } + match op { + Operation::Read(buffer) => { + Self::setup(addr)?; + self.read_async_internal(buffer, false, last).await?; + } + Operation::Write(buffer) => { + Self::setup(addr)?; + self.write_async_internal(buffer.into_iter().cloned(), last).await?; } } - Ok(()) } + Ok(()) } } } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index f608f176..d21b5f7b 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -1,11 +1,13 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; mod intrinsics; +pub mod adc; pub mod dma; pub mod gpio; pub mod i2c; @@ -19,7 +21,7 @@ pub mod uart; #[cfg(feature = "nightly")] pub mod usb; -mod clocks; +pub mod clocks; pub mod flash; mod reset; @@ -98,6 +100,8 @@ embassy_hal_common::peripherals! { RTC, FLASH, + + ADC, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index e4b6f0b1..c173909c 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -164,7 +164,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { } } -/// Errors that can occur on methods on [RtcClock] +/// Errors that can occur on methods on [RealTimeClock] #[derive(Clone, Debug, PartialEq, Eq)] pub enum RtcError { /// An invalid DateTime was given or stored on the hardware. diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 754e2dd3..2b7a818d 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -554,45 +554,33 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use core::future::Future; - use super::*; impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Async> { - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async { Ok(()) } + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite for Spi<'d, T, Async> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(data) + async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.write(words).await } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead for Spi<'d, T, Async> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(data) + async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.read(words).await } } impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spi<'d, T, Async> { - type TransferFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> { - self.transfer(rx, tx) + async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + self.transfer(read, write).await } - type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> { - self.transfer_in_place(words) + async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> { + self.transfer_in_place(words).await } } } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 4f0a5553..32029f81 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,337 +1,421 @@ use core::future::{poll_fn, Future}; -use core::task::{Poll, Waker}; +use core::slice; +use core::task::Poll; -use atomic_polyfill::{compiler_fence, Ordering}; -use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; -use embassy_hal_common::ring_buffer::RingBuffer; -use embassy_sync::waitqueue::WakerRegistration; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_hal_common::atomic_ring_buffer::RingBuffer; +use embassy_sync::waitqueue::AtomicWaker; use super::*; -pub struct State<'d, T: Instance>(StateStorage>); -impl<'d, T: Instance> State<'d, T> { +pub struct State { + tx_waker: AtomicWaker, + tx_buf: RingBuffer, + rx_waker: AtomicWaker, + rx_buf: RingBuffer, +} + +impl State { pub const fn new() -> Self { - Self(StateStorage::new()) + Self { + rx_buf: RingBuffer::new(), + tx_buf: RingBuffer::new(), + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + } } } -pub struct RxState<'d, T: Instance>(StateStorage>); -impl<'d, T: Instance> RxState<'d, T> { - pub const fn new() -> Self { - Self(StateStorage::new()) - } -} - -pub struct TxState<'d, T: Instance>(StateStorage>); -impl<'d, T: Instance> TxState<'d, T> { - pub const fn new() -> Self { - Self(StateStorage::new()) - } -} - -struct RxStateInner<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - - waker: WakerRegistration, - buf: RingBuffer<'d>, -} - -struct TxStateInner<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - - waker: WakerRegistration, - buf: RingBuffer<'d>, -} - -struct FullStateInner<'d, T: Instance> { - rx: RxStateInner<'d, T>, - tx: TxStateInner<'d, T>, -} - -unsafe impl<'d, T: Instance> Send for RxStateInner<'d, T> {} -unsafe impl<'d, T: Instance> Sync for RxStateInner<'d, T> {} - -unsafe impl<'d, T: Instance> Send for TxStateInner<'d, T> {} -unsafe impl<'d, T: Instance> Sync for TxStateInner<'d, T> {} - -unsafe impl<'d, T: Instance> Send for FullStateInner<'d, T> {} -unsafe impl<'d, T: Instance> Sync for FullStateInner<'d, T> {} - pub struct BufferedUart<'d, T: Instance> { - inner: PeripheralMutex<'d, FullStateInner<'d, T>>, + phantom: PhantomData<&'d mut T>, } pub struct BufferedUartRx<'d, T: Instance> { - inner: PeripheralMutex<'d, RxStateInner<'d, T>>, + phantom: PhantomData<&'d mut T>, } pub struct BufferedUartTx<'d, T: Instance> { - inner: PeripheralMutex<'d, TxStateInner<'d, T>>, + phantom: PhantomData<&'d mut T>, } -impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} -impl<'d, T: Instance> Unpin for BufferedUartRx<'d, T> {} -impl<'d, T: Instance> Unpin for BufferedUartTx<'d, T> {} - impl<'d, T: Instance> BufferedUart<'d, T> { - pub fn new( - state: &'d mut State<'d, T>, - _uart: Uart<'d, T, M>, + pub fn new( + _uart: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + rx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], - ) -> BufferedUart<'d, T> { - into_ref!(irq); + config: Config, + ) -> Self { + into_ref!(tx, rx); + Self::new_inner( + irq, + tx.map_into(), + rx.map_into(), + None, + None, + tx_buffer, + rx_buffer, + config, + ) + } + + pub fn new_with_rtscts( + _uart: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(tx, rx, cts, rts); + Self::new_inner( + irq, + tx.map_into(), + rx.map_into(), + Some(rts.map_into()), + Some(cts.map_into()), + tx_buffer, + rx_buffer, + config, + ) + } + + fn new_inner( + irq: impl Peripheral

+ 'd, + mut tx: PeripheralRef<'d, AnyPin>, + mut rx: PeripheralRef<'d, AnyPin>, + mut rts: Option>, + mut cts: Option>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(irq); + super::Uart::<'d, T, Async>::init( + Some(tx.reborrow()), + Some(rx.reborrow()), + rts.as_mut().map(|x| x.reborrow()), + cts.as_mut().map(|x| x.reborrow()), + config, + ); + + let state = T::state(); + let regs = T::regs(); + + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - let r = T::regs(); unsafe { - r.uartimsc().modify(|w| { + regs.uartimsc().modify(|w| { w.set_rxim(true); w.set_rtim(true); w.set_txim(true); }); } - Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || FullStateInner { - tx: TxStateInner { - phantom: PhantomData, - waker: WakerRegistration::new(), - buf: RingBuffer::new(tx_buffer), - }, - rx: RxStateInner { - phantom: PhantomData, - waker: WakerRegistration::new(), - buf: RingBuffer::new(rx_buffer), - }, - }), - } + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); + + Self { phantom: PhantomData } } } impl<'d, T: Instance> BufferedUartRx<'d, T> { - pub fn new( - state: &'d mut RxState<'d, T>, - _uart: UartRx<'d, T, M>, + pub fn new( + _uart: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, rx_buffer: &'d mut [u8], - ) -> BufferedUartRx<'d, T> { - into_ref!(irq); + config: Config, + ) -> Self { + into_ref!(rx); + Self::new_inner(irq, rx.map_into(), None, rx_buffer, config) + } + + pub fn new_with_rts( + _uart: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + rx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(rx, rts); + Self::new_inner(irq, rx.map_into(), Some(rts.map_into()), rx_buffer, config) + } + + fn new_inner( + irq: impl Peripheral

+ 'd, + mut rx: PeripheralRef<'d, AnyPin>, + mut rts: Option>, + rx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(irq); + super::Uart::<'d, T, Async>::init( + None, + Some(rx.reborrow()), + rts.as_mut().map(|x| x.reborrow()), + None, + config, + ); + + let state = T::state(); + let regs = T::regs(); + + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - let r = T::regs(); unsafe { - r.uartimsc().modify(|w| { + regs.uartimsc().modify(|w| { w.set_rxim(true); w.set_rtim(true); }); } - Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || RxStateInner { - phantom: PhantomData, + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); - buf: RingBuffer::new(rx_buffer), - waker: WakerRegistration::new(), - }), - } + Self { phantom: PhantomData } + } + + fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a { + poll_fn(move |cx| { + let state = T::state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let n = rx_reader.pop(|data| { + let n = data.len().min(buf.len()); + buf[..n].copy_from_slice(&data[..n]); + n + }); + if n == 0 { + state.rx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(n)) + }) + } + + fn fill_buf<'a>() -> impl Future> { + poll_fn(move |cx| { + let state = T::state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let (p, n) = rx_reader.pop_buf(); + if n == 0 { + state.rx_waker.register(cx.waker()); + return Poll::Pending; + } + + let buf = unsafe { slice::from_raw_parts(p, n) }; + Poll::Ready(Ok(buf)) + }) + } + + fn consume(amt: usize) { + let state = T::state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + rx_reader.pop_done(amt) } } impl<'d, T: Instance> BufferedUartTx<'d, T> { - pub fn new( - state: &'d mut TxState<'d, T>, - _uart: UartTx<'d, T, M>, + pub fn new( + _uart: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], - ) -> BufferedUartTx<'d, T> { - into_ref!(irq); + config: Config, + ) -> Self { + into_ref!(tx); + Self::new_inner(irq, tx.map_into(), None, tx_buffer, config) + } + + pub fn new_with_cts( + _uart: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(tx, cts); + Self::new_inner(irq, tx.map_into(), Some(cts.map_into()), tx_buffer, config) + } + + fn new_inner( + irq: impl Peripheral

+ 'd, + mut tx: PeripheralRef<'d, AnyPin>, + mut cts: Option>, + tx_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(irq); + super::Uart::<'d, T, Async>::init( + Some(tx.reborrow()), + None, + None, + cts.as_mut().map(|x| x.reborrow()), + config, + ); + + let state = T::state(); + let regs = T::regs(); + + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - let r = T::regs(); unsafe { - r.uartimsc().modify(|w| { + regs.uartimsc().modify(|w| { w.set_txim(true); }); } - Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || TxStateInner { - phantom: PhantomData, + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); - buf: RingBuffer::new(tx_buffer), - waker: WakerRegistration::new(), - }), - } - } -} - -impl<'d, T: Instance> PeripheralState for FullStateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - self.rx.on_interrupt(); - self.tx.on_interrupt(); - } -} - -impl<'d, T: Instance> RxStateInner<'d, T> -where - Self: 'd, -{ - fn read(&mut self, buf: &mut [u8], waker: &Waker) -> (Poll>, bool) { - // We have data ready in buffer? Return it. - let mut do_pend = false; - let data = self.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if self.buf.is_full() { - do_pend = true; - } - self.buf.pop(len); - - return (Poll::Ready(Ok(len)), do_pend); - } - - self.waker.register(waker); - (Poll::Pending, do_pend) + Self { phantom: PhantomData } } - fn fill_buf<'a>(&mut self, waker: &Waker) -> Poll> { - // We have data ready in buffer? Return it. - let buf = self.buf.pop_buf(); - if !buf.is_empty() { - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - self.waker.register(waker); - Poll::Pending - } - - fn consume(&mut self, amt: usize) -> bool { - let full = self.buf.is_full(); - self.buf.pop(amt); - full - } -} - -impl<'d, T: Instance> PeripheralState for RxStateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - let r = T::regs(); - unsafe { - let ris = r.uartris().read(); - // Clear interrupt flags - r.uarticr().modify(|w| { - w.set_rxic(true); - w.set_rtic(true); + fn write<'a>(buf: &'a [u8]) -> impl Future> + 'a { + poll_fn(move |cx| { + let state = T::state(); + let mut tx_writer = unsafe { state.tx_buf.writer() }; + let n = tx_writer.push(|data| { + let n = data.len().min(buf.len()); + data[..n].copy_from_slice(&buf[..n]); + n }); - - if ris.peris() { - warn!("Parity error"); - r.uarticr().modify(|w| { - w.set_peic(true); - }); - } - if ris.feris() { - warn!("Framing error"); - r.uarticr().modify(|w| { - w.set_feic(true); - }); - } - if ris.beris() { - warn!("Break error"); - r.uarticr().modify(|w| { - w.set_beic(true); - }); - } - if ris.oeris() { - warn!("Overrun error"); - r.uarticr().modify(|w| { - w.set_oeic(true); - }); - } - - if !r.uartfr().read().rxfe() { - let buf = self.buf.push_buf(); - if !buf.is_empty() { - buf[0] = r.uartdr().read().data(); - self.buf.push(1); - } else { - warn!("RX buffer full, discard received byte"); - } - - if self.buf.is_full() { - self.waker.wake(); - } - } - - if ris.rtris() { - self.waker.wake(); - }; - } - } -} - -impl<'d, T: Instance> TxStateInner<'d, T> -where - Self: 'd, -{ - fn write(&mut self, buf: &[u8], waker: &Waker) -> (Poll>, bool) { - let empty = self.buf.is_empty(); - let tx_buf = self.buf.push_buf(); - if tx_buf.is_empty() { - self.waker.register(waker); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - self.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - } - - fn flush(&mut self, waker: &Waker) -> Poll> { - if !self.buf.is_empty() { - self.waker.register(waker); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - } -} - -impl<'d, T: Instance> PeripheralState for TxStateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - let r = T::regs(); - unsafe { - let buf = self.buf.pop_buf(); - if !buf.is_empty() { - r.uartimsc().modify(|w| { - w.set_txim(true); - }); - r.uartdr().write(|w| w.set_data(buf[0].into())); - self.buf.pop(1); - self.waker.wake(); + if n == 0 { + state.tx_waker.register(cx.waker()); + return Poll::Pending; } else { - // Disable interrupt until we have something to transmit again - r.uartimsc().modify(|w| { - w.set_txim(false); - }); + unsafe { T::Interrupt::steal() }.pend(); } + + Poll::Ready(Ok(n)) + }) + } + + fn flush() -> impl Future> { + poll_fn(move |cx| { + let state = T::state(); + if !state.tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + } +} + +impl<'d, T: Instance> Drop for BufferedUart<'d, T> { + fn drop(&mut self) { + unsafe { + T::Interrupt::steal().disable(); + let state = T::state(); + state.tx_buf.deinit(); + state.rx_buf.deinit(); + } + } +} + +impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { + fn drop(&mut self) { + unsafe { + T::Interrupt::steal().disable(); + let state = T::state(); + state.tx_buf.deinit(); + state.rx_buf.deinit(); + } + } +} + +impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { + fn drop(&mut self) { + unsafe { + T::Interrupt::steal().disable(); + let state = T::state(); + state.tx_buf.deinit(); + state.rx_buf.deinit(); + } + } +} + +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + trace!("on_interrupt"); + + let r = T::regs(); + let s = T::state(); + + unsafe { + // RX + + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rxic(true); + w.set_rtic(true); + }); + + if ris.peris() { + warn!("Parity error"); + r.uarticr().write(|w| { + w.set_peic(true); + }); + } + if ris.feris() { + warn!("Framing error"); + r.uarticr().write(|w| { + w.set_feic(true); + }); + } + if ris.beris() { + warn!("Break error"); + r.uarticr().write(|w| { + w.set_beic(true); + }); + } + if ris.oeris() { + warn!("Overrun error"); + r.uarticr().write(|w| { + w.set_oeic(true); + }); + } + + let mut rx_writer = s.rx_buf.writer(); + if !r.uartfr().read().rxfe() { + let val = r.uartdr().read().data(); + if !rx_writer.push_one(val) { + warn!("RX buffer full, discard received byte"); + } + s.rx_waker.wake(); + } + + // TX + let mut tx_reader = s.tx_buf.reader(); + if let Some(val) = tx_reader.pop_one() { + r.uartimsc().modify(|w| { + w.set_txim(true); + }); + r.uartdr().write(|w| w.set_data(val)); + s.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.uartimsc().modify(|w| { + w.set_txim(false); + }); } } } @@ -355,135 +439,53 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - poll_fn(move |cx| { - let (res, do_pend) = self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - state.rx.read(buf, cx.waker()) - }); - - if do_pend { - self.inner.pend(); - } - - res - }) + async fn read(&mut self, buf: &mut [u8]) -> Result { + BufferedUartRx::<'d, T>::read(buf).await } } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - poll_fn(move |cx| { - let (res, do_pend) = self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - state.read(buf, cx.waker()) - }); - - if do_pend { - self.inner.pend(); - } - - res - }) + async fn read(&mut self, buf: &mut [u8]) -> Result { + Self::read(buf).await } } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - state.rx.fill_buf(cx.waker()) - }) - }) + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + BufferedUartRx::<'d, T>::fill_buf().await } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| state.rx.consume(amt)); - if signal { - self.inner.pend(); - } + BufferedUartRx::<'d, T>::consume(amt) } } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - state.fill_buf(cx.waker()) - }) - }) + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + Self::fill_buf().await } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| state.consume(amt)); - if signal { - self.inner.pend(); - } + Self::consume(amt) } } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker())); - if empty { - self.inner.pend(); - } - poll - }) + async fn write(&mut self, buf: &[u8]) -> Result { + BufferedUartTx::<'d, T>::write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))) + async fn flush(&mut self) -> Result<(), Self::Error> { + BufferedUartTx::<'d, T>::flush().await } } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker())); - if empty { - self.inner.pend(); - } - poll - }) + async fn write(&mut self, buf: &[u8]) -> Result { + Self::write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))) + async fn flush(&mut self) -> Result<(), Self::Error> { + Self::flush().await } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 56c25e18..7e7bcaf3 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -7,6 +7,11 @@ use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; +#[cfg(feature = "nightly")] +mod buffered; +#[cfg(feature = "nightly")] +pub use buffered::{BufferedUart, BufferedUartRx, BufferedUartTx}; + #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum DataBits { DataBits5, @@ -196,7 +201,7 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { config: Config, ) -> Self { into_ref!(tx, rx); - Self::new_inner(uart, rx.map_into(), tx.map_into(), None, None, None, None, config) + Self::new_inner(uart, tx.map_into(), rx.map_into(), None, None, None, None, config) } /// Create a new UART with hardware flow control (RTS/CTS) @@ -211,8 +216,8 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { into_ref!(tx, rx, cts, rts); Self::new_inner( uart, - rx.map_into(), tx.map_into(), + rx.map_into(), Some(rts.map_into()), Some(cts.map_into()), None, @@ -235,8 +240,8 @@ impl<'d, T: Instance> Uart<'d, T, Async> { into_ref!(tx, rx, tx_dma, rx_dma); Self::new_inner( uart, - rx.map_into(), tx.map_into(), + rx.map_into(), None, None, Some(tx_dma.map_into()), @@ -259,8 +264,8 @@ impl<'d, T: Instance> Uart<'d, T, Async> { into_ref!(tx, rx, cts, rts, tx_dma, rx_dma); Self::new_inner( uart, - rx.map_into(), tx.map_into(), + rx.map_into(), Some(rts.map_into()), Some(cts.map_into()), Some(tx_dma.map_into()), @@ -273,41 +278,52 @@ impl<'d, T: Instance> Uart<'d, T, Async> { impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { fn new_inner( _uart: impl Peripheral

+ 'd, - tx: PeripheralRef<'d, AnyPin>, - rx: PeripheralRef<'d, AnyPin>, - rts: Option>, - cts: Option>, + mut tx: PeripheralRef<'d, AnyPin>, + mut rx: PeripheralRef<'d, AnyPin>, + mut rts: Option>, + mut cts: Option>, tx_dma: Option>, rx_dma: Option>, config: Config, ) -> Self { - into_ref!(_uart); + Self::init( + Some(tx.reborrow()), + Some(rx.reborrow()), + rts.as_mut().map(|x| x.reborrow()), + cts.as_mut().map(|x| x.reborrow()), + config, + ); + Self { + tx: UartTx::new(tx_dma), + rx: UartRx::new(rx_dma), + } + } + + fn init( + tx: Option>, + rx: Option>, + rts: Option>, + cts: Option>, + config: Config, + ) { + let r = T::regs(); unsafe { - let r = T::regs(); - - tx.io().ctrl().write(|w| w.set_funcsel(2)); - rx.io().ctrl().write(|w| w.set_funcsel(2)); - - tx.pad_ctrl().write(|w| { - w.set_ie(true); - }); - - rx.pad_ctrl().write(|w| { - w.set_ie(true); - }); - + if let Some(pin) = &tx { + pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.pad_ctrl().write(|w| w.set_ie(true)); + } + if let Some(pin) = &rx { + pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.pad_ctrl().write(|w| w.set_ie(true)); + } if let Some(pin) = &cts { pin.io().ctrl().write(|w| w.set_funcsel(2)); - pin.pad_ctrl().write(|w| { - w.set_ie(true); - }); + pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rts { pin.io().ctrl().write(|w| w.set_funcsel(2)); - pin.pad_ctrl().write(|w| { - w.set_ie(true); - }); + pin.pad_ctrl().write(|w| w.set_ie(true)); } let clk_base = crate::clocks::clk_peri_freq(); @@ -359,11 +375,6 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { w.set_rtsen(rts.is_some()); }); } - - Self { - tx: UartTx::new(tx_dma), - rx: UartRx::new(rx_dma), - } } } @@ -611,11 +622,6 @@ mod eha { } } -#[cfg(feature = "nightly")] -mod buffered; -#[cfg(feature = "nightly")] -pub use buffered::*; - mod sealed { use super::*; @@ -628,6 +634,9 @@ mod sealed { type Interrupt: crate::interrupt::Interrupt; fn regs() -> pac::uart::Uart; + + #[cfg(feature = "nightly")] + fn state() -> &'static buffered::State; } pub trait TxPin {} pub trait RxPin {} @@ -663,6 +672,12 @@ macro_rules! impl_instance { fn regs() -> pac::uart::Uart { pac::$inst } + + #[cfg(feature = "nightly")] + fn state() -> &'static buffered::State { + static STATE: buffered::State = buffered::State::new(); + &STATE + } } impl Instance for peripherals::$inst {} }; diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 6dc90b98..dfc2e9da 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -1,4 +1,4 @@ -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::marker::PhantomData; use core::slice; use core::sync::atomic::Ordering; @@ -352,9 +352,7 @@ pub struct Bus<'d, T: Instance> { } impl<'d, T: Instance> driver::Bus for Bus<'d, T> { - type PollFuture<'a> = impl Future + 'a where Self: 'a; - - fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> { + async fn poll(&mut self) -> Event { poll_fn(move |cx| unsafe { BUS_WAKER.register(cx.waker()); @@ -406,6 +404,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { }); Poll::Pending }) + .await } #[inline] @@ -456,22 +455,12 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } } - type EnableFuture<'a> = impl Future + 'a where Self: 'a; + async fn enable(&mut self) {} - fn enable(&mut self) -> Self::EnableFuture<'_> { - async move {} - } + async fn disable(&mut self) {} - type DisableFuture<'a> = impl Future + 'a where Self: 'a; - - fn disable(&mut self) -> Self::DisableFuture<'_> { - async move {} - } - - type RemoteWakeupFuture<'a> = impl Future> + 'a where Self: 'a; - - fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> { - async move { Err(Unsupported) } + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + Err(Unsupported) } } @@ -515,24 +504,20 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { &self.info } - type WaitEnabledFuture<'a> = impl Future + 'a where Self: 'a; - - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { - async move { - trace!("wait_enabled IN WAITING"); - let index = self.info.addr.index(); - poll_fn(|cx| { - EP_IN_WAKERS[index].register(cx.waker()); - let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() }; - if val.enable() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - trace!("wait_enabled IN OK"); - } + async fn wait_enabled(&mut self) { + trace!("wait_enabled IN WAITING"); + let index = self.info.addr.index(); + poll_fn(|cx| { + EP_IN_WAKERS[index].register(cx.waker()); + let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() }; + if val.enable() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + trace!("wait_enabled IN OK"); } } @@ -541,117 +526,105 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> { &self.info } - type WaitEnabledFuture<'a> = impl Future + 'a where Self: 'a; - - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { - async move { - trace!("wait_enabled OUT WAITING"); - let index = self.info.addr.index(); - poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); - let val = unsafe { T::dpram().ep_out_control(self.info.addr.index() - 1).read() }; - if val.enable() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - trace!("wait_enabled OUT OK"); - } + async fn wait_enabled(&mut self) { + trace!("wait_enabled OUT WAITING"); + let index = self.info.addr.index(); + poll_fn(|cx| { + EP_OUT_WAKERS[index].register(cx.waker()); + let val = unsafe { T::dpram().ep_out_control(self.info.addr.index() - 1).read() }; + if val.enable() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + trace!("wait_enabled OUT OK"); } } impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - trace!("READ WAITING, buf.len() = {}", buf.len()); - let index = self.info.addr.index(); - let val = poll_fn(|cx| unsafe { - EP_OUT_WAKERS[index].register(cx.waker()); - let val = T::dpram().ep_out_buffer_control(index).read(); - if val.available(0) { - Poll::Pending - } else { - Poll::Ready(val) - } - }) - .await; - - let rx_len = val.length(0) as usize; - if rx_len > buf.len() { - return Err(EndpointError::BufferOverflow); + async fn read(&mut self, buf: &mut [u8]) -> Result { + trace!("READ WAITING, buf.len() = {}", buf.len()); + let index = self.info.addr.index(); + let val = poll_fn(|cx| unsafe { + EP_OUT_WAKERS[index].register(cx.waker()); + let val = T::dpram().ep_out_buffer_control(index).read(); + if val.available(0) { + Poll::Pending + } else { + Poll::Ready(val) } - self.buf.read(&mut buf[..rx_len]); + }) + .await; - trace!("READ OK, rx_len = {}", rx_len); - - unsafe { - let pid = !val.pid(0); - T::dpram().ep_out_buffer_control(index).write(|w| { - w.set_pid(0, pid); - w.set_length(0, self.info.max_packet_size); - }); - cortex_m::asm::delay(12); - T::dpram().ep_out_buffer_control(index).write(|w| { - w.set_pid(0, pid); - w.set_length(0, self.info.max_packet_size); - w.set_available(0, true); - }); - } - - Ok(rx_len) + let rx_len = val.length(0) as usize; + if rx_len > buf.len() { + return Err(EndpointError::BufferOverflow); } + self.buf.read(&mut buf[..rx_len]); + + trace!("READ OK, rx_len = {}", rx_len); + + unsafe { + let pid = !val.pid(0); + T::dpram().ep_out_buffer_control(index).write(|w| { + w.set_pid(0, pid); + w.set_length(0, self.info.max_packet_size); + }); + cortex_m::asm::delay(12); + T::dpram().ep_out_buffer_control(index).write(|w| { + w.set_pid(0, pid); + w.set_length(0, self.info.max_packet_size); + w.set_available(0, true); + }); + } + + Ok(rx_len) } } impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - if buf.len() > self.info.max_packet_size as usize { - return Err(EndpointError::BufferOverflow); - } - - trace!("WRITE WAITING"); - - let index = self.info.addr.index(); - let val = poll_fn(|cx| unsafe { - EP_IN_WAKERS[index].register(cx.waker()); - let val = T::dpram().ep_in_buffer_control(index).read(); - if val.available(0) { - Poll::Pending - } else { - Poll::Ready(val) - } - }) - .await; - - self.buf.write(buf); - - unsafe { - let pid = !val.pid(0); - T::dpram().ep_in_buffer_control(index).write(|w| { - w.set_pid(0, pid); - w.set_length(0, buf.len() as _); - w.set_full(0, true); - }); - cortex_m::asm::delay(12); - T::dpram().ep_in_buffer_control(index).write(|w| { - w.set_pid(0, pid); - w.set_length(0, buf.len() as _); - w.set_full(0, true); - w.set_available(0, true); - }); - } - - trace!("WRITE OK"); - - Ok(()) + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + if buf.len() > self.info.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); } + + trace!("WRITE WAITING"); + + let index = self.info.addr.index(); + let val = poll_fn(|cx| unsafe { + EP_IN_WAKERS[index].register(cx.waker()); + let val = T::dpram().ep_in_buffer_control(index).read(); + if val.available(0) { + Poll::Pending + } else { + Poll::Ready(val) + } + }) + .await; + + self.buf.write(buf); + + unsafe { + let pid = !val.pid(0); + T::dpram().ep_in_buffer_control(index).write(|w| { + w.set_pid(0, pid); + w.set_length(0, buf.len() as _); + w.set_full(0, true); + }); + cortex_m::asm::delay(12); + T::dpram().ep_in_buffer_control(index).write(|w| { + w.set_pid(0, pid); + w.set_length(0, buf.len() as _); + w.set_full(0, true); + w.set_available(0, true); + }); + } + + trace!("WRITE OK"); + + Ok(()) } } @@ -661,199 +634,183 @@ pub struct ControlPipe<'d, T: Instance> { } impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { - type SetupFuture<'a> = impl Future + 'a where Self: 'a; - type DataOutFuture<'a> = impl Future> + 'a where Self: 'a; - type DataInFuture<'a> = impl Future> + 'a where Self: 'a; - type AcceptFuture<'a> = impl Future + 'a where Self: 'a; - type RejectFuture<'a> = impl Future + 'a where Self: 'a; - fn max_packet_size(&self) -> usize { 64 } - fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> { - async move { - loop { - trace!("SETUP read waiting"); - let regs = T::regs(); - unsafe { regs.inte().write_set(|w| w.set_setup_req(true)) }; - - poll_fn(|cx| unsafe { - EP_OUT_WAKERS[0].register(cx.waker()); - let regs = T::regs(); - if regs.sie_status().read().setup_rec() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - let mut buf = [0; 8]; - EndpointBuffer::::new(0, 8).read(&mut buf); - - let regs = T::regs(); - unsafe { - regs.sie_status().write(|w| w.set_setup_rec(true)); - - // set PID to 0, so (after toggling) first DATA is PID 1 - T::dpram().ep_in_buffer_control(0).write(|w| w.set_pid(0, false)); - T::dpram().ep_out_buffer_control(0).write(|w| w.set_pid(0, false)); - } - - trace!("SETUP read ok"); - return buf; - } - } - } - - fn data_out<'a>(&'a mut self, buf: &'a mut [u8], _first: bool, _last: bool) -> Self::DataOutFuture<'a> { - async move { - unsafe { - let bufcontrol = T::dpram().ep_out_buffer_control(0); - let pid = !bufcontrol.read().pid(0); - bufcontrol.write(|w| { - w.set_length(0, self.max_packet_size); - w.set_pid(0, pid); - }); - cortex_m::asm::delay(12); - bufcontrol.write(|w| { - w.set_length(0, self.max_packet_size); - w.set_pid(0, pid); - w.set_available(0, true); - }); - } - - trace!("control: data_out len={} first={} last={}", buf.len(), _first, _last); - let val = poll_fn(|cx| unsafe { - EP_OUT_WAKERS[0].register(cx.waker()); - let val = T::dpram().ep_out_buffer_control(0).read(); - if val.available(0) { - Poll::Pending - } else { - Poll::Ready(val) - } - }) - .await; - - let rx_len = val.length(0) as _; - trace!("control data_out DONE, rx_len = {}", rx_len); - - if rx_len > buf.len() { - return Err(EndpointError::BufferOverflow); - } - EndpointBuffer::::new(0x100, 64).read(&mut buf[..rx_len]); - - Ok(rx_len) - } - } - - fn data_in<'a>(&'a mut self, buf: &'a [u8], _first: bool, _last: bool) -> Self::DataInFuture<'a> { - async move { - trace!("control: data_in len={} first={} last={}", buf.len(), _first, _last); - - if buf.len() > 64 { - return Err(EndpointError::BufferOverflow); - } - EndpointBuffer::::new(0x100, 64).write(buf); - - unsafe { - let bufcontrol = T::dpram().ep_in_buffer_control(0); - let pid = !bufcontrol.read().pid(0); - bufcontrol.write(|w| { - w.set_length(0, buf.len() as _); - w.set_pid(0, pid); - w.set_full(0, true); - }); - cortex_m::asm::delay(12); - bufcontrol.write(|w| { - w.set_length(0, buf.len() as _); - w.set_pid(0, pid); - w.set_full(0, true); - w.set_available(0, true); - }); - } + async fn setup(&mut self) -> [u8; 8] { + loop { + trace!("SETUP read waiting"); + let regs = T::regs(); + unsafe { regs.inte().write_set(|w| w.set_setup_req(true)) }; poll_fn(|cx| unsafe { - EP_IN_WAKERS[0].register(cx.waker()); - let bufcontrol = T::dpram().ep_in_buffer_control(0); - if bufcontrol.read().available(0) { - Poll::Pending - } else { + EP_OUT_WAKERS[0].register(cx.waker()); + let regs = T::regs(); + if regs.sie_status().read().setup_rec() { Poll::Ready(()) + } else { + Poll::Pending } }) .await; - trace!("control: data_in DONE"); - if _last { - // prepare status phase right away. - unsafe { - let bufcontrol = T::dpram().ep_out_buffer_control(0); - bufcontrol.write(|w| { - w.set_length(0, 0); - w.set_pid(0, true); - }); - cortex_m::asm::delay(12); - bufcontrol.write(|w| { - w.set_length(0, 0); - w.set_pid(0, true); - w.set_available(0, true); - }); - } - } - - Ok(()) - } - } - - fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> { - async move { - trace!("control: accept"); - - let bufcontrol = T::dpram().ep_in_buffer_control(0); - unsafe { - bufcontrol.write(|w| { - w.set_length(0, 0); - w.set_pid(0, true); - w.set_full(0, true); - }); - cortex_m::asm::delay(12); - bufcontrol.write(|w| { - w.set_length(0, 0); - w.set_pid(0, true); - w.set_full(0, true); - w.set_available(0, true); - }); - } - - // wait for completion before returning, needed so - // set_address() doesn't happen early. - poll_fn(|cx| { - EP_IN_WAKERS[0].register(cx.waker()); - if unsafe { bufcontrol.read().available(0) } { - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await; - } - } - - fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> { - async move { - trace!("control: reject"); + let mut buf = [0; 8]; + EndpointBuffer::::new(0, 8).read(&mut buf); let regs = T::regs(); unsafe { - regs.ep_stall_arm().write_set(|w| { - w.set_ep0_in(true); - w.set_ep0_out(true); - }); - T::dpram().ep_out_buffer_control(0).write(|w| w.set_stall(true)); - T::dpram().ep_in_buffer_control(0).write(|w| w.set_stall(true)); + regs.sie_status().write(|w| w.set_setup_rec(true)); + + // set PID to 0, so (after toggling) first DATA is PID 1 + T::dpram().ep_in_buffer_control(0).write(|w| w.set_pid(0, false)); + T::dpram().ep_out_buffer_control(0).write(|w| w.set_pid(0, false)); } + + trace!("SETUP read ok"); + return buf; + } + } + + async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result { + unsafe { + let bufcontrol = T::dpram().ep_out_buffer_control(0); + let pid = !bufcontrol.read().pid(0); + bufcontrol.write(|w| { + w.set_length(0, self.max_packet_size); + w.set_pid(0, pid); + }); + cortex_m::asm::delay(12); + bufcontrol.write(|w| { + w.set_length(0, self.max_packet_size); + w.set_pid(0, pid); + w.set_available(0, true); + }); + } + + trace!("control: data_out len={} first={} last={}", buf.len(), first, last); + let val = poll_fn(|cx| unsafe { + EP_OUT_WAKERS[0].register(cx.waker()); + let val = T::dpram().ep_out_buffer_control(0).read(); + if val.available(0) { + Poll::Pending + } else { + Poll::Ready(val) + } + }) + .await; + + let rx_len = val.length(0) as _; + trace!("control data_out DONE, rx_len = {}", rx_len); + + if rx_len > buf.len() { + return Err(EndpointError::BufferOverflow); + } + EndpointBuffer::::new(0x100, 64).read(&mut buf[..rx_len]); + + Ok(rx_len) + } + + async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError> { + trace!("control: data_in len={} first={} last={}", data.len(), first, last); + + if data.len() > 64 { + return Err(EndpointError::BufferOverflow); + } + EndpointBuffer::::new(0x100, 64).write(data); + + unsafe { + let bufcontrol = T::dpram().ep_in_buffer_control(0); + let pid = !bufcontrol.read().pid(0); + bufcontrol.write(|w| { + w.set_length(0, data.len() as _); + w.set_pid(0, pid); + w.set_full(0, true); + }); + cortex_m::asm::delay(12); + bufcontrol.write(|w| { + w.set_length(0, data.len() as _); + w.set_pid(0, pid); + w.set_full(0, true); + w.set_available(0, true); + }); + } + + poll_fn(|cx| unsafe { + EP_IN_WAKERS[0].register(cx.waker()); + let bufcontrol = T::dpram().ep_in_buffer_control(0); + if bufcontrol.read().available(0) { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + trace!("control: data_in DONE"); + + if last { + // prepare status phase right away. + unsafe { + let bufcontrol = T::dpram().ep_out_buffer_control(0); + bufcontrol.write(|w| { + w.set_length(0, 0); + w.set_pid(0, true); + }); + cortex_m::asm::delay(12); + bufcontrol.write(|w| { + w.set_length(0, 0); + w.set_pid(0, true); + w.set_available(0, true); + }); + } + } + + Ok(()) + } + + async fn accept(&mut self) { + trace!("control: accept"); + + let bufcontrol = T::dpram().ep_in_buffer_control(0); + unsafe { + bufcontrol.write(|w| { + w.set_length(0, 0); + w.set_pid(0, true); + w.set_full(0, true); + }); + cortex_m::asm::delay(12); + bufcontrol.write(|w| { + w.set_length(0, 0); + w.set_pid(0, true); + w.set_full(0, true); + w.set_available(0, true); + }); + } + + // wait for completion before returning, needed so + // set_address() doesn't happen early. + poll_fn(|cx| { + EP_IN_WAKERS[0].register(cx.waker()); + if unsafe { bufcontrol.read().available(0) } { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + } + + async fn reject(&mut self) { + trace!("control: reject"); + + let regs = T::regs(); + unsafe { + regs.ep_stall_arm().write_set(|w| { + w.set_ep0_in(true); + w.set_ep0_out(true); + }); + T::dpram().ep_out_buffer_control(0).write(|w| w.set_stall(true)); + T::dpram().ep_in_buffer_control(0).write(|w| w.set_stall(true)); } } } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6b00518a..b7f718c5 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -44,7 +44,7 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} embedded-storage = "0.3.0" @@ -67,7 +67,7 @@ nb = "1.0.0" stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" -embedded-io = { version = "0.3.1", features = ["async"], optional = true } +embedded-io = { version = "0.4.0", features = ["async"], optional = true } [build-dependencies] proc-macro2 = "1.0.36" diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 8f81cb7a..90aa7d3b 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -71,7 +71,7 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(adc_g0)] T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(true); + reg.set_chselrmod(false); }); } @@ -200,7 +200,7 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(not(stm32g0))] T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); #[cfg(stm32g0)] - T::regs().chselr().write(|reg| reg.set_chsel(pin.channel() as u32)); + T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel())); // Some models are affected by an erratum: // If we perform conversions slower than 1 kHz, the first read ADC value can be diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 674255dd..e6ce05b7 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -3,6 +3,7 @@ use core::sync::atomic::{fence, Ordering}; use core::task::Waker; +use embassy_cortex_m::interrupt::Priority; use embassy_sync::waitqueue::AtomicWaker; use super::{TransferOptions, Word, WordSize}; @@ -38,10 +39,12 @@ impl State { static STATE: State = State::new(); /// safety: must be called only once -pub(crate) unsafe fn init() { +pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => { - crate::interrupt::$irq::steal().enable(); + let irq = crate::interrupt::$irq::steal(); + irq.set_priority(irq_priority); + irq.enable(); }; } crate::_generated::init_bdma(); diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index a45b8780..97a3df08 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,6 +1,7 @@ use core::sync::atomic::{fence, Ordering}; use core::task::Waker; +use embassy_cortex_m::interrupt::Priority; use embassy_sync::waitqueue::AtomicWaker; use super::{Burst, FlowControl, Request, TransferOptions, Word, WordSize}; @@ -67,10 +68,12 @@ impl State { static STATE: State = State::new(); /// safety: must be called only once -pub(crate) unsafe fn init() { +pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => { - interrupt::$irq::steal().enable(); + let irq = interrupt::$irq::steal(); + irq.set_priority(irq_priority); + irq.enable(); }; } crate::_generated::init_dma(); diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index cc030a93..74bce6aa 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -12,6 +12,8 @@ use core::mem; use core::pin::Pin; use core::task::{Context, Poll, Waker}; +#[cfg(any(dma, bdma))] +use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{impl_peripheral, into_ref}; #[cfg(dmamux)] @@ -294,11 +296,11 @@ pub struct NoDma; impl_peripheral!(NoDma); // safety: must be called only once at startup -pub(crate) unsafe fn init() { +pub(crate) unsafe fn init(#[cfg(bdma)] bdma_priority: Priority, #[cfg(dma)] dma_priority: Priority) { #[cfg(bdma)] - bdma::init(); + bdma::init(bdma_priority); #[cfg(dma)] - dma::init(); + dma::init(dma_priority); #[cfg(dmamux)] dmamux::init(); #[cfg(gpdma)] diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index a81ee118..5b76d1e7 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -116,6 +116,24 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); + // disable all MMC RX interrupts + mac.mmc_rx_interrupt_mask().write(|w| { + w.set_rxcrcerpim(true); + w.set_rxalgnerpim(true); + w.set_rxucgpim(true); + w.set_rxlpiuscim(true); + w.set_rxlpitrcim(true) + }); + + // disable all MMC TX interrupts + mac.mmc_tx_interrupt_mask().write(|w| { + w.set_txscolgpim(true); + w.set_txmcolgpim(true); + w.set_txgpktim(true); + w.set_txlpiuscim(true); + w.set_txlpitrcim(true); + }); + mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); mtl.mtltx_qomr().modify(|w| w.set_tsf(true)); diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index dca99185..f9078581 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -167,39 +167,33 @@ mod eh1 { } #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use futures::FutureExt; use super::*; impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for ExtiInput<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) } } } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 3f2129de..3178b3be 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -79,24 +79,19 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { let from = from - super::FLASH_BASE as u32; let to = to - super::FLASH_BASE as u32; - let bank_size = (super::FLASH_SIZE / 2) as u32; - - let (bank, start, end) = if to <= bank_size { + let (start, end) = if to <= super::FLASH_SIZE as u32 { let start_sector = from / super::ERASE_SIZE as u32; let end_sector = to / super::ERASE_SIZE as u32; - (0, start_sector, end_sector) - } else if from >= SECOND_BANK_OFFSET as u32 && to <= (SECOND_BANK_OFFSET as u32 + bank_size) { - let start_sector = (from - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32; - let end_sector = (to - SECOND_BANK_OFFSET as u32) / super::ERASE_SIZE as u32; - (1, start_sector, end_sector) + (start_sector, end_sector) } else { - error!("Attempting to write outside of defined sectors"); + error!("Attempting to write outside of defined sectors {:x} {:x}", from, to); return Err(Error::Unaligned); }; - trace!("Erasing bank {}, sectors from {} to {}", bank, start, end); + trace!("Erasing sectors from {} to {}", start, end); for sector in start..end { - let ret = erase_sector(pac::FLASH.bank(bank), sector as u8); + let bank = if sector >= 8 { 1 } else { 0 }; + let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); if ret.is_err() { return ret; } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index aa4e6bb0..47dc7d2a 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1048,43 +1048,35 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use core::future::Future; - use super::super::{RxDma, TxDma}; use super::*; impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(address, buffer) + async fn read<'a>(&'a mut self, address: u8, read: &'a mut [u8]) -> Result<(), Self::Error> { + self.read(address, read).await } - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(address, bytes) + async fn write<'a>(&'a mut self, address: u8, write: &'a [u8]) -> Result<(), Self::Error> { + self.write(address, write).await } - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn write_read<'a>( + async fn write_read<'a>( &'a mut self, address: u8, - bytes: &'a [u8], - buffer: &'a mut [u8], - ) -> Self::WriteReadFuture<'a> { - self.write_read(address, bytes, buffer) + write: &'a [u8], + read: &'a mut [u8], + ) -> Result<(), Self::Error> { + self.write_read(address, write, read).await } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - - fn transaction<'a, 'b>( + async fn transaction<'a, 'b>( &'a mut self, address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { + operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], + ) -> Result<(), Self::Error> { let _ = address; let _ = operations; - async move { todo!() } + todo!() } } } diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index bcf2feee..16c46ca2 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,5 +1,9 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr( + feature = "nightly", + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) +)] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] // This must go FIRST so that all the other modules see its macros. pub mod fmt; @@ -75,6 +79,8 @@ pub(crate) mod _generated { // Reexports pub use _generated::{peripherals, Peripherals}; pub use embassy_cortex_m::executor; +#[cfg(any(dma, bdma))] +use embassy_cortex_m::interrupt::Priority; pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] @@ -87,6 +93,10 @@ pub struct Config { pub rcc: rcc::Config, #[cfg(dbgmcu)] pub enable_debug_during_sleep: bool, + #[cfg(bdma)] + pub bdma_interrupt_priority: Priority, + #[cfg(dma)] + pub dma_interrupt_priority: Priority, } impl Default for Config { @@ -95,6 +105,10 @@ impl Default for Config { rcc: Default::default(), #[cfg(dbgmcu)] enable_debug_during_sleep: true, + #[cfg(bdma)] + bdma_interrupt_priority: Priority::P0, + #[cfg(dma)] + dma_interrupt_priority: Priority::P0, } } } @@ -133,7 +147,12 @@ pub fn init(config: Config) -> Peripherals { } gpio::init(); - dma::init(); + dma::init( + #[cfg(bdma)] + config.bdma_interrupt_priority, + #[cfg(dma)] + config.dma_interrupt_priority, + ); #[cfg(feature = "exti")] exti::init(); diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index c91f3c8b..a52c65b9 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -18,6 +18,9 @@ use crate::rcc::RccPeripheral; use crate::time::Hertz; use crate::{peripherals, Peripheral}; +/// Frequency used for SD Card initialization. Must be no higher than 400 kHz. +const SD_INIT_FREQ: Hertz = Hertz(400_000); + /// The signalling scheme used on the SDMMC bus #[non_exhaustive] #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -295,7 +298,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { T::reset(); let inner = T::inner(); - let clock = unsafe { inner.new_inner(T::frequency()) }; + unsafe { inner.new_inner() }; irq.set_handler(Self::on_interrupt); irq.unpend(); @@ -314,7 +317,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { d3, config, - clock, + clock: SD_INIT_FREQ, signalling: Default::default(), card: None, } @@ -415,7 +418,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { T::reset(); let inner = T::inner(); - let clock = unsafe { inner.new_inner(T::frequency()) }; + unsafe { inner.new_inner() }; irq.set_handler(Self::on_interrupt); irq.unpend(); @@ -434,7 +437,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { d3, config, - clock, + clock: SD_INIT_FREQ, signalling: Default::default(), card: None, } @@ -561,16 +564,10 @@ impl SdmmcInner { /// # Safety /// /// Access to `regs` registers should be exclusive - unsafe fn new_inner(&self, kernel_clk: Hertz) -> Hertz { + unsafe fn new_inner(&self) { let regs = self.0; - // While the SD/SDIO card or eMMC is in identification mode, - // the SDMMC_CK frequency must be less than 400 kHz. - let (clkdiv, clock) = unwrap!(clk_div(kernel_clk, 400_000)); - regs.clkcr().write(|w| { - w.set_widbus(0); - w.set_clkdiv(clkdiv); w.set_pwrsav(false); w.set_negedge(false); w.set_hwfc_en(true); @@ -582,8 +579,6 @@ impl SdmmcInner { // Power off, writen 00: Clock to the card is stopped; // D[7:0], CMD, and CK are driven high. regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); - - clock } /// Initializes card (if present) and sets the bus at the @@ -605,6 +600,19 @@ impl SdmmcInner { // NOTE(unsafe) We have exclusive access to the peripheral unsafe { + // While the SD/SDIO card or eMMC is in identification mode, + // the SDMMC_CK frequency must be no more than 400 kHz. + let (clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); + *clock = init_clock; + + // CPSMACT and DPSMACT must be 0 to set WIDBUS + self.wait_idle(); + + regs.clkcr().modify(|w| { + w.set_widbus(0); + w.set_clkdiv(clkdiv); + }); + regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); self.cmd(Cmd::idle(), false)?; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 39642778..ab4352a5 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -8,7 +8,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; use self::sealed::WordSize; -use crate::dma::{slice_ptr_parts, NoDma, Transfer}; +use crate::dma::{slice_ptr_parts, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::AnyPin; use crate::pac::spi::{regs, vals, Spi as Regs}; @@ -812,7 +812,7 @@ mod eh02 { // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 macro_rules! impl_blocking { ($w:ident) => { - impl<'d, T: Instance> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, Tx, Rx> { type Error = Error; fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { @@ -820,7 +820,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, Tx, Rx> { type Error = Error; fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { @@ -849,19 +849,19 @@ mod eh1 { } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, Tx, Rx> { fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { self.blocking_read(words) } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, Tx, Rx> { fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { self.blocking_write(words) } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBus for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus for Spi<'d, T, Tx, Rx> { fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } @@ -885,46 +885,34 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use core::future::Future; - use super::*; impl<'d, T: Instance, Tx, Rx> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> { - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async { Ok(()) } + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } impl<'d, T: Instance, Tx: TxDma, Rx, W: Word> embedded_hal_async::spi::SpiBusWrite for Spi<'d, T, Tx, Rx> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, data: &'a [W]) -> Self::WriteFuture<'a> { - self.write(data) + async fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { + self.write(words).await } } impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBusRead for Spi<'d, T, Tx, Rx> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, data: &'a mut [W]) -> Self::ReadFuture<'a> { - self.read(data) + async fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { + self.read(words).await } } impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Tx, Rx> { - type TransferFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer<'a>(&'a mut self, rx: &'a mut [W], tx: &'a [W]) -> Self::TransferFuture<'a> { - self.transfer(rx, tx) + async fn transfer<'a>(&'a mut self, read: &'a mut [W], write: &'a [W]) -> Result<(), Self::Error> { + self.transfer(read, write).await } - type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - - fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Self::TransferInPlaceFuture<'a> { - self.transfer_in_place(words) + async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Result<(), Self::Error> { + self.transfer_in_place(words).await } } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 0a6d6e14..d024bedc 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,5 +1,5 @@ use core::cell::RefCell; -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::task::Poll; use atomic_polyfill::{compiler_fence, Ordering}; @@ -112,6 +112,9 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { unsafe { r.cr1().modify(|w| { + #[cfg(lpuart_v2)] + w.set_fifoen(true); + w.set_rxneie(true); w.set_idleie(true); }); @@ -339,32 +342,20 @@ impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { } impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.inner_read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).await } } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.inner.inner_read(buf) + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.inner_read(buf).await } } impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - self.inner_fill_buf() + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner_fill_buf().await } fn consume(&mut self, amt: usize) { @@ -373,12 +364,8 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { - type FillBufFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - self.inner.inner_fill_buf() + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.inner_fill_buf().await } fn consume(&mut self, amt: usize) { @@ -387,37 +374,21 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<' } impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.inner_write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner_write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.inner_flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner_flush().await } } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.inner.inner_write(buf) + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner.inner_write(buf).await } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.inner.inner_flush() + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.inner_flush().await } } diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 2654f156..460abfe2 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -1,6 +1,6 @@ #![macro_use] -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::marker::PhantomData; use core::sync::atomic::Ordering; use core::task::Poll; @@ -429,9 +429,7 @@ pub struct Bus<'d, T: Instance> { } impl<'d, T: Instance> driver::Bus for Bus<'d, T> { - type PollFuture<'a> = impl Future + 'a where Self: 'a; - - fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> { + async fn poll(&mut self) -> Event { poll_fn(move |cx| unsafe { BUS_WAKER.register(cx.waker()); @@ -488,6 +486,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { return Poll::Ready(Event::PowerDetected); } }) + .await } #[inline] @@ -598,22 +597,11 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { trace!("EPR after: {:04x}", unsafe { reg.read() }.0); } - type EnableFuture<'a> = impl Future + 'a where Self: 'a; + async fn enable(&mut self) {} + async fn disable(&mut self) {} - fn enable(&mut self) -> Self::EnableFuture<'_> { - async move {} - } - - type DisableFuture<'a> = impl Future + 'a where Self: 'a; - - fn disable(&mut self) -> Self::DisableFuture<'_> { - async move {} - } - - type RemoteWakeupFuture<'a> = impl Future> + 'a where Self: 'a; - - fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> { - async move { Err(Unsupported) } + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + Err(Unsupported) } } @@ -676,24 +664,20 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { &self.info } - type WaitEnabledFuture<'a> = impl Future + 'a where Self: 'a; - - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { - async move { - trace!("wait_enabled OUT WAITING"); - let index = self.info.addr.index(); - poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); - let regs = T::regs(); - if unsafe { regs.epr(index).read() }.stat_tx() == Stat::DISABLED { - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await; - trace!("wait_enabled OUT OK"); - } + async fn wait_enabled(&mut self) { + trace!("wait_enabled OUT WAITING"); + let index = self.info.addr.index(); + poll_fn(|cx| { + EP_OUT_WAKERS[index].register(cx.waker()); + let regs = T::regs(); + if unsafe { regs.epr(index).read() }.stat_tx() == Stat::DISABLED { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + trace!("wait_enabled OUT OK"); } } @@ -702,116 +686,104 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> { &self.info } - type WaitEnabledFuture<'a> = impl Future + 'a where Self: 'a; - - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { - async move { - trace!("wait_enabled OUT WAITING"); - let index = self.info.addr.index(); - poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); - let regs = T::regs(); - if unsafe { regs.epr(index).read() }.stat_rx() == Stat::DISABLED { - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await; - trace!("wait_enabled OUT OK"); - } + async fn wait_enabled(&mut self) { + trace!("wait_enabled OUT WAITING"); + let index = self.info.addr.index(); + poll_fn(|cx| { + EP_OUT_WAKERS[index].register(cx.waker()); + let regs = T::regs(); + if unsafe { regs.epr(index).read() }.stat_rx() == Stat::DISABLED { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + trace!("wait_enabled OUT OK"); } } impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - trace!("READ WAITING, buf.len() = {}", buf.len()); - let index = self.info.addr.index(); - let stat = poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); - let regs = T::regs(); - let stat = unsafe { regs.epr(index).read() }.stat_rx(); - if matches!(stat, Stat::NAK | Stat::DISABLED) { - Poll::Ready(stat) - } else { - Poll::Pending - } - }) - .await; - - if stat == Stat::DISABLED { - return Err(EndpointError::Disabled); - } - - let rx_len = self.read_data(buf)?; - + async fn read(&mut self, buf: &mut [u8]) -> Result { + trace!("READ WAITING, buf.len() = {}", buf.len()); + let index = self.info.addr.index(); + let stat = poll_fn(|cx| { + EP_OUT_WAKERS[index].register(cx.waker()); let regs = T::regs(); - unsafe { - regs.epr(index).write(|w| { - w.set_ep_type(convert_type(self.info.ep_type)); - w.set_ea(self.info.addr.index() as _); - w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); - w.set_stat_tx(Stat(0)); - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }) - }; - trace!("READ OK, rx_len = {}", rx_len); + let stat = unsafe { regs.epr(index).read() }.stat_rx(); + if matches!(stat, Stat::NAK | Stat::DISABLED) { + Poll::Ready(stat) + } else { + Poll::Pending + } + }) + .await; - Ok(rx_len) + if stat == Stat::DISABLED { + return Err(EndpointError::Disabled); } + + let rx_len = self.read_data(buf)?; + + let regs = T::regs(); + unsafe { + regs.epr(index).write(|w| { + w.set_ep_type(convert_type(self.info.ep_type)); + w.set_ea(self.info.addr.index() as _); + w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); + w.set_stat_tx(Stat(0)); + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear + }) + }; + trace!("READ OK, rx_len = {}", rx_len); + + Ok(rx_len) } } impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - if buf.len() > self.info.max_packet_size as usize { - return Err(EndpointError::BufferOverflow); - } - - let index = self.info.addr.index(); - - trace!("WRITE WAITING"); - let stat = poll_fn(|cx| { - EP_IN_WAKERS[index].register(cx.waker()); - let regs = T::regs(); - let stat = unsafe { regs.epr(index).read() }.stat_tx(); - if matches!(stat, Stat::NAK | Stat::DISABLED) { - Poll::Ready(stat) - } else { - Poll::Pending - } - }) - .await; - - if stat == Stat::DISABLED { - return Err(EndpointError::Disabled); - } - - self.write_data(buf); - - let regs = T::regs(); - unsafe { - regs.epr(index).write(|w| { - w.set_ep_type(convert_type(self.info.ep_type)); - w.set_ea(self.info.addr.index() as _); - w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); - w.set_stat_rx(Stat(0)); - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }) - }; - - trace!("WRITE OK"); - - Ok(()) + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + if buf.len() > self.info.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); } + + let index = self.info.addr.index(); + + trace!("WRITE WAITING"); + let stat = poll_fn(|cx| { + EP_IN_WAKERS[index].register(cx.waker()); + let regs = T::regs(); + let stat = unsafe { regs.epr(index).read() }.stat_tx(); + if matches!(stat, Stat::NAK | Stat::DISABLED) { + Poll::Ready(stat) + } else { + Poll::Pending + } + }) + .await; + + if stat == Stat::DISABLED { + return Err(EndpointError::Disabled); + } + + self.write_data(buf); + + let regs = T::regs(); + unsafe { + regs.epr(index).write(|w| { + w.set_ep_type(convert_type(self.info.ep_type)); + w.set_ea(self.info.addr.index() as _); + w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); + w.set_stat_rx(Stat(0)); + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear + }) + }; + + trace!("WRITE OK"); + + Ok(()) } } @@ -823,84 +795,16 @@ pub struct ControlPipe<'d, T: Instance> { } impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { - type SetupFuture<'a> = impl Future + 'a where Self: 'a; - type DataOutFuture<'a> = impl Future> + 'a where Self: 'a; - type DataInFuture<'a> = impl Future> + 'a where Self: 'a; - type AcceptFuture<'a> = impl Future + 'a where Self: 'a; - type RejectFuture<'a> = impl Future + 'a where Self: 'a; - fn max_packet_size(&self) -> usize { usize::from(self.max_packet_size) } - fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> { - async move { - loop { - trace!("SETUP read waiting"); - poll_fn(|cx| { - EP_OUT_WAKERS[0].register(cx.waker()); - if EP0_SETUP.load(Ordering::Relaxed) { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - let mut buf = [0; 8]; - let rx_len = self.ep_out.read_data(&mut buf); - if rx_len != Ok(8) { - trace!("SETUP read failed: {:?}", rx_len); - continue; - } - - EP0_SETUP.store(false, Ordering::Relaxed); - - trace!("SETUP read ok"); - return buf; - } - } - } - - fn data_out<'a>(&'a mut self, buf: &'a mut [u8], first: bool, last: bool) -> Self::DataOutFuture<'a> { - async move { - let regs = T::regs(); - - // When a SETUP is received, Stat/Stat is set to NAK. - // On first transfer, we must set Stat=VALID, to get the OUT data stage. - // We want Stat=STALL so that the host gets a STALL if it switches to the status - // stage too soon, except in the last transfer we set Stat=NAK so that it waits - // for the status stage, which we will ACK or STALL later. - if first || last { - let mut stat_rx = 0; - let mut stat_tx = 0; - if first { - // change NAK -> VALID - stat_rx ^= Stat::NAK.0 ^ Stat::VALID.0; - stat_tx ^= Stat::NAK.0 ^ Stat::STALL.0; - } - if last { - // change STALL -> VALID - stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0; - } - // Note: if this is the first AND last transfer, the above effectively - // changes stat_tx like NAK -> NAK, so noop. - unsafe { - regs.epr(0).write(|w| { - w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(stat_rx)); - w.set_stat_tx(Stat(stat_tx)); - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }) - } - } - - trace!("data_out WAITING, buf.len() = {}", buf.len()); + async fn setup(&mut self) -> [u8; 8] { + loop { + trace!("SETUP read waiting"); poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); - let regs = T::regs(); - if unsafe { regs.epr(0).read() }.stat_rx() == Stat::NAK { + if EP0_SETUP.load(Ordering::Relaxed) { Poll::Ready(()) } else { Poll::Pending @@ -908,157 +812,209 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { }) .await; - if EP0_SETUP.load(Ordering::Relaxed) { - trace!("received another SETUP, aborting data_out."); - return Err(EndpointError::Disabled); + let mut buf = [0; 8]; + let rx_len = self.ep_out.read_data(&mut buf); + if rx_len != Ok(8) { + trace!("SETUP read failed: {:?}", rx_len); + continue; } - let rx_len = self.ep_out.read_data(buf)?; + EP0_SETUP.store(false, Ordering::Relaxed); + trace!("SETUP read ok"); + return buf; + } + } + + async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result { + let regs = T::regs(); + + // When a SETUP is received, Stat/Stat is set to NAK. + // On first transfer, we must set Stat=VALID, to get the OUT data stage. + // We want Stat=STALL so that the host gets a STALL if it switches to the status + // stage too soon, except in the last transfer we set Stat=NAK so that it waits + // for the status stage, which we will ACK or STALL later. + if first || last { + let mut stat_rx = 0; + let mut stat_tx = 0; + if first { + // change NAK -> VALID + stat_rx ^= Stat::NAK.0 ^ Stat::VALID.0; + stat_tx ^= Stat::NAK.0 ^ Stat::STALL.0; + } + if last { + // change STALL -> VALID + stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0; + } + // Note: if this is the first AND last transfer, the above effectively + // changes stat_tx like NAK -> NAK, so noop. unsafe { regs.epr(0).write(|w| { w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(match last { - // If last, set STAT_RX=STALL. - true => Stat::NAK.0 ^ Stat::STALL.0, - // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet. - false => Stat::NAK.0 ^ Stat::VALID.0, - })); + w.set_stat_rx(Stat(stat_rx)); + w.set_stat_tx(Stat(stat_tx)); w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear }) - }; - - Ok(rx_len) + } } + + trace!("data_out WAITING, buf.len() = {}", buf.len()); + poll_fn(|cx| { + EP_OUT_WAKERS[0].register(cx.waker()); + let regs = T::regs(); + if unsafe { regs.epr(0).read() }.stat_rx() == Stat::NAK { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + if EP0_SETUP.load(Ordering::Relaxed) { + trace!("received another SETUP, aborting data_out."); + return Err(EndpointError::Disabled); + } + + let rx_len = self.ep_out.read_data(buf)?; + + unsafe { + regs.epr(0).write(|w| { + w.set_ep_type(EpType::CONTROL); + w.set_stat_rx(Stat(match last { + // If last, set STAT_RX=STALL. + true => Stat::NAK.0 ^ Stat::STALL.0, + // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet. + false => Stat::NAK.0 ^ Stat::VALID.0, + })); + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear + }) + }; + + Ok(rx_len) } - fn data_in<'a>(&'a mut self, buf: &'a [u8], first: bool, last: bool) -> Self::DataInFuture<'a> { - async move { - trace!("control: data_in"); + async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError> { + trace!("control: data_in"); - if buf.len() > self.ep_in.info.max_packet_size as usize { - return Err(EndpointError::BufferOverflow); + if data.len() > self.ep_in.info.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); + } + + let regs = T::regs(); + + // When a SETUP is received, Stat is set to NAK. + // We want it to be STALL in non-last transfers. + // We want it to be VALID in last transfer, so the HW does the status stage. + if first || last { + let mut stat_rx = 0; + if first { + // change NAK -> STALL + stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0; } - - let regs = T::regs(); - - // When a SETUP is received, Stat is set to NAK. - // We want it to be STALL in non-last transfers. - // We want it to be VALID in last transfer, so the HW does the status stage. - if first || last { - let mut stat_rx = 0; - if first { - // change NAK -> STALL - stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0; - } - if last { - // change STALL -> VALID - stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0; - } - // Note: if this is the first AND last transfer, the above effectively - // does a change of NAK -> VALID. - unsafe { - regs.epr(0).write(|w| { - w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(stat_rx)); - w.set_ep_kind(last); // set OUT_STATUS if last. - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }) - } + if last { + // change STALL -> VALID + stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0; } - - trace!("WRITE WAITING"); - poll_fn(|cx| { - EP_IN_WAKERS[0].register(cx.waker()); - EP_OUT_WAKERS[0].register(cx.waker()); - let regs = T::regs(); - if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - if EP0_SETUP.load(Ordering::Relaxed) { - trace!("received another SETUP, aborting data_in."); - return Err(EndpointError::Disabled); - } - - self.ep_in.write_data(buf); - - let regs = T::regs(); + // Note: if this is the first AND last transfer, the above effectively + // does a change of NAK -> VALID. unsafe { regs.epr(0).write(|w| { w.set_ep_type(EpType::CONTROL); - w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); + w.set_stat_rx(Stat(stat_rx)); w.set_ep_kind(last); // set OUT_STATUS if last. w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear }) - }; - - trace!("WRITE OK"); - - Ok(()) - } - } - - fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> { - async move { - let regs = T::regs(); - trace!("control: accept"); - - self.ep_in.write_data(&[]); - - // Set OUT=stall, IN=accept - unsafe { - let epr = regs.epr(0).read(); - regs.epr(0).write(|w| { - w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); - w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0)); - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }); } - trace!("control: accept WAITING"); + } - // Wait is needed, so that we don't set the address too soon, breaking the status stage. - // (embassy-usb sets the address after accept() returns) - poll_fn(|cx| { - EP_IN_WAKERS[0].register(cx.waker()); - let regs = T::regs(); - if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { - Poll::Ready(()) - } else { - Poll::Pending - } + trace!("WRITE WAITING"); + poll_fn(|cx| { + EP_IN_WAKERS[0].register(cx.waker()); + EP_OUT_WAKERS[0].register(cx.waker()); + let regs = T::regs(); + if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + if EP0_SETUP.load(Ordering::Relaxed) { + trace!("received another SETUP, aborting data_in."); + return Err(EndpointError::Disabled); + } + + self.ep_in.write_data(data); + + let regs = T::regs(); + unsafe { + regs.epr(0).write(|w| { + w.set_ep_type(EpType::CONTROL); + w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); + w.set_ep_kind(last); // set OUT_STATUS if last. + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear }) - .await; + }; - trace!("control: accept OK"); - } + trace!("WRITE OK"); + + Ok(()) } - fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> { - async move { - let regs = T::regs(); - trace!("control: reject"); + async fn accept(&mut self) { + let regs = T::regs(); + trace!("control: accept"); - // Set IN+OUT to stall - unsafe { - let epr = regs.epr(0).read(); - regs.epr(0).write(|w| { - w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); - w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0)); - w.set_ctr_rx(true); // don't clear - w.set_ctr_tx(true); // don't clear - }); + self.ep_in.write_data(&[]); + + // Set OUT=stall, IN=accept + unsafe { + let epr = regs.epr(0).read(); + regs.epr(0).write(|w| { + w.set_ep_type(EpType::CONTROL); + w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); + w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0)); + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear + }); + } + trace!("control: accept WAITING"); + + // Wait is needed, so that we don't set the address too soon, breaking the status stage. + // (embassy-usb sets the address after accept() returns) + poll_fn(|cx| { + EP_IN_WAKERS[0].register(cx.waker()); + let regs = T::regs(); + if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { + Poll::Ready(()) + } else { + Poll::Pending } + }) + .await; + + trace!("control: accept OK"); + } + + async fn reject(&mut self) { + let regs = T::regs(); + trace!("control: reject"); + + // Set IN+OUT to stall + unsafe { + let epr = regs.epr(0).read(); + regs.epr(0).write(|w| { + w.set_ep_type(EpType::CONTROL); + w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); + w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0)); + w.set_ctr_rx(true); // don't clear + w.set_ctr_tx(true); // don't clear + }); } } } diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index 85176eef..92b9a5ca 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -13,12 +13,12 @@ pub struct IndependentWatchdog<'d, T: Instance> { const MAX_RL: u16 = 0xFFF; /// Calculates maximum watchdog timeout in us (RL = 0xFFF) for a given prescaler -const fn max_timeout(prescaler: u8) -> u32 { +const fn max_timeout(prescaler: u16) -> u32 { 1_000_000 * MAX_RL as u32 / (LSI_FREQ.0 / prescaler as u32) } /// Calculates watchdog reload value for the given prescaler and desired timeout -const fn reload_value(prescaler: u8, timeout_us: u32) -> u16 { +const fn reload_value(prescaler: u16, timeout_us: u32) -> u16 { (timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16 } @@ -33,12 +33,12 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { // Find lowest prescaler value, which makes watchdog period longer or equal to timeout. // This iterates from 4 (2^2) to 256 (2^8). let psc_power = unwrap!((2..=8).find(|psc_power| { - let psc = 2u8.pow(*psc_power); + let psc = 2u16.pow(*psc_power); timeout_us <= max_timeout(psc) })); // Prescaler value - let psc = 2u8.pow(psc_power); + let psc = 2u16.pow(psc_power); // Convert prescaler power to PR register value let pr = psc_power as u8 - 2; diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index b7fe1643..1eeb94c9 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -35,7 +35,7 @@ atomic-polyfill = "1.0.1" critical-section = "1.1" heapless = "0.7.5" cfg-if = "1.0.0" -embedded-io = "0.3.1" +embedded-io = "0.4.0" [dev-dependencies] futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 80bb907a..f9435ecf 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index cd577f34..905686ac 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -352,8 +352,6 @@ where mod io_impls { use core::convert::Infallible; - use futures_util::FutureExt; - use super::*; impl embedded_io::Io for Pipe { @@ -361,30 +359,18 @@ mod io_impls { } impl embedded_io::asynch::Read for Pipe { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - Pipe::read(self, buf).map(Ok) + async fn read(&mut self, buf: &mut [u8]) -> Result { + Ok(Pipe::read(self, buf).await) } } impl embedded_io::asynch::Write for Pipe { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - Pipe::write(self, buf).map(Ok) + async fn write(&mut self, buf: &[u8]) -> Result { + Ok(Pipe::write(self, buf).await) } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - futures_util::future::ready(Ok(())) + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } @@ -393,30 +379,18 @@ mod io_impls { } impl embedded_io::asynch::Read for &Pipe { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - Pipe::read(self, buf).map(Ok) + async fn read(&mut self, buf: &mut [u8]) -> Result { + Ok(Pipe::read(self, buf).await) } } impl embedded_io::asynch::Write for &Pipe { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - Pipe::write(self, buf).map(Ok) + async fn write(&mut self, buf: &[u8]) -> Result { + Ok(Pipe::write(self, buf).await) } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - futures_util::future::ready(Ok(())) + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } @@ -425,12 +399,8 @@ mod io_impls { } impl embedded_io::asynch::Read for Reader<'_, M, N> { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - Reader::read(self, buf).map(Ok) + async fn read(&mut self, buf: &mut [u8]) -> Result { + Ok(Reader::read(self, buf).await) } } @@ -439,20 +409,12 @@ mod io_impls { } impl embedded_io::asynch::Write for Writer<'_, M, N> { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - Writer::write(self, buf).map(Ok) + async fn write(&mut self, buf: &[u8]) -> Result { + Ok(Writer::write(self, buf).await) } - type FlushFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - futures_util::future::ready(Ok(())) + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } } diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index c3c10a8a..bea67d8b 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -56,6 +56,15 @@ where } } +impl Default for Signal +where + M: RawMutex, +{ + fn default() -> Self { + Self::new() + } +} + impl Signal where M: RawMutex, diff --git a/embassy-sync/src/waitqueue/waker.rs b/embassy-sync/src/waitqueue/waker.rs index 64e300eb..9ce94a08 100644 --- a/embassy-sync/src/waitqueue/waker.rs +++ b/embassy-sync/src/waitqueue/waker.rs @@ -6,7 +6,7 @@ use crate::blocking_mutex::raw::CriticalSectionRawMutex; use crate::blocking_mutex::Mutex; /// Utility struct to register and wake a waker. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct WakerRegistration { waker: Option, } diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 9487003c..5701ab35 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -134,7 +134,7 @@ 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.9", optional = true} -embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} futures-util = { version = "0.3.17", default-features = false } embassy-sync = { version = "0.1", path = "../embassy-sync" } diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs index ff6b6869..0ca176ab 100644 --- a/embassy-time/src/delay.rs +++ b/embassy-time/src/delay.rs @@ -33,26 +33,18 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use core::future::Future; - - use futures_util::FutureExt; - use super::*; use crate::Timer; impl embedded_hal_async::delay::DelayUs for Delay { type Error = core::convert::Infallible; - type DelayUsFuture<'a> = impl Future> + 'a where Self: 'a; - - fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> { - Timer::after(Duration::from_micros(micros as _)).map(Ok) + async fn delay_us(&mut self, micros: u32) -> Result<(), Self::Error> { + Ok(Timer::after(Duration::from_micros(micros as _)).await) } - type DelayMsFuture<'a> = impl Future> + 'a where Self: 'a; - - fn delay_ms(&mut self, millis: u32) -> Self::DelayMsFuture<'_> { - Timer::after(Duration::from_millis(millis as _)).map(Ok) + async fn delay_ms(&mut self, millis: u32) -> Result<(), Self::Error> { + Ok(Timer::after(Duration::from_millis(millis as _)).await) } } } diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 586aa28d..8b0aebe1 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] #![doc = include_str!("../README.md")] #![allow(clippy::new_without_default)] #![warn(missing_docs)] diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 931e9c31..9300ff81 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] - -use core::future::Future; +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] /// Direction of USB traffic. Note that in the USB standard the direction is always indicated from /// the perspective of the host, which is backward for devices, but the standard directions are used @@ -155,27 +155,14 @@ pub trait Driver<'a> { } pub trait Bus { - type EnableFuture<'a>: Future + 'a - where - Self: 'a; - type DisableFuture<'a>: Future + 'a - where - Self: 'a; - type PollFuture<'a>: Future + 'a - where - Self: 'a; - type RemoteWakeupFuture<'a>: Future> + 'a - where - Self: 'a; - /// Enables the USB peripheral. Soon after enabling the device will be reset, so /// there is no need to perform a USB reset in this method. - fn enable(&mut self) -> Self::EnableFuture<'_>; + async fn enable(&mut self); /// Disables and powers down the USB peripheral. - fn disable(&mut self) -> Self::DisableFuture<'_>; + async fn disable(&mut self); - fn poll<'a>(&'a mut self) -> Self::PollFuture<'a>; + async fn poll(&mut self) -> Event; /// Sets the device USB address to `addr`. fn set_address(&mut self, addr: u8); @@ -197,7 +184,7 @@ pub trait Bus { /// /// # Errors /// - /// * [`Unsupported`](crate::driver::Unsupported) - This UsbBus implementation doesn't support + /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support /// simulating a disconnect or it has not been enabled at creation time. fn force_reset(&mut self) -> Result<(), Unsupported> { Err(Unsupported) @@ -207,87 +194,59 @@ pub trait Bus { /// /// # Errors /// - /// * [`Unsupported`](crate::driver::Unsupported) - This UsbBus implementation doesn't support + /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support /// remote wakeup or it has not been enabled at creation time. - fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_>; + async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; } pub trait Endpoint { - type WaitEnabledFuture<'a>: Future + 'a - where - Self: 'a; - /// Get the endpoint address fn info(&self) -> &EndpointInfo; /// Waits for the endpoint to be enabled. - fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_>; + async fn wait_enabled(&mut self); } pub trait EndpointOut: Endpoint { - type ReadFuture<'a>: Future> + 'a - where - Self: 'a; - /// Reads a single packet of data from the endpoint, and returns the actual length of /// the packet. /// /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; + async fn read(&mut self, buf: &mut [u8]) -> Result; } pub trait ControlPipe { - type SetupFuture<'a>: Future + 'a - where - Self: 'a; - type DataOutFuture<'a>: Future> + 'a - where - Self: 'a; - type DataInFuture<'a>: Future> + 'a - where - Self: 'a; - type AcceptFuture<'a>: Future + 'a - where - Self: 'a; - type RejectFuture<'a>: Future + 'a - where - Self: 'a; - /// Maximum packet size for the control pipe fn max_packet_size(&self) -> usize; /// Reads a single setup packet from the endpoint. - fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a>; + async fn setup(&mut self) -> [u8; 8]; /// Reads a DATA OUT packet into `buf` in response to a control write request. /// /// Must be called after `setup()` for requests with `direction` of `Out` /// and `length` greater than zero. - fn data_out<'a>(&'a mut self, buf: &'a mut [u8], first: bool, last: bool) -> Self::DataOutFuture<'a>; + async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result; /// Sends a DATA IN packet with `data` in response to a control read request. /// /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`. - fn data_in<'a>(&'a mut self, data: &'a [u8], first: bool, last: bool) -> Self::DataInFuture<'a>; + async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>; /// Accepts a control request. /// /// Causes the STATUS packet for the current request to be ACKed. - fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a>; + async fn accept(&mut self); /// Rejects a control request. /// /// Sets a STALL condition on the pipe to indicate an error. - fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a>; + async fn reject(&mut self); } pub trait EndpointIn: Endpoint { - type WriteFuture<'a>: Future> + 'a - where - Self: 'a; - /// Writes a single packet of data to the endpoint. - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>; + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; } #[derive(Copy, Clone, Eq, PartialEq, Debug)] diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml new file mode 100644 index 00000000..2f466592 --- /dev/null +++ b/embassy-usb-logger/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "embassy-usb-logger" +version = "0.1.0" +edition = "2021" + +[dependencies] +embassy-usb = { version = "0.1.0", path = "../embassy-usb" } +embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +futures = { version = "0.3", default-features = false } +static_cell = "1" +usbd-hid = "0.6.0" +log = "0.4" diff --git a/embassy-usb-logger/README.md b/embassy-usb-logger/README.md new file mode 100644 index 00000000..81b0dcd0 --- /dev/null +++ b/embassy-usb-logger/README.md @@ -0,0 +1,15 @@ +# embassy-usb-logger + +USB implementation of the `log` crate. This logger can be used by any device that implements `embassy-usb`. When running, +it will output all logging done through the `log` facade to the USB serial peripheral. + +## Usage + +Add the following embassy task to your application. The `Driver` type is different depending on which HAL you use. + + ```rust +#[embassy_executor::task] +async fn logger_task(driver: Driver<'static, USB>) { + embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); +} +``` diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs new file mode 100644 index 00000000..6386e209 --- /dev/null +++ b/embassy-usb-logger/src/lib.rs @@ -0,0 +1,146 @@ +#![no_std] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] + +use core::fmt::Write as _; + +use embassy_futures::join::join; +use embassy_sync::pipe::Pipe; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::Driver; +use embassy_usb::{Builder, Config}; +use log::{Metadata, Record}; + +type CS = embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; + +/// The logger state containing buffers that must live as long as the USB peripheral. +pub struct LoggerState<'d> { + state: State<'d>, + device_descriptor: [u8; 32], + config_descriptor: [u8; 128], + bos_descriptor: [u8; 16], + control_buf: [u8; 64], +} + +impl<'d> LoggerState<'d> { + /// Create a new instance of the logger state. + pub fn new() -> Self { + Self { + state: State::new(), + device_descriptor: [0; 32], + config_descriptor: [0; 128], + bos_descriptor: [0; 16], + control_buf: [0; 64], + } + } +} + +/// The logger handle, which contains a pipe with configurable size for buffering log messages. +pub struct UsbLogger { + buffer: Pipe, +} + +impl UsbLogger { + /// Create a new logger instance. + pub const fn new() -> Self { + Self { buffer: Pipe::new() } + } + + /// Run the USB logger using the state and USB driver. Never returns. + pub async fn run<'d, D>(&'d self, state: &'d mut LoggerState<'d>, driver: D) -> ! + where + D: Driver<'d>, + Self: 'd, + { + const MAX_PACKET_SIZE: u8 = 64; + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial logger"); + config.serial_number = None; + config.max_power = 100; + config.max_packet_size_0 = MAX_PACKET_SIZE; + + // Required for windows compatiblity. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + let mut builder = Builder::new( + driver, + config, + &mut state.device_descriptor, + &mut state.config_descriptor, + &mut state.bos_descriptor, + &mut state.control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state.state, MAX_PACKET_SIZE as u16); + + // Build the builder. + let mut device = builder.build(); + + loop { + let run_fut = device.run(); + let log_fut = async { + let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; + class.wait_connection().await; + loop { + let len = self.buffer.read(&mut rx[..]).await; + let _ = class.write_packet(&rx[..len]).await; + } + }; + join(run_fut, log_fut).await; + } + } +} + +impl log::Log for UsbLogger { + fn enabled(&self, _metadata: &Metadata) -> bool { + true + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let _ = write!(Writer(&self.buffer), "{}\r\n", record.args()); + } + } + + fn flush(&self) {} +} + +struct Writer<'d, const N: usize>(&'d Pipe); + +impl<'d, const N: usize> core::fmt::Write for Writer<'d, N> { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + let _ = self.0.try_write(s.as_bytes()); + Ok(()) + } +} + +/// Initialize and run the USB serial logger, never returns. +/// +/// Arguments specify the buffer size, log level and the USB driver, respectively. +/// +/// # Usage +/// +/// ``` +/// embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); +/// ``` +/// +/// # Safety +/// +/// This macro should only be invoked only once since it is setting the global logging state of the application. +#[macro_export] +macro_rules! run { + ( $x:expr, $l:expr, $p:ident ) => { + static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new(); + unsafe { + let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level($l)); + } + let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await; + }; +} diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 4d1fa995..b967aba0 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -299,7 +299,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> { /// **Note:** If `N` > the maximum packet size of the endpoint (i.e. output /// reports may be split across multiple packets) and this method's future /// is dropped after some packets have been read, the next call to `read()` - /// will return a [`ReadError::SyncError()`]. The range in the sync error + /// will return a [`ReadError::Sync`]. The range in the sync error /// indicates the portion `buf` that was filled by the current call to /// `read()`. If the dropped future used the same `buf`, then `buf` will /// contain the full report. diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index a5d82b60..1e7a5a84 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/rp/.cargo/config.toml b/examples/boot/application/rp/.cargo/config.toml new file mode 100644 index 00000000..edbd0a86 --- /dev/null +++ b/examples/boot/application/rp/.cargo/config.toml @@ -0,0 +1,12 @@ +[unstable] +build-std = ["core"] +build-std-features = ["panic_immediate_abort"] + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-run --chip RP2040" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml new file mode 100644 index 00000000..8d826790 --- /dev/null +++ b/examples/boot/application/rp/Cargo.toml @@ -0,0 +1,33 @@ +[package] +edition = "2021" +name = "embassy-boot-rp-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } +embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } +embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp" } +embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } + +defmt = "0.3" +defmt-rtt = "0.4" +panic-probe = { version = "0.3", features = ["print-defmt"], optional = true } +panic-reset = { version = "0.1.1", optional = true } +embedded-hal = { version = "0.2.6" } + +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" + +[features] +default = ["panic-reset"] +debug = [ + "embassy-rp/defmt", + "embassy-boot-rp/defmt", + "panic-probe" +] + +[profile.release] +debug = true diff --git a/examples/boot/application/rp/README.md b/examples/boot/application/rp/README.md new file mode 100644 index 00000000..41304c52 --- /dev/null +++ b/examples/boot/application/rp/README.md @@ -0,0 +1,28 @@ +# Examples using bootloader + +Example for Raspberry Pi Pico demonstrating the bootloader. The example consists of application binaries, 'a' +which waits for 5 seconds before flashing the 'b' binary, which blinks the LED. + +NOTE: The 'b' binary does not mark the new binary as active, so if you reset the device, it will roll back to the 'a' binary before automatically updating it again. + +## Prerequisites + +* `cargo-binutils` +* `cargo-flash` +* `embassy-boot-rp` + +## Usage + +``` +# Flash bootloader +cargo flash --manifest-path ../../bootloader/rp/Cargo.toml --release --chip RP2040 + +# Build 'b' +cargo build --release --bin b + +# Generate binary for 'b' +cargo objcopy --release --bin b -- -O binary b.bin + +# Flash `a` (which includes b.bin) +cargo flash --release --bin a --chip RP2040 +``` diff --git a/examples/boot/application/rp/build.rs b/examples/boot/application/rp/build.rs new file mode 100644 index 00000000..30691aa9 --- /dev/null +++ b/examples/boot/application/rp/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/boot/application/rp/memory.x b/examples/boot/application/rp/memory.x new file mode 100644 index 00000000..c1947311 --- /dev/null +++ b/examples/boot/application/rp/memory.x @@ -0,0 +1,15 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K + FLASH : ORIGIN = 0x10007000, LENGTH = 512K + DFU : ORIGIN = 0x10087000, LENGTH = 516K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2); +__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOT2); + +__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOT2); +__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2); diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs new file mode 100644 index 00000000..3736c914 --- /dev/null +++ b/examples/boot/application/rp/src/bin/a.rs @@ -0,0 +1,52 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt_rtt as _; +use embassy_boot_rp::*; +use embassy_executor::Spawner; +use embassy_rp::flash::Flash; +use embassy_rp::gpio::{Level, Output}; +use embassy_time::{Duration, Timer}; +#[cfg(feature = "panic-probe")] +use panic_probe as _; +#[cfg(feature = "panic-reset")] +use panic_reset as _; + +static APP_B: &[u8] = include_bytes!("../../b.bin"); +const FLASH_SIZE: usize = 2 * 1024 * 1024; + +#[embassy_executor::main] +async fn main(_s: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut led = Output::new(p.PIN_25, Level::Low); + + let mut flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); + + let mut updater = FirmwareUpdater::default(); + + Timer::after(Duration::from_secs(5)).await; + led.set_high(); + let mut offset = 0; + let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); + defmt::info!("preparing update"); + let mut writer = updater + .prepare_update_blocking(&mut flash) + .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) + .unwrap(); + defmt::info!("writer created, starting write"); + for chunk in APP_B.chunks(4096) { + buf.0[..chunk.len()].copy_from_slice(chunk); + defmt::info!("writing block at offset {}", offset); + writer + .write_block_blocking(offset, &buf.0[..], &mut flash, 256) + .unwrap(); + offset += chunk.len(); + } + defmt::info!("firmware written, marking update"); + updater.mark_updated_blocking(&mut flash, &mut buf.0[..1]).unwrap(); + Timer::after(Duration::from_secs(2)).await; + led.set_low(); + defmt::info!("update marked, resetting"); + cortex_m::peripheral::SCB::sys_reset(); +} diff --git a/examples/boot/application/rp/src/bin/b.rs b/examples/boot/application/rp/src/bin/b.rs new file mode 100644 index 00000000..47dec329 --- /dev/null +++ b/examples/boot/application/rp/src/bin/b.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_rp::gpio; +use embassy_time::{Duration, Timer}; +use gpio::{Level, Output}; +use {defmt_rtt as _, panic_reset as _}; + +#[embassy_executor::main] +async fn main(_s: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut led = Output::new(p.PIN_25, Level::Low); + + loop { + led.set_high(); + Timer::after(Duration::from_millis(100)).await; + + led.set_low(); + Timer::after(Duration::from_millis(100)).await; + } +} diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 3a184356..aa279fb7 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 8d9c4490..1ec0643a 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index b4314aa7..a4eefe2a 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index a17d336a..36eada29 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 683f2c86..67efda74 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index b879c0d7..4b2e02dd 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index e3bc0e49..fecbfc51 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -13,7 +13,7 @@ embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32 embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index b417a40d..8a6f5364 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } diff --git a/examples/boot/bootloader/rp/.cargo/config.toml b/examples/boot/bootloader/rp/.cargo/config.toml new file mode 100644 index 00000000..18bd4dfe --- /dev/null +++ b/examples/boot/bootloader/rp/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-run --chip RP2040" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml new file mode 100644 index 00000000..580ced22 --- /dev/null +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -0,0 +1,29 @@ +[package] +edition = "2021" +name = "rp-bootloader-example" +version = "0.1.0" +description = "Example bootloader for RP2040 chips" +license = "MIT OR Apache-2.0" + +[dependencies] +defmt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } + +embassy-rp = { path = "../../../../embassy-rp", default-features = false, features = ["nightly"] } +embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false } +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = { version = "0.7" } +embedded-storage = "0.3.0" +embedded-storage-async = "0.3.0" +cfg-if = "1.0.0" + +[features] +defmt = [ + "dep:defmt", + "embassy-boot-rp/defmt", + "embassy-rp/defmt", +] +debug = ["defmt-rtt", "defmt"] + +[profile.release] +debug = true diff --git a/examples/boot/bootloader/rp/README.md b/examples/boot/bootloader/rp/README.md new file mode 100644 index 00000000..064e8727 --- /dev/null +++ b/examples/boot/bootloader/rp/README.md @@ -0,0 +1,17 @@ +# Bootloader for RP2040 + +The bootloader uses `embassy-boot` to interact with the flash. + +# Usage + +Flashing the bootloader + +``` +cargo flash --release --chip RP2040 +``` + +To debug, use `cargo run` and enable the debug feature flag + +``` rust +cargo run --release --features debug +``` diff --git a/examples/boot/bootloader/rp/build.rs b/examples/boot/bootloader/rp/build.rs new file mode 100644 index 00000000..c201704a --- /dev/null +++ b/examples/boot/bootloader/rp/build.rs @@ -0,0 +1,28 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tlink-rp.x"); + if env::var("CARGO_FEATURE_DEFMT").is_ok() { + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + } +} diff --git a/examples/boot/bootloader/rp/memory.x b/examples/boot/bootloader/rp/memory.x new file mode 100644 index 00000000..d6ef3846 --- /dev/null +++ b/examples/boot/bootloader/rp/memory.x @@ -0,0 +1,19 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 24K + BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K + ACTIVE : ORIGIN = 0x10007000, LENGTH = 512K + DFU : ORIGIN = 0x10087000, LENGTH = 516K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2); +__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOT2); + +__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(BOOT2); +__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(BOOT2); + +__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOT2); +__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2); diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs new file mode 100644 index 00000000..5028ec68 --- /dev/null +++ b/examples/boot/bootloader/rp/src/main.rs @@ -0,0 +1,51 @@ +#![no_std] +#![no_main] + +use cortex_m_rt::{entry, exception}; +#[cfg(feature = "defmt")] +use defmt_rtt as _; +use embassy_boot_rp::*; +use embassy_rp::flash::{Flash, ERASE_SIZE}; +use embassy_rp::peripherals::FLASH; + +const FLASH_SIZE: usize = 2 * 1024 * 1024; + +#[entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + + // Uncomment this if you are debugging the bootloader with debugger/RTT attached, + // as it prevents a hard fault when accessing flash 'too early' after boot. + /* + for i in 0..10000000 { + cortex_m::asm::nop(); + } + */ + + let mut bl: BootLoader = BootLoader::default(); + let flash: Flash<'_, FLASH, FLASH_SIZE> = Flash::new(p.FLASH); + let mut flash = BootFlash::<_, ERASE_SIZE>::new(flash); + let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); + core::mem::drop(flash); + + unsafe { bl.load(start) } +} + +#[no_mangle] +#[cfg_attr(target_os = "none", link_section = ".HardFault.user")] +unsafe extern "C" fn HardFault() { + cortex_m::peripheral::SCB::sys_reset(); +} + +#[exception] +unsafe fn DefaultHandler(_: i16) -> ! { + const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; + let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; + + panic!("DefaultHandler #{:?}", irqn); +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + cortex_m::asm::udf(); +} diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 4ddd1c99..be659e02 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.3", optional = true } +defmt-rtt = { version = "0.4", optional = true } embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index c633f82f..8b95ac3a 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -17,14 +17,14 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } -embedded-io = "0.3.1" +embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" static_cell = "1.0" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } @@ -34,4 +34,4 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa 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 } +serde = { version = "1.0.136", default-features = false } \ No newline at end of file diff --git a/examples/nrf/src/bin/i2s_effect.rs b/examples/nrf/src/bin/i2s_effect.rs new file mode 100644 index 00000000..3cca005b --- /dev/null +++ b/examples/nrf/src/bin/i2s_effect.rs @@ -0,0 +1,117 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::f32::consts::PI; + +use defmt::{error, info}; +use embassy_executor::Spawner; +use embassy_nrf::i2s::{self, Channels, Config, MasterClock, MultiBuffering, Sample as _, SampleWidth, I2S}; +use embassy_nrf::interrupt; +use {defmt_rtt as _, panic_probe as _}; + +type Sample = i16; + +const NUM_BUFFERS: usize = 2; +const NUM_SAMPLES: usize = 4; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + + let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); + + let sample_rate = master_clock.sample_rate(); + info!("Sample rate: {}", sample_rate); + + let config = Config::default() + .sample_width(SampleWidth::_16bit) + .channels(Channels::MonoLeft); + + let irq = interrupt::take!(I2S); + let buffers_out = MultiBuffering::::new(); + let buffers_in = MultiBuffering::::new(); + let mut full_duplex_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).full_duplex( + p.P0_29, + p.P0_28, + buffers_out, + buffers_in, + ); + + let mut modulator = SineOsc::new(); + modulator.set_frequency(8.0, 1.0 / sample_rate as f32); + modulator.set_amplitude(1.0); + + full_duplex_stream.start().await.expect("I2S Start"); + + loop { + let (buff_out, buff_in) = full_duplex_stream.buffers(); + for i in 0..NUM_SAMPLES { + let modulation = (Sample::SCALE as f32 * bipolar_to_unipolar(modulator.generate())) as Sample; + buff_out[i] = buff_in[i] * modulation; + } + + if let Err(err) = full_duplex_stream.send_and_receive().await { + error!("{}", err); + } + } +} + +struct SineOsc { + amplitude: f32, + modulo: f32, + phase_inc: f32, +} + +impl SineOsc { + const B: f32 = 4.0 / PI; + const C: f32 = -4.0 / (PI * PI); + const P: f32 = 0.225; + + pub fn new() -> Self { + Self { + amplitude: 1.0, + modulo: 0.0, + phase_inc: 0.0, + } + } + + pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { + self.phase_inc = freq * inv_sample_rate; + } + + pub fn set_amplitude(&mut self, amplitude: f32) { + self.amplitude = amplitude; + } + + pub fn generate(&mut self) -> f32 { + let signal = self.parabolic_sin(self.modulo); + self.modulo += self.phase_inc; + if self.modulo < 0.0 { + self.modulo += 1.0; + } else if self.modulo > 1.0 { + self.modulo -= 1.0; + } + signal * self.amplitude + } + + fn parabolic_sin(&mut self, modulo: f32) -> f32 { + let angle = PI - modulo * 2.0 * PI; + let y = Self::B * angle + Self::C * angle * abs(angle); + Self::P * (y * abs(y) - y) + y + } +} + +#[inline] +fn abs(value: f32) -> f32 { + if value < 0.0 { + -value + } else { + value + } +} + +#[inline] +fn bipolar_to_unipolar(value: f32) -> f32 { + (value + 1.0) / 2.0 +} diff --git a/examples/nrf/src/bin/i2s_monitor.rs b/examples/nrf/src/bin/i2s_monitor.rs new file mode 100644 index 00000000..48eb7d58 --- /dev/null +++ b/examples/nrf/src/bin/i2s_monitor.rs @@ -0,0 +1,115 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{debug, error, info}; +use embassy_executor::Spawner; +use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; +use embassy_nrf::interrupt; +use embassy_nrf::pwm::{Prescaler, SimplePwm}; +use {defmt_rtt as _, panic_probe as _}; + +type Sample = i16; + +const NUM_SAMPLES: usize = 500; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + + let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); + + let sample_rate = master_clock.sample_rate(); + info!("Sample rate: {}", sample_rate); + + let config = Config::default() + .sample_width(SampleWidth::_16bit) + .channels(Channels::MonoLeft); + + let irq = interrupt::take!(I2S); + let buffers = DoubleBuffering::::new(); + let mut input_stream = + I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); + + // Configure the PWM to use the pins corresponding to the RGB leds + let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24); + pwm.set_prescaler(Prescaler::Div1); + pwm.set_max_duty(255); + + let mut rms_online = RmsOnline::::default(); + + input_stream.start().await.expect("I2S Start"); + + loop { + let rms = rms_online.process(input_stream.buffer()); + let rgb = rgb_from_rms(rms); + + debug!("RMS: {}, RGB: {:?}", rms, rgb); + for i in 0..3 { + pwm.set_duty(i, rgb[i].into()); + } + + if let Err(err) = input_stream.receive().await { + error!("{}", err); + } + } +} + +/// RMS from 0.0 until 0.75 will give green with a proportional intensity +/// RMS from 0.75 until 0.9 will give a blend between orange and red proportionally to the intensity +/// RMS above 0.9 will give a red with a proportional intensity +fn rgb_from_rms(rms: f32) -> [u8; 3] { + if rms < 0.75 { + let intensity = rms / 0.75; + [0, (intensity * 165.0) as u8, 0] + } else if rms < 0.9 { + let intensity = (rms - 0.75) / 0.15; + [200, 165 - (165.0 * intensity) as u8, 0] + } else { + let intensity = (rms - 0.9) / 0.1; + [200 + (55.0 * intensity) as u8, 0, 0] + } +} + +pub struct RmsOnline { + pub squares: [f32; N], + pub head: usize, +} + +impl Default for RmsOnline { + fn default() -> Self { + RmsOnline { + squares: [0.0; N], + head: 0, + } + } +} + +impl RmsOnline { + pub fn reset(&mut self) { + self.squares = [0.0; N]; + self.head = 0; + } + + pub fn process(&mut self, buf: &[Sample]) -> f32 { + buf.iter() + .for_each(|sample| self.push(*sample as f32 / Sample::SCALE as f32)); + + let sum_of_squares = self.squares.iter().fold(0.0, |acc, v| acc + *v); + Self::approx_sqrt(sum_of_squares / N as f32) + } + + pub fn push(&mut self, signal: f32) { + let square = signal * signal; + self.squares[self.head] = square; + self.head = (self.head + 1) % N; + } + + /// Approximated sqrt taken from [micromath] + /// + /// [micromath]: https://docs.rs/micromath/latest/src/micromath/float/sqrt.rs.html#11-17 + /// + fn approx_sqrt(value: f32) -> f32 { + f32::from_bits((value.to_bits() + 0x3f80_0000) >> 1) + } +} diff --git a/examples/nrf/src/bin/i2s_waveform.rs b/examples/nrf/src/bin/i2s_waveform.rs new file mode 100644 index 00000000..1b0e8ebc --- /dev/null +++ b/examples/nrf/src/bin/i2s_waveform.rs @@ -0,0 +1,151 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::f32::consts::PI; + +use defmt::{error, info}; +use embassy_executor::Spawner; +use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; +use embassy_nrf::interrupt; +use {defmt_rtt as _, panic_probe as _}; + +type Sample = i16; + +const NUM_SAMPLES: usize = 50; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + + let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); + + let sample_rate = master_clock.sample_rate(); + info!("Sample rate: {}", sample_rate); + + let config = Config::default() + .sample_width(SampleWidth::_16bit) + .channels(Channels::MonoLeft); + + let irq = interrupt::take!(I2S); + let buffers = DoubleBuffering::::new(); + let mut output_stream = + I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); + + let mut waveform = Waveform::new(1.0 / sample_rate as f32); + + waveform.process(output_stream.buffer()); + + output_stream.start().await.expect("I2S Start"); + + loop { + waveform.process(output_stream.buffer()); + + if let Err(err) = output_stream.send().await { + error!("{}", err); + } + } +} + +struct Waveform { + inv_sample_rate: f32, + carrier: SineOsc, + freq_mod: SineOsc, + amp_mod: SineOsc, +} + +impl Waveform { + fn new(inv_sample_rate: f32) -> Self { + let mut carrier = SineOsc::new(); + carrier.set_frequency(110.0, inv_sample_rate); + + let mut freq_mod = SineOsc::new(); + freq_mod.set_frequency(1.0, inv_sample_rate); + freq_mod.set_amplitude(1.0); + + let mut amp_mod = SineOsc::new(); + amp_mod.set_frequency(16.0, inv_sample_rate); + amp_mod.set_amplitude(0.5); + + Self { + inv_sample_rate, + carrier, + freq_mod, + amp_mod, + } + } + + fn process(&mut self, buf: &mut [Sample]) { + for sample in buf.chunks_mut(1) { + let freq_modulation = bipolar_to_unipolar(self.freq_mod.generate()); + self.carrier + .set_frequency(110.0 + 440.0 * freq_modulation, self.inv_sample_rate); + + let amp_modulation = bipolar_to_unipolar(self.amp_mod.generate()); + self.carrier.set_amplitude(amp_modulation); + + let signal = self.carrier.generate(); + + sample[0] = (Sample::SCALE as f32 * signal) as Sample; + } + } +} + +struct SineOsc { + amplitude: f32, + modulo: f32, + phase_inc: f32, +} + +impl SineOsc { + const B: f32 = 4.0 / PI; + const C: f32 = -4.0 / (PI * PI); + const P: f32 = 0.225; + + pub fn new() -> Self { + Self { + amplitude: 1.0, + modulo: 0.0, + phase_inc: 0.0, + } + } + + pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { + self.phase_inc = freq * inv_sample_rate; + } + + pub fn set_amplitude(&mut self, amplitude: f32) { + self.amplitude = amplitude; + } + + pub fn generate(&mut self) -> f32 { + let signal = self.parabolic_sin(self.modulo); + self.modulo += self.phase_inc; + if self.modulo < 0.0 { + self.modulo += 1.0; + } else if self.modulo > 1.0 { + self.modulo -= 1.0; + } + signal * self.amplitude + } + + fn parabolic_sin(&mut self, modulo: f32) -> f32 { + let angle = PI - modulo * 2.0 * PI; + let y = Self::B * angle + Self::C * angle * abs(angle); + Self::P * (y * abs(y) - y) + y + } +} + +#[inline] +fn abs(value: f32) -> f32 { + if value < 0.0 { + -value + } else { + value + } +} + +#[inline] +fn bipolar_to_unipolar(value: f32) -> f32 { + (value + 1.0) / 2.0 +} diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf/src/bin/spis.rs new file mode 100644 index 00000000..fe3b0c53 --- /dev/null +++ b/examples/nrf/src/bin/spis.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::spis::{Config, Spis}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("Running!"); + + let irq = interrupt::take!(SPIM2_SPIS2_SPI2); + let mut spis = Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); + + loop { + let mut rx_buf = [0_u8; 64]; + let tx_buf = [1_u8, 2, 3, 4, 5, 6, 7, 8]; + if let Ok((n_rx, n_tx)) = spis.transfer(&mut rx_buf, &tx_buf).await { + info!("RX: {:?}", rx_buf[..n_rx]); + info!("TX: {:?}", tx_buf[..n_tx]); + } + } +} diff --git a/examples/nrf/src/bin/twis.rs b/examples/nrf/src/bin/twis.rs new file mode 100644 index 00000000..54cba949 --- /dev/null +++ b/examples/nrf/src/bin/twis.rs @@ -0,0 +1,46 @@ +//! TWIS example + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::twis::{self, Command, Twis}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + + let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); + let mut config = twis::Config::default(); + // Set i2c address + config.address0 = 0x55; + let mut i2c = Twis::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); + + info!("Listening..."); + loop { + let response = [1, 2, 3, 4, 5, 6, 7, 8]; + // This buffer is used if the i2c master performs a Write or WriteRead + let mut buf = [0u8; 16]; + match i2c.listen(&mut buf).await { + Ok(Command::Read) => { + info!("Got READ command. Respond with data:\n{:?}\n", response); + if let Err(e) = i2c.respond_to_read(&response).await { + error!("{:?}", e); + } + } + Ok(Command::Write(n)) => info!("Got WRITE command with data:\n{:?}\n", buf[..n]), + Ok(Command::WriteRead(n)) => { + info!("Got WRITE/READ command with data:\n{:?}", buf[..n]); + info!("Respond with data:\n{:?}\n", response); + if let Err(e) = i2c.respond_to_read(&response).await { + error!("{:?}", e); + } + } + Err(e) => error!("{:?}", e), + } + } +} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 31f68830..60a8ba94 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -13,9 +13,10 @@ embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt" embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" @@ -28,10 +29,11 @@ display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "0.1.0-alpha.3" } -embedded-io = { version = "0.3.1", features = ["async", "defmt"] } +embedded-hal-async = "0.2.0-alpha.0" +embedded-io = { version = "0.4.0", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } static_cell = "1.0.0" +log = "0.4" [profile.release] debug = true diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs new file mode 100644 index 00000000..2a9e9373 --- /dev/null +++ b/examples/rp/src/bin/adc.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::adc::{Adc, Config}; +use embassy_rp::interrupt; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let irq = interrupt::take!(ADC_IRQ_FIFO); + let mut adc = Adc::new(p.ADC, irq, Config::default()); + + let mut p26 = p.PIN_26; + let mut p27 = p.PIN_27; + let mut p28 = p.PIN_28; + + loop { + let level = adc.read(&mut p26).await; + info!("Pin 26 ADC: {}", level); + let level = adc.read(&mut p27).await; + info!("Pin 27 ADC: {}", level); + let level = adc.read(&mut p28).await; + info!("Pin 28 ADC: {}", level); + let temp = adc.read_temperature().await; + info!("Temp: {}", temp); + Timer::after(Duration::from_secs(1)).await; + } +} diff --git a/examples/rp/src/bin/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs new file mode 100644 index 00000000..52417a02 --- /dev/null +++ b/examples/rp/src/bin/usb_logger.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_rp::interrupt; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::Driver; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn logger_task(driver: Driver<'static, USB>) { + embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let irq = interrupt::take!(USBCTRL_IRQ); + let driver = Driver::new(p.USB, irq); + spawner.spawn(logger_task(driver)).unwrap(); + + let mut counter = 0; + loop { + counter += 1; + log::info!("Tick {}", counter); + Timer::after(Duration::from_secs(1)).await; + } +} diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 79025838..41680f8f 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["lo embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4", "pool-16"] } -embedded-io = { version = "0.3.1", features = ["async", "std", "futures"] } +embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } async-io = "1.6.0" diff --git a/examples/stm32f0/.cargo/config.toml b/examples/stm32f0/.cargo/config.toml index d1b1cd0b..16abc29b 100644 --- a/examples/stm32f0/.cargo/config.toml +++ b/examples/stm32f0/.cargo/config.toml @@ -1,5 +1,5 @@ [target.thumbv6m-none-eabi] -runner = 'probe-run --chip STM32F030F4Px' +runner = 'probe-run --chip STM32F091RCTX' [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index a56c546e..c10af171 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -10,10 +10,10 @@ license = "MIT OR Apache-2.0" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" panic-probe = "0.3" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f030f4", "time-driver-any"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any"] } diff --git a/examples/stm32f0/src/bin/blinky.rs b/examples/stm32f0/src/bin/blinky.rs new file mode 100644 index 00000000..9f923399 --- /dev/null +++ b/examples/stm32f0/src/bin/blinky.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +// main is itself an async function. +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + //PA5 is the onboard LED on the Nucleo F091RC + let mut led = Output::new(p.PA5, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + + info!("low"); + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +} diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 6be131f3..53f369b3 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -13,7 +13,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index f6adda2a..afaf9a0c 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 27188dd1..69ebef78 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -13,7 +13,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index b05457ea..62d3f08d 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -12,12 +12,12 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-io = "0.3.1" +embedded-io = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index b14afd2f..f4d674cd 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -10,10 +10,10 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } -embedded-io = { version = "0.3.1", features = ["async"] } +embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index f5673718..e7273c9f 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index ecda2880..8a57a8ef 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -12,7 +12,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 0dccff6e..11ce3505 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -10,17 +10,17 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } -embedded-io = { version = "0.3.1", features = ["async"] } +embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3" } -embedded-nal-async = "0.2.0" +embedded-hal-async = { version = "=0.2.0-alpha.0" } +embedded-nal-async = "0.3.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32h7/src/bin/wdg.rs b/examples/stm32h7/src/bin/wdg.rs new file mode 100644 index 00000000..2b0301aa --- /dev/null +++ b/examples/stm32h7/src/bin/wdg.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::wdg::IndependentWatchdog; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut wdg = IndependentWatchdog::new(p.IWDG1, 20_000_000); + + unsafe { wdg.unleash() }; + + loop { + Timer::after(Duration::from_secs(1)).await; + unsafe { wdg.pet() }; + } +} diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 8b00773b..86933a62 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -19,10 +19,10 @@ lorawan-device = { version = "0.8.0", default-features = false, features = ["asy lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" embedded-storage = "0.3.0" -embedded-io = "0.3.1" +embedded-io = "0.4.0" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 9460febf..6e3b2103 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 83d456b2..45d3dd36 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -14,13 +14,13 @@ embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3" } +embedded-hal-async = { version = "=0.2.0-alpha.0" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 848723f8..73ad5078 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -17,7 +17,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0" defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] } cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } @@ -26,5 +26,5 @@ embedded-hal = "0.2.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } rand_core = { version = "0.6.3", default-features = false } -embedded-io = { version = "0.3.1", features = ["async"] } +embedded-io = { version = "0.4.0", features = ["async"] } static_cell = "1.0" diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 3d704011..d88fdda5 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" @@ -21,8 +21,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" - -#[patch.crates-io] -#defmt = { git="https://github.com/knurling-rs/defmt.git" } -#defmt-rtt = { git="https://github.com/knurling-rs/defmt.git" } - diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 5b96fa19..e27b4527 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55cc", "time-driver-any", "exti"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index c827d2b7..690481bb 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -15,7 +15,7 @@ lorawan-device = { version = "0.8.0", default-features = false, features = ["asy lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } defmt = "0.3" -defmt-rtt = "0.3" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c05aac3f..55539405 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,7 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2022-10-25" +channel = "nightly-2022-11-22" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 069b7fb8..a07b479e 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -12,16 +12,16 @@ embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightl embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" -defmt-rtt = "0.3.0" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3" } +embedded-hal-async = { version = "=0.2.0-alpha.0" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } -embedded-io = { version = "0.3.1", features = ["async"] } +embedded-io = { version = "0.4.0", features = ["async"] } embedded-storage = { version = "0.3" } [profile.dev] diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs index af22fe27..80e92d0f 100644 --- a/tests/rp/src/bin/gpio.rs +++ b/tests/rp/src/bin/gpio.rs @@ -78,6 +78,7 @@ async fn main(_spawner: Spawner) { a.set_as_input(); // When an OutputOpenDrain is high, it doesn't drive the pin. + b.set_high(); a.set_pull(Pull::Up); delay(); assert!(a.is_high()); @@ -85,9 +86,8 @@ async fn main(_spawner: Spawner) { delay(); assert!(a.is_low()); - b.set_low(); - // When an OutputOpenDrain is low, it drives the pin low. + b.set_low(); a.set_pull(Pull::Up); delay(); assert!(a.is_low()); @@ -95,14 +95,36 @@ async fn main(_spawner: Spawner) { delay(); assert!(a.is_low()); + // Check high again b.set_high(); - a.set_pull(Pull::Up); delay(); assert!(a.is_high()); a.set_pull(Pull::Down); delay(); assert!(a.is_low()); + + // When an OutputOpenDrain is high, it reads the input value in the pin. + b.set_high(); + a.set_as_input(); + a.set_pull(Pull::Up); + delay(); + assert!(b.is_high()); + a.set_as_output(); + a.set_low(); + delay(); + assert!(b.is_low()); + + // When an OutputOpenDrain is low, it always reads low. + b.set_low(); + a.set_as_input(); + a.set_pull(Pull::Up); + delay(); + assert!(b.is_low()); + a.set_as_output(); + a.set_low(); + delay(); + assert!(b.is_low()); } // FLEX diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index 9cc20bb9..bea9283e 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -5,7 +5,7 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; use embassy_rp::interrupt; -use embassy_rp::uart::{BufferedUart, Config, State, Uart}; +use embassy_rp::uart::{BufferedUart, Config}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; @@ -17,25 +17,22 @@ async fn main(_spawner: Spawner) { let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); let config = Config::default(); - let uart = Uart::new_blocking(uart, tx, rx, config); - let irq = interrupt::take!(UART0_IRQ); let tx_buf = &mut [0u8; 16]; let rx_buf = &mut [0u8; 16]; - let mut state = State::new(); - let mut uart = BufferedUart::new(&mut state, uart, irq, tx_buf, rx_buf); + let mut uart = BufferedUart::new(uart, irq, tx, rx, tx_buf, rx_buf, config); // Make sure we send more bytes than fits in the FIFO, to test the actual // bufferedUart. let data = [ - 1_u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, + 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, ]; uart.write_all(&data).await.unwrap(); info!("Done writing"); - let mut buf = [0; 32]; + let mut buf = [0; 31]; uart.read_exact(&mut buf).await.unwrap(); assert_eq!(buf, data); diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 602c1fb5..08a775ea 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -20,13 +20,13 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] } defmt = "0.3.0" -defmt-rtt = "0.3.0" +defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = { version = "=0.1.0-alpha.3" } +embedded-hal-async = { version = "=0.2.0-alpha.0" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } [profile.dev]